interlock (also python -m interlock) is installed with the package. Each
command's --help shows the same text as below. --no-color (before the
command) turns off color; color is also off when output isn't a terminal or
NO_COLOR is set.
| Command | Does |
|---|---|
doctor |
check that protection is actually on |
report |
summarize a receipt log: blocks, monitor findings, latency |
hook |
decide one coding-agent tool call (run by the agent) |
install-hook |
put interlock in front of Claude Code, Codex or Cursor |
policy |
check a declarative policy file, or show a built-in pack |
learn |
draft a reviewable policy from observed receipts |
trust |
trust a project's policy overlay |
mcp |
inventory the MCP servers configured on this machine |
reviews |
decide pending reviews; serve their status to agents |
drift |
diff a live surface's tool listing against a recorded corpus |
Check that interlock protection is actually on.
Runs host-level checks and, when you point it at them, checks your policy, your sidecar and your receipt log:
interlock doctor
interlock doctor --rules myapp.policy:RULES --receipts audit.jsonl --key-file audit.key
interlock doctor --sidecar ~/.interlock/guard.sock --jsonInside your own process, call interlock.posture.attest() (or require()) instead: it sees the installed engine, sink, guards and approver directly.
Exit codes: 0 enforced, 1 degraded (warnings), 2 unprotected (a check failed).
usage: interlock doctor [-h] [--rules MODULE:ATTR] [--policy FILE] [--sidecar PATH]
[--receipts FILE] [--key-file FILE] [--json]
--rules load this policy (same spec as interlock-sidecar --rules)
--policy load this declarative policy file (JSON, or YAML with PyYAML)
--sidecar probe the sidecar listening on this socket
--receipts check this receipt log
--key-file the receipt MAC key, to verify the log's chain
--json machine-readable output
Summarize a receipt log: what interlock blocked, rewrote and escalated, what monitor mode would have done, how long evaluation took, and whether any receipts were lost.
interlock report audit.jsonl --key-file audit.key
interlock report audit.jsonl --since 24h --monitor # "would have blocked"
interlock report audit.jsonl --jsonThe chain is verified first when --key-file is given; the report always says whether it was. Receipts hold argument digests, never values.
Exit codes: 0 ok, 2 the chain is broken, 3 the log could not be read.
usage: interlock report [-h] [--since DURATION] [--monitor] [--top N] [--key-file FILE] [--json]
file
file a receipt log written by FileSink
--since only receipts newer than this: 30m, 24h, 7d (default: all)
--monitor show what monitor mode would have blocked or rewritten
--top rows per table
--key-file the MAC key, to verify the chain
--json machine-readable output
Decide one coding-agent tool call (run by the agent, not by you).
The agent passes its hook payload on stdin; the verdict goes back on stdout and
the exit code, in that agent's own protocol. interlock install-hook writes
the command line; you do not normally run this by hand, except to test:
echo '{"hook_event_name":"PreToolUse","tool_name":"Bash",
"tool_input":{"command":"git push --force origin main"},
"session_id":"s","cwd":"/tmp"}' | interlock hook --agent claude-codeAny error - unreadable input, a policy that will not load, a crash - denies the call (fail closed) unless --on-error allow, which lets the call through and says so loudly.
usage: interlock hook [-h] --agent {claude-code,codex,cursor} [--policy FILE]
[--rules MODULE:ATTR] [--sidecar PATH] [--receipts FILE] [--key-file FILE]
[--no-receipts] [--no-overlay] [--monitor] [--on-error {deny,allow}]
[--reviews {fallback,always,off}] [--review-db FILE]
--agent
--policy policy file (default ~/.interlock/policy.json, else the built-in coding-agent pack)
--rules a Python policy module instead
--sidecar ask this sidecar instead
--receipts receipt log (default ~/.interlock/receipts.jsonl when its key exists)
--key-file receipt MAC key
--no-receipts write no receipts
--no-overlay ignore project overlays
--monitor record what would happen, never block
--on-error what an internal error does (default deny)
--reviews settle escalations through the review queue: when the agent can't ask (default), always, or never
--review-db review queue (default ~/.interlock/reviews.db)
Put interlock in front of a coding agent's tool calls.
interlock install-hook --for claude-code
interlock install-hook --for all --dry-run # show the change, write nothing
interlock install-hook --for codex --uninstallRegisters interlock hook --agent <name> in the agent's hook settings
(~/.claude/settings.json, ~/.codex/hooks.json, ~/.cursor/hooks.json), keeping
every other entry, backing the file up first (.interlock-bak) and
replacing any earlier interlock entry. The hook runs this Python and this
interlock package, whatever directory the agent runs it from, and denies the
call if it cannot start. It also creates ~/.interlock with a default policy
that extends the built-in coding-agent pack and a key for the receipt log.
Restart running agent sessions afterwards; re-run this if interlock moves.
Run this yourself, outside the agent: the coding-agent pack refuses it from inside one.
Exit codes: 0 done, 1 an agent could not be configured, 64 bad arguments.
usage: interlock install-hook [-h] --for {claude-code,codex,cursor,all} [--settings FILE]
[--policy FILE] [--sidecar PATH] [--receipts FILE] [--key-file FILE]
[--monitor] [--dry-run] [--uninstall]
--for
--settings the agent's settings file (default: its usual path)
--policy pin this policy file in the hook
--sidecar decide through this sidecar
--receipts receipt log path
--key-file receipt key path
--monitor install in monitor mode
--dry-run print the change only
--uninstall remove interlock's hooks
Check a declarative policy file before you rely on it.
interlock policy check policy.json
interlock policy check ~/.interlock/policy.json --overlay .interlock/policy.json
interlock policy show builtin:coding-agent # print a built-in packcheck loads the file the way the guard will: it lists the rules, the locked
ones, any rule quarantined as invalid (with why), and each overlay's status.
Exit codes: 0 valid, 1 loaded with quarantined rules, 2 does not load.
usage: interlock policy [-h] ACTION ...
usage: interlock policy check [-h] [--overlay FILE] [--json] file
usage: interlock policy show [-h] name
Draft a reviewable policy from a receipt log: the actions your guard actually allowed, and an escalation for everything else.
interlock learn --receipts audit.jsonl --out baseline.json
interlock learn --receipts audit.jsonl --out baseline.json --name acme-baselineReads a receipt log written by FileSink, keeps the actions observed with an ALLOW verdict (matched case-insensitively), and writes a declarative policy whose single rule escalates any action outside that envelope. The draft is deterministic - two runs over the same log are byte-identical - so a diff between two drafts is worth reviewing.
It is never auto-enforced: this command only writes the file you name. It
installs nothing, enables nothing, locks nothing and emits no block rule.
Review it with interlock policy check and install it on purpose.
Exit codes: 0 wrote the policy, 3 the log could not be read, held no ALLOW-observed receipt, or the output could not be written (no file is written in those cases); 64 the command line was wrong.
usage: interlock learn [-h] [--receipts FILE] [--out FILE] [--name NAME]
--receipts a receipt log written by FileSink (required)
--out where to write the draft policy (required)
--name the policy 'name' field (default: learned-behavioural-baseline)
Trust (or stop trusting) a project's policy overlay.
interlock trust add # the overlay found from this directory
interlock trust add path/to/.interlock/policy.json
interlock trust list # ok / CHANGED / MISSING
interlock trust remove [PATH]An overlay (.interlock/policy.json in a repository) is ignored until trusted,
and trust is tied to the file's exact content: edit it and it is ignored again
until you re-trust it. add shows what the overlay adds before recording it.
Run this yourself, outside the agent - the coding-agent pack refuses it from
inside one.
usage: interlock trust [-h] [--store FILE] ACTION ...
--store trust file (default ~/.interlock/trusted.json)
usage: interlock trust add [-h] [--yes] [path]
usage: interlock trust list [-h]
usage: interlock trust remove [-h] [path]
Inventory the MCP servers configured on this machine.
interlock mcp scan # every server, with risk notes
interlock mcp scan --json
interlock mcp scan --emit-policy mcp.json # a rule blocking unlisted servers
interlock mcp where github # every config that defines itReads Claude Desktop, Claude Code, Cursor, VS Code, Windsurf, Gemini CLI and Codex configs (user scope, plus the project around --cwd). Never starts a server and never touches the network. Environment and header values are not read into the report - only their names.
Exit codes: 0 no risks found, 1 at least one risk note, 2 nothing matched (where).
usage: interlock mcp [-h] ACTION ...
usage: interlock mcp scan [-h] [--json] [--cwd CWD] [--emit-policy FILE]
usage: interlock mcp where [-h] [--cwd CWD] name
Decide pending reviews, and serve their status to agents.
interlock reviews list # pending first; --status, --all
interlock reviews show r-1a2b3c4d
interlock reviews approve r-1a2b3c4d # --as NAME (default: your login)
interlock reviews reject r-1a2b3c4d --reason "use the staging table"
interlock reviews revoke r-1a2b3c4d
interlock reviews wait r-1a2b3c4d --timeout 120
interlock reviews mcp # read-only MCP server (stdio)A review is filed when a call escalates and is settled through the queue (see interlock.reviews). After an approval the agent retries the identical call, which runs once. Run approve/reject/revoke yourself, outside the agent: the coding-agent policy refuses them from inside one.
For the agent, add the MCP server to its config, e.g. for Claude Code:
{"mcpServers": {"interlock-reviews": {"command": "interlock", "args": ["reviews", "mcp"]}}}Exit codes: 0 done, 1 the review can't be changed (wrong state, self-approval, unknown id), 64 bad arguments.
usage: interlock reviews [-h] [--db FILE] [--json] ACTION ...
--db review queue (default ~/.interlock/reviews.db)
--json machine-readable output
usage: interlock reviews list [-h] [--status {pending,approved,rejected,revoked,expired,executed}]
[--limit LIMIT]
usage: interlock reviews show [-h] id
usage: interlock reviews approve [-h] [--as NAME] id
usage: interlock reviews reject [-h] [--as NAME] [--reason REASON] id
usage: interlock reviews revoke [-h] [--as NAME] id
usage: interlock reviews wait [-h] [--timeout TIMEOUT] id
usage: interlock reviews mcp [-h]
Diff a live surface's tool listing against a recorded corpus.
interlock drift --corpus corpus.json --live "my-mcp-server --list-tools"
interlock drift --corpus corpus.json --live "..." --surface demo
interlock drift --corpus corpus.json --live "..." --jsonThe corpus records each covered surface's tool-listing fingerprint and schema
shapes (see interlock/drift.py). --live runs a command and reads its
standard output as the live tool listing: one tool name per line, or one JSON
object per line when you also record schema shapes. --live is a shell
string, so a command with arguments or quoted paths works as written.
Every added tool, removed tool and changed schema shape is reported with a severity, and any finding is drift. Nothing is written back and nothing is filed - the corpus is only ever read - so the diff is a report you act on.
Exit codes: 0 no drift, 1 drift found, 64 the command line was wrong, 3 the corpus could not be read or the live command failed.
usage: interlock drift [-h] [--corpus FILE] [--live CMD] [--surface NAME] [--json]
--corpus the recorded corpus to diff against
--live a shell command whose stdout is the live tool listing
--surface which corpus surface the live command describes (default: the corpus's only surface)
--json machine-readable output