Illustration · planned interface
This screen is a mockup of a graphical interface that does not
exist yet. The shipped product is a command-line tool: one static binary that runs locally, with no account
and no server. The project, the file counts and the log below are a worked example, not a real scan.
Scan in progress
Reading C:\dev\my-saas
Reading files. Not executing them, not installing anything, not calling anything. The scan is root-scoped and symlink-safe: a link pointing outside the project root is skipped and reported.
Files walked12,481
Elapsed3.4s / 60s budget
Read budget512 MiB total
The five detection layers
Each layer corresponds to one of the five places an obligation hides. A layer that finds nothing says so explicitly — "clean" and "not looked at" must never look the same.
01
Code licence
Manifests, lockfiles, vendored LICENSE / COPYING / NOTICE, SPDX headers in source
done · 47
02
Model weights
Magic-byte detection, GGUF metadata, config.json, model-card frontmatter
done · 3
03
Platform ToS
Third-party CLI orchestration detected from source AST and config files
running…
04
Brand assets
Name, logo and icon files reserved outside the grant — forces a rebrand obligation
queued
05
Territories
Territorial clauses evaluated against your declared jurisdictions
queued
Scan log
deterministic order · no map iteration3.402SCANwalk complete · 12,481 files · 2 skipped
3.404WARNSkipped symlink 'docs/link' → '/etc/passwd' (outside project root).
3.405WARNcannot parse pnpm-lock.yaml: unsupported lockfile — falling back to package.json ranges (E-PARSE-001).
3.410L147 dependencies resolved
3.412L1licence trap: firecrawl · AGPL-3.0-only
3.418L23 weight files by magic bytes
3.421L2weights licence from model-card frontmatter: CC-BY-NC-4.0
3.424L2no licence statement for models/embed.bin
3.426L3scanning for upstream CLI orchestration…
——awaiting layer 3
What the scanner will not do
enforced by test✓Never executes project code — no os/exec outside the CLI layer
✓Never runs a package manager — lockfiles only, nothing installed
✓Never reads a .env — its existence is recorded, its contents are not
✓Never follows a symlink out of the project root
✓Never makes an outbound call in --offline — asserted, not promised
✓Never writes to the scanned tree — read-only, always
✓Never parses a billion-laughs YAML or a deep-nesting bomb — refused, not crashed
Seven invariants, each with a named test in internal/guard_invariants_test.go and the security suite.
A degraded scan is still a scan. If a file is too large, unparseable or
unreadable, Clearance records the gap and keeps going — then reports it. It never silently drops a
dependency, and it never rounds a gap up to "clean".
Budgets and limits
Every limit has a defined behaviour, because "it just hung" is not an error state anyone can act on.
| Limit | Value | On breach | Code | Exit |
|---|---|---|---|---|
| Files walked | 200,000 | Truncate, report the ratio | E-SCAN-003 | 0 |
| Single file size | 8 MiB | Skip parsing, record the file | E-SCAN-006 | 0 |
| Directory depth | 24 | Stop descending, report the node | E-SCAN-008 | 0 |
| AST size | 2 MiB | Fall back to string signals | E-SCAN-014 | 0 |
| JSON nesting depth | 64 | Refuse to parse — treat as hostile | E-PARSE-006 | 0 |
| Total scan time | 60s | Fatal — no verdict produced | E-SCAN-019 | 2 |
| Ignore breadth | > 50% of tree | Warn — an ignore may be hiding a dependency | E-SCAN-020 | 0 |
Only the timeout is fatal. Everything else degrades and says so — a partial answer with a stated gap is more useful than no answer, as long as the gap is visible.