Skip to content

The CLI

The library also ships as a small command-line scanner with the same engine and the same zero-network scan. Scan files, stdin, or a unified diff; detected values are always masked in the output.

Install

# Homebrew (macOS & Linux)
brew install hoophq/tap/alcatraz

# or grab a checksum-verified binary from GitHub releases, or build from source
go install github.com/hoophq/alcatraz/cmd/alcatraz@latest

Commands

alcatraz scan [flags] [file...]      scan files, or stdin when no file is given
alcatraz diff [flags]                scan only the lines a unified diff adds
alcatraz hook claude-post [flags]    Claude Code PostToolUse output rewriter
alcatraz hook claude-prompt [flags]  Claude Code UserPromptSubmit guard
alcatraz models download [flags]     fetch and verify the optional NER model
alcatraz models verify [flags]       re-check an already-seeded model directory
alcatraz models pins [flags]         print a model's pin table entry as JSON
alcatraz version                     print the version (also -version/--version)
alcatraz scan secrets.log app.log      # scan files line by line
git diff | alcatraz diff               # scan only the lines a diff adds
pbpaste | alcatraz scan                # scan pasted text from stdin
alcatraz scan -json report.log         # machine-readable output (masked too)

Exit codes

Grep-style: 0 clean, 1 findings, 2 error.

Hook mode always exits 0: findings travel in the hook's output, never in the exit code, so a detection never fails the surrounding tool. That holds even for bad flags, an oversized payload or a failed -chain.

Flags: scan and diff

Flag Default Meaning
-threshold 0.4 drop results scoring below this
-entities all comma-separated entity types to restrict to
-ignore DATE_TIME,URL entity types to suppress
-allowlist-file none one allowed value per line, # comments
-json false machine-readable output (values still masked)
-exclude none glob patterns of paths to skip; consulted by diff only
-context true context-aware scoring; -context=false scores each match on its pattern alone

See Context-aware scoring for what -context changes.

Flags: hook claude-post

Masks PII in tool outputs before they enter model context.

Flag Default Meaning
-threshold 0.5 drop results scoring below this
-entities all comma-separated entity types to restrict to
-ignore DATE_TIME,URL,IP_ADDRESS entity types to suppress
-context true context-aware scoring
-skip-tools Read comma-separated tool names whose output is left alone
-chain none upstream rewriter to compose with, so two output rewriters never race

Flags: hook claude-prompt

Warns or blocks when the user's own prompt carries PII.

Flag Default Meaning
-threshold 0.5 drop results scoring below this
-entities all comma-separated entity types to restrict to
-ignore DATE_TIME,URL,IP_ADDRESS entity types to suppress
-context true context-aware scoring
-mode warn warn annotates the prompt; block rejects it

Note

The hook defaults are stricter than scan: threshold 0.5 instead of 0.4, and IP_ADDRESS added to -ignore because tool output is full of addresses that are not PII.

Flags: models download

Flag Default Meaning
--dest the cache ner.New reads from install into a self-contained directory instead
--model KnightsAnalytics/distilbert-NER which pinned model to fetch
--origin the model's pinned origin fetch from a mirror laid out like the hub

All accept a single dash too; the double-dash form is what ner's own error messages tell you to run, so it is the spelling used throughout these docs.

--origin points the fetch at an internal bucket or an air-gapped cache. Only the base URL moves — the layout under it stays {origin}/{model}/resolve/{revision}/{file} — so a mirror is a bucket keyed like the hub, not a different command. The pinned digests are unchanged, so a mirror serving anything else fails exactly as a corrupt transfer does and installs nothing. Run without the flag to see each pinned model's origin in the usage text; the origin actually used is printed before the fetch starts.

Flags: models verify

Flag Default Meaning
--dir the cache ner.New reads from models directory to check
--dest alias for --dir, so a download line copies across unedited
--model KnightsAnalytics/distilbert-NER which pinned model to check

verify re-checks a directory download already filled. It opens no socket and writes nothing — not even the default cache directory, which it names but does not create — so it is safe against a read-only mount and inside a network-sealed build. It exits non-zero naming the first file that is absent, mismatched or unreadable; those are three different problems, and the message says which one you have.

Two callers need this rather than a second download: a CI job proving an image really carries the model it claims to, asserted from outside because a distroless image has no shell, and an operator diagnosing a volume the model runtime rejected.

Note

--dir is the models directory — the parent holding every model — which is the same one download --dest fills. Passing a model's own directory is the classic slip; verify recognises it and points at the parent instead of telling you to download a second copy nested inside the first.

The flag is --dir rather than --dest because this command writes nothing, so it has no destination — but --dest names the same directory and is accepted, so the two lines in a runbook differ only in the verb:

alcatraz models download --dest /opt/alcatraz/models   # in the build
alcatraz models verify   --dest /opt/alcatraz/models   # in the test that follows

Flags: models pins

Flag Default Meaning
--model KnightsAnalytics/distilbert-NER which pinned model to describe
--list off print every pinned model id, one per line, and exit

Prints the pin table's entry as JSON — revision, origin, licence, and every file with its digest, size and the key the downloader requests:

{
  "model": "KnightsAnalytics/distilbert-NER",
  "revision": "13a742d5ea02349d17e18f3755301282c9ee33f7",
  "origin": "https://huggingface.co",
  "license": {
    "id": "Apache-2.0",
    "source": "https://huggingface.co/dslim/distilbert-NER"
  },
  "files": [
    {
      "key": "KnightsAnalytics/distilbert-NER/resolve/13a742d5.../config.json",
      "path": "config.json",
      "name": "config.json",
      "sha256": "a2b1...",
      "size": 925
    }
  ]
}

files holds every pinned file; one is shown here, and the digest and key are elided for width.

This exists so a tool filling a mirror reads the same table the downloader verifies against, rather than keeping its own list that drifts. key is precomputed for the same reason: the layout lives in one place.

alcatraz models pins | jq -r '.files[].key'   # what the bucket has to hold
alcatraz models pins --list                   # every model that has to be mirrored

hack/mirror-model.sh drives the aws CLI from this output — it downloads and verifies locally, uploads write-once, then round-trips through --origin to prove the mirror actually serves what the downloader will accept.

Two workflows wrap it. Mirror model publishes, manually, assuming an AWS role via OIDC behind a reviewer gate. Mirror check runs on any PR touching models/, and fails it if the mirror does not already serve every pinned file at its pinned size — so a pin that nobody mirrored is caught there rather than in a later image build.

Model licences

Both commands print the model's pinned licence, and alcatraz models lists it per model. Baking weights into an image you publish is a redistribution, so the question is answered before the first build, not during one.

The identifier travels with the URL declaring it, because the two are not always the same repository:

license: Apache-2.0 (declared at https://huggingface.co/dslim/distilbert-NER)

KnightsAnalytics/distilbert-NER is an ONNX conversion published with no model card, so it declares nothing; the licence is inherited from the weights it converted.

Note

The fine-tune is trained on CoNLL-2003, whose underlying Reuters corpus has terms of its own. Those bind the training data rather than the published weights, but they are worth checking on a larger candidate model — the class most likely to be non-commercial.

See Running NER without internet access for the deployment patterns these commands exist for.

Network

Scanning never touches the network: no telemetry, no lookups, nothing leaves the process. The single exception is alcatraz models download, which fetches the optional NER model from pinned, checksum-verified URLs when you ask it to.

Integrations

The CLI powers the Hoop plugin for Claude Code's /hoop:pii-scan command and pairs with alcatraz-action for CI. The Hoop plugin wires both hook processors up without extra configuration.