Inspecting a Repository Before You Clone It
- security
- repository-review
- sandbox
- cloudflare
- durable-objects

Before you clone a repository, you want to know what is in it. Git tells you who changed which line. It does not tell you that a build script reads your environment, or that a dependency carries a published advisory.
The repository inspection tool takes a GitHub URL and returns a signed report. The report covers the source tree, the dependency manifests, and the files that run at install time. This post describes how a review runs, and what the result does and does not establish.
Two components
The Operation Ledger is a Cloudflare Durable Object. It owns policy, state, and the audit trail. It does not run repository code. It validates the request, records the approval, issues a lease, and stores the signed result.
The executor is a separate daemon on a different machine. It claims a lease, runs a fixed container image, and posts the result back. Leases are time-boxed. A lease that expires without a completion frees the operation for a new claim.
Splitting the two keeps the code that handles untrusted input away from the service that holds the audit record.
The lifecycle
A review needs two approvals before anything runs: one for the request, then one to queue it. The executor picks the job up from a signed wake-up delivered over a Cloudflare tunnel.
| Step | Trigger | State after |
|---|---|---|
| Submit | POST /request | requested |
| Approve | POST /approve | approved |
| Queue | POST /queue | queued |
| Claim | POST /claim | running |
| Complete | POST /complete | succeeded or failed |
A 202 from the queue endpoint reports one thing: the executor accepted the notification. The review is finished once the ledger holds terminal state and signed completion evidence.
Two phases, two network policies
The review runs in two phases, and they have different network rules.
Fetch. review-fetch.sh runs a dedicated image that acquires a shallow copy of the repository. That image has network access, and submodules are disabled. The fetch phase does not run repository code.
Scan. review-repo.sh runs a second image against the fetched source with --network none. The source is mounted read-only. There are no credentials, no Docker socket, and no host project mount.
One scanner is the exception to the network rule. OSV-Scanner has to reach the advisory database, so it runs with a bridge network. It is the only scanner container with network access, and its source mount is read-only like the rest.
The scanners
| Scanner | What it covers | Network |
|---|---|---|
| heckler | Characters that hide or reorder text: bidirectional controls, invisible characters, homoglyphs, zero-width characters, tag characters, variation selectors | none |
| OSV-Scanner 2.5.0 | Published advisories against the dependency manifests | bridge |
| skillspector | Agent skills and plugins, and only when the repository contains a manifest for one | none |
| syft 1.48.0 | A software bill of materials in CycloneDX JSON | none |
skillspector writes a not-applicable result and exits when no manifest is present. A scanner that did not run is recorded as not applicable rather than as a pass.
What each container is allowed to do
Every scanner container is created for one run and destroyed after it. The flags are the same across the four:
- Root filesystem mounted read-only, with
--tmpfs /tmpsetnoexecandnosuid - All Linux capabilities dropped
no-new-privilegesset- Runs as uid 10001, not root
- Source mounted read-only
- Memory capped between 1 GB and 3 GB
- One to two CPUs, and a process limit of 128
- A wall-clock timeout of 600 to 900 seconds
The executor never installs dependencies, runs lifecycle hooks, builds the project, executes binaries, imports plugins, or clones into the agent's project tree. Those are separate operations with separate approvals.
Findings and classification
Each finding records a rule, a severity, a file, a line range, and a short excerpt of the matched text. The tool then classifies the finding by where it landed, because the same character means different things in a README and in a shell script.
- false-positive: the match is in a binary asset with no established executable consumer or parser path.
- contextual: the match is in documentation or a test fixture rather than production code.
- inconclusive: the match is in runtime code, or in a file type the classifier does not recognize. A hidden character in a Python file is a question for a human.
The verdict
The executor sets one of three verdicts:
do-not-clonewhen a critical runtime finding is presentneeds-manual-reviewwhen the review found issues but no critical runtime findingsafe-to-clonewhen it found nothing
A static verdict is not proof that malware is absent. The review reads source. Code it cannot reach stays out of the report, and the report says so by recording the sandbox boundary alongside the verdict.
The report
The executor writes one signed artifact, review-result-v1.json. It holds the reviewed commit, a digest of the source tree, the findings, the verdict, the scanner versions, and the sandbox evidence. The Worker serves the same data at a signed URL under inspect.bobatron.com/report/<token>, where the token addresses exactly one operation.
One recent run reviewed hermes-token-logger at commit 6d7d1b0. It recorded 7 findings across 4 scanners, with the sandbox boundary marked enforced. The complete report for that run is live.
Cloning is a separate step
A clone requires its own explicit approval, given after the review and after the reviewed commit is verified as immutable. It runs with Git hooks disabled:
git -c core.hooksPath=/dev/null clone --depth 1 --no-tags --no-recurse-submodules \
https://github.com/<owner>/<repo>.git <destination>
Any install or build after that is its own operation with its own approval, and it runs in the same disposable executor rather than on the host.