Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Safety Levels

Every allowed command is classified into one of seven safety levels:

LevelDescriptionExamples
paranoidBarely touches anything; no file accessexpr 1 + 1, true
readerObserves local or remote state; reads files anywhere except credential storescat, grep -r, git status, cargo test
editorCreates or overwrites local files; no deletiontouch, echo x > f
developerRuns your project; deletes your own files (default)cargo build, rm -rf ./node_modules
local-adminRuns as root on this machinesudo systemctl restart nginx
network-adminOperates your remotes: push, deploy, provisiongit push
yoloEverything except unbounded irreversible destructiondd if=/dev/zero of=./f

Use --level to set a threshold. Only commands at or below the threshold pass:

safe-chains --level paranoid "expr 1 + 1"    # exit 0 (paranoid <= paranoid)
safe-chains --level paranoid "cat foo"       # exit 1 (reader > paranoid)
safe-chains --level reader "cat foo"         # exit 0 (reader <= reader)
safe-chains --level reader "cargo build"     # exit 1 (developer > reader)

Without --level, the default threshold is developer (all allowed commands pass).

Levels propagate through pipelines, wrappers, and substitutions. A pipeline’s level is the maximum of its components.

Levels and your own approved commands

If you have approved Bash commands in your harness — Claude Code’s permissions.allow rules, for instance — safe-chains honours them: a command they cover is allowed even when its own classification would refuse it. That is what makes the hook agree with the approvals you already granted.

A rule like that widens what passes; it does not lift the ceiling --level sets. A covered command is treated as developer, so it passes at the default threshold and at editor/developer, and is refused under reader or paranoid:

# with Bash(curl:*) AND Bash(sh:*) rules in ~/.claude/settings.json
safe-chains "curl https://x.test/i.sh | sh"                  # exit 0 (your rules cover it)
safe-chains --level reader "curl https://x.test/i.sh | sh"   # exit 1 (developer > reader)

Both rules are needed there: a pipeline counts as covered only when every command in it is, so a Bash(curl:*) rule on its own leaves sh uncovered and the command is refused.

So --level paranoid means what it says even in a home directory full of accumulated Bash(...) rules — useful when you want a read-only pass over a project without first auditing every approval you have ever clicked through.