Claude Operator: Prompt to Autonomy · 20 min · 140 XP
Writing allow, deny, and path rules
Scope rules to the narrowest thing that works, and protect your secrets by path.
Rules name a tool and, in brackets, what it may act on. Bash(git status) is one command. Bash(git *) is every git command including push and clean. Read(./.env) is one file.
{
"permissions": {
"allow": [
"Bash(git status)",
"Bash(git diff *)",
"Bash(npm test)"
],
"deny": [
"Bash(rm -rf *)",
"Read(./.env)",
"Read(./secrets/**)"
]
}
}Allow the command, not the tool. Bash(*) removes the prompt entirely and with it your last chance to notice something unexpected. A handful of specific patterns covers the commands you run constantly, and everything else still asks — which is the point, because the prompt is doing work precisely on the commands you didn't anticipate.
Deny by path for secrets. A Read deny rule on ./.env or ./secrets/** stops Claude's file tools reading them at all. Worth doing even on a practice project, because the habit is what transfers. Note a useful asymmetry in how patterns are treated: a deny or ask rule whose path can't be used as a gitignore pattern still guards that exact path, while an allow rule with an unusable pattern approves nothing. Failure leans towards safety.
Test both directions. A rule you haven't tested is a belief. Confirm the intended command works and that a neighbouring risky one doesn't — write the rule, run the thing it should permit, then run the thing it should stop.
Practice. Write a narrow allow rule for one safe read operation and prove one intended match and one non-match. Scope a shell permission to a single harmless command pattern, then confirm a neighbouring risky command still stops. Add a deny rule for a destructive command and demonstrate the denial safely. Then protect a secret-like practice file by path and confirm its documented example counterpart is still readable.
Loading your workspace…