Source-tree exception ledger for Rust repositories.
No invisible source exceptions.
cargo-allow is a source-tree exception ledger for Rust repositories. It scans
repository files without executing project code, then checks syntax-visible
exceptions against policy/allow.toml.
It helps teams answer:
- What source exceptions exist?
- Why are they allowed?
- Who owns them?
- What evidence supports them?
- When do they expire or need review?
- Did this PR add, remove, broaden, weaken, or improve anything?
- What should a human or agent fix next?
The first useful run should feel small:
one repo
-> one visible exception surface
-> one no-new policy
-> one CI receipt
-> one worklist item to close
That is the adoption spine: make retained exceptions visible, keep new debt out, record CI evidence, and give humans or agents a bounded repair queue.
Most repositories accumulate exceptions:
unsafeunwrap/expect/panic!- indexing and slicing
#[allow]/#[expect]- generated code
- scripts, workflows, docs, config, and other non-Rust tracked files
The hard part is not finding one exception. The hard part is keeping retained exceptions owned, scoped, evidenced, reviewable, and difficult to silently broaden.
cargo-allow is the ledger layer.
cargo-allow scans source-tree inventory and compares findings to policy
receipts. The durable policy file is policy/allow.toml.
Core workflows:
cargo-allow doctor
cargo-allow audit
cargo-allow check --mode no-new
cargo-allow diff --base origin/main
cargo-allow list
cargo-allow explain allow-0042
cargo-allow worklist --format jsoncargo-allow scans repository files directly. It may be installed as a Cargo
external subcommand, but the primary UX is the standalone cargo-allow binary.
cargo allow ... remains compatibility syntax for users who invoke it through
Cargo.
cargo-allow does not compile the project or execute repository code.
It does not require a successful build and does not invoke
Cargo metadata, Cargo commands, rustc, Clippy, build scripts, proc macros,
cargo-deny, cargo-vet, ripr, unsafe-review, or coverage tooling.
It does not require network access or GitHub APIs for its own scan.
Cargo.toml and Cargo.lock are files in the scanned source tree, not required
build metadata.
It does not require:
- Cargo metadata
cargo checkorcargo test- rustc
- Clippy
- build scripts
- proc macro expansion
- dependency resolution
- type analysis
- MIR
- control-flow or data-flow analysis
- proof that unsafe code is correct
- proof that tests are adequate
- coverage proof
- network access
- GitHub API access
Other tools can provide evidence. cargo-allow owns the durable
source-exception ledger.
Current reports may claim:
No new unreceipted findings were found in scanned source-tree inventory.
They must not claim that no unsafe, panic, lint suppression, or other exception exists outside the syntax-visible surface that was scanned.
cargo-allow is not a linter, compiler wrapper, dependency-policy tool, or
unsafe proof system.
Clippy:
flags code patterns.
cargo-deny / cargo-vet:
govern dependency and supply-chain policy.
ripr / unsafe-review / coverage:
can provide evidence for retained exceptions.
cargo-allow:
owns the source-exception ledger: what is allowed, why, by whom, where,
with what evidence, until when, and whether this PR weakened posture.
Other tools can provide evidence. cargo-allow owns the durable receipt.
Most users start from the surface they already own.
| User type | First action | Main doc |
|---|---|---|
| Maintainer | Run cargo-allow doctor, then cargo-allow audit. |
Getting started |
| New adopter | Choose the closest path: source exceptions, no-new, spec-system, CI, or cross-repo rollout. | Onboarding |
| CI owner | Add cargo-allow check --mode no-new and upload the receipt. |
CI guide |
| Reviewer | Run cargo-allow diff --base origin/main. |
PR posture |
| Auditor | Run cargo-allow list and cargo-allow explain <id>. |
Explain an allow |
| Migrator | Run cargo-allow migrate --repo-policy <dir>. |
Migration |
| Agent operator | Run cargo-allow worklist --format json. |
Agent worklists |
Install:
cargo install cargo-allow --lockedFor a specific published release:
cargo install cargo-allow --version 0.1.9 --lockedUse the latest published version shown on crates.io. Do not copy release-candidate versions until they are published.
Check setup:
cargo-allow doctorInventory current exceptions:
cargo-allow auditStart strict, for a small repo:
cargo-allow init --root .Adopt no-new-debt, for an existing repo:
cargo-allow propose --write policy/allow.toml
cargo-allow check --mode no-newGenerated baseline entries are intentionally uncomfortable. Review them, narrow them, add evidence, or remove them.
For pull requests:
cargo-allow diff \
--base origin/main \
--format markdown \
--output target/cargo-allow/pr-summary.mdFor mainline:
cargo-allow check \
--mode no-new \
--format markdown \
--receipt target/cargo-allow/check.receipt.json \
--output target/cargo-allow/check.mdUpload target/cargo-allow/ as a CI artifact, especially on failure.
REVIEW REQUIRED allow-0042
kind: panic
family: indexing_slicing
path: crates/parser/src/span.rs
owner: parser
classification: validated_span_invariant
Evidence:
✓ doc:docs/safety/parser-spans.md exists
? test:parser_rejects_invalid_text_range not validated offline
Current match:
ast_kind: index_expr
container: slice_checked_text_range
selector precision: high
Claim boundary:
source-tree/source-syntax only; no macro expansion, type analysis, MIR,
control-flow, data-flow, or proof-tool execution.
A matching policy receipt is intentionally specific:
[[allow]]
id = "allow-0042"
kind = "panic"
family = "indexing_slicing"
path = "crates/parser/src/span.rs"
owner = "parser"
classification = "validated_span_invariant"
reason = "Parser validates TextRange before slicing."
created = "2026-06-01"
review_after = "2026-09-01"
evidence = ["doc:docs/safety/parser-spans.md"]
[allow.selector]
ast_kind = "index_expr"
container = "slice_checked_text_range"Evidence references can point to local files or traceability handles.
Locally checked examples:
doc:docs/safety.md
spec:docs/specs/parser.md
adr:docs/adr/0001.md
ripr:target/ripr/span-gap.json
unsafe-review:target/unsafe-review/ffi.json
coverage:target/coverage/receipt.json
Traceability-only examples:
test:parser_rejects_invalid_text_range
issue:#123
pr:#456
legacy-policy:no-panic-baseline
cargo-allow does not run those tools. It classifies what it can see and
reports what is missing.
cargo-allow worklist --format jsonWorklist items are intended for bounded human or agent work:
broken_evidence_link
weak_evidence_reference
baseline_debt
stale_allow
broad_scope
unsafe_missing_evidence
new_unreceipted_finding
Use the suggested actions and proof commands. Do not suppress findings just to pass CI.
| Surface | Current state |
|---|---|
| Source inventory | Git-tracked files first, filesystem fallback when needed. |
| Rust scanning | Source-syntax unsafe, panic-family, indexing/slicing, and lint suppressions. |
| Non-Rust scanning | Tracked scripts, workflows, docs/config, generated files, and other governed surfaces. |
| Policy | policy/allow.toml receipts with owner, reason, classification, lifecycle, selector, and evidence. |
| Evidence | Local evidence path checks plus traceability-only references. No proof-tool execution. |
| PR posture | diff --base ... reports new, removed, broadened, weakened, and improved exception posture. |
| Worklists | JSON queues for humans and agents to close retained-risk seams. |
| Migration | Legacy policy adapters for replacing bespoke xtask/TOML allowlists. |
| Need | Doc |
|---|---|
| Choose an adoption path | Onboarding |
| First hour | Getting started |
| Claim boundaries | Claim boundaries |
| Run in CI | CI guide |
| Explain retained exceptions | Explain an allow |
| Repair evidence | Fix broken evidence |
| Feed agents work | Agent worklists |
| Migrate legacy policy | Migration from xtask |
| Understand the model | Source exception ledger |
| Understand PR posture | PR posture |
| Understand policy weakening | Policy weakening |
| JSON artifacts | JSON schemas |
| Crate responsibilities | Crates |
| Changelog | CHANGELOG.md |
Most users only need cargo-allow.
The workspace uses allow-* crates for implementation layers:
allow-coreallow-policyallow-policy-legacyallow-inventoryallow-filesallow-rustallow-matchallow-diffallow-report
These crates are public because the workspace is split cleanly, but their
primary purpose is supporting cargo-allow. See the
crate responsibility guide and
crate namespace policy before adding new public
crates.
Use nearby project patterns, keep changes narrow, and validate source-tree posture before publishing a README or policy change:
cargo test -p cargo-allow readme
cargo run -p cargo-allow -- check --mode no-new --format markdown --receipt target/cargo-allow/check.receipt.json --output target/cargo-allow/check.mdMIT OR Apache-2.0