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.

Narrow allows, decisive denies
{
  "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…