CARL is a code-scanning tool. Scanning other people's code carries a particular trust burden. This guide explains the architecture that lets the hosted CARL assessor clone a private repository, evaluate it, and produce a report, without persisting the source, logging the contents, or holding the OAuth token any longer than strictly necessary.
If you are considering running the hosted assessor against a private repository — your own or your team's — read this page before clicking Assess.
Three trust tiers
CARL supports three ways to assess a private repository, from most convenient to most restrictive:
- Hosted, GitHub OAuth. You authenticate to
carl.wentzel.ai/appwith GitHub, the hosted assessor fetches your repo into an ephemeral Worker isolate (in memory), and you get a report. Source code never persists. This is the default. - Self-hosted Docker image. You run CARL inside your own network. Source never leaves your infrastructure. See Self-hosting CARL.
- CLI on a build machine. You run the CARL CLI (
@wentzel/carl-cli, npm publish pending) from a CI runner or laptop that already has cloned access to the code. No network round-trip to Wentzel.ai at all.
The remainder of this guide describes tier 1 — the ephemeral-isolate
architecture on carl.wentzel.ai/app.
The data flow
[ User's browser ]
|
| (1) GitHub OAuth — short-lived access token, minimal scope
v
[ carl.wentzel.ai/app — Cloudflare Worker (OpenNext) ]
|
| (2) POST /api/assess with the repo reference + token
v
[ The Worker isolate handles the request ]
|
| (3) Fetch the repo as a GitHub codeload tarball straight into
| Worker memory — no disk; unpacked in-RAM via DecompressionStream
| (4) Run the deterministic engine across the in-memory file map
| (5) (Optional, opt-in) AI-assisted phase against the AI API
| (6) Write the SANITIZED report to Cloudflare D1
| (7) Request returns — the isolate is discarded; the repo bytes vanish
v
[ Cloudflare D1 — sanitized report only ]
|
v
[ User sees the report on a print-friendly page ]
The work is bounded by the Worker's CPU + wall-clock limits. Outbound network is restricted to the source host (GitHub) and — if the user opted in — the AI API. There is no persistent disk: the repository is held only in the Worker's memory for the duration of the request, and is gone the moment the request returns.
OAuth contract
When you sign in, CARL requests the minimum possible GitHub scopes:
read:user— to identify your account for the audit log.repo:read(or the finer-grained equivalent when GitHub's rollout permits) — to clone private repositories you have access to.
The access token returned by GitHub is used once, for the clone. It is:
- Decrypted only in worker memory.
- Never written to disk.
- Never logged in stdout, stderr, Sentry breadcrumbs, or audit-log metadata.
- Discarded when the worker terminates.
A refresh token (encrypted at rest) may be stored on your CARL session so you can run subsequent assessments without re-authenticating, but it is never available to the worker except at the moment of use.
What persists in the database
CARL's Cloudflare D1 (SQLite) schema persists these fields per assessment:
- Repository identifier (
owner/repo) — never the repository contents. - Commit hash assessed.
- Timestamp of the assessment and duration.
- Pillar scores and the CARL level achieved.
- Per-criterion results with a sanitized evidence summary (e.g., "Found
AGENTS.mdat repo root with 7 headings") — not code, not function bodies, not secrets. - Remediation text generated from the findings.
- User account (if authenticated; public assessments are anonymous).
Specifically excluded: raw file contents, function or class bodies, identifying code snippets, secrets the assessor may have observed in passing, OAuth tokens.
What never persists
- Source code from the assessed repository.
- File contents of any kind.
- Raw OAuth access tokens.
- Secrets the assessor may have read during evaluation.
- Any byproduct of the clone beyond the sanitized report.
The Worker isolate that handled the request is torn down when it returns; nothing is pooled or reused, and no repository bytes ever touch disk.
The audit log
Every repository clone, every assessment, every report write is logged to an immutable audit table with:
- User (if authenticated).
- Repository identifier.
- Timestamp.
- Worker invocation ID (the Cloudflare request id of the ephemeral isolate).
- Checksum of the resulting sanitized report.
The audit log is append-only at the application level — no application code
path issues UPDATE or DELETE against the audit table. If you ever need to
prove that your code was handled correctly, the audit log is the ground truth.
What you can verify for yourself
- The engine is deterministic and reproducible. The engine and the CLI are the same reference implementations Wentzel.ai runs internally (proprietary, all rights reserved — see License); their behavior is documented in the criteria catalog, not a black box.
- The engine runs in a stateless Worker isolate. The deterministic engine
that touches your repo is the same
@wentzel/carl-enginebuild described above — the CLI against the same commit reproduces the result with no third-party round-trip. - The sanitization guarantee is schema-enforced. The JSON schemas live next
to the prompts at
prompts/schemas/. Test the sanitization yourself with a fixture that tries to return raw code — it will be rejected.
When the hosted assessor is not the right choice
- Regulated data or classified systems. Even with the ephemeral-isolate contract, some teams cannot let their code touch any third-party infrastructure. Use Self-hosting CARL.
- Air-gapped networks. Use the CLI on a build machine with no internet access.
- Forks of commercial products you don't own. If your internal fork of a commercial library has license restrictions on who can hold the source, respect them; self-host instead.
Security disclosure
If you discover an issue with the architecture described here, or with the
running service, please email security@wentzel.ai. See SECURITY.md in the
monorepo for details.
Related
- Self-hosting CARL — the fully air-gapped option.
- Governance — how Wentzel.ai governs the project and handles disclosures.
- Reading your report — interpreting the sanitized output after your assessment.