Why agents need permission
Through Chapter 05 your agent reads, writes, edits, and runs bash — for real, in a sandbox. That is exactly the problem. A loop that turns a model's tool_use into side effects, with nothing between the decision and the disk, is a foot-gun: one confident-but-wrong turn and it rm -rfs the working tree or overwrites .git. Permissions are the brakes. This chapter builds the engine that decides, for every single tool call, whether it allows, asks, or denys — before the side effect happens.
deny → ask → allow → mode default
Every call is checked against the rules in settings.json in a fixed order, and the first match wins:
deny— if any deny rule matches, the call is blocked. Nothing later can override it.ask— if any ask rule matches, the user is prompted.allow— if any allow rule matches, the call runs without a prompt.- mode default — no rule matched, so the active permission mode decides.
The order is not alphabetical and it is not configurable — it is the safety contract. deny is first precisely so that a deny is unconditional: a rule you wrote to stop Bash(rm *) can never be undone by a broad allow somewhere else in the file.
tool call ─▶ deny match? ──yes─▶ DENY
│ no
▼
ask match? ──yes─▶ ASK
│ no
▼
allow match? ──yes─▶ ALLOW
│ no
▼
mode default ───────▶ allow | ask | deny
The mode default is the fallthrough when no rule matched. There are four modes:
default— auto-allow read-only tools, ask before any write orbashside effect.acceptEdits— also auto-allow in-tree edits (Write/Edit), still ask for the rest.plan— read-only: every side-effecting tool is denied until you leave plan mode.bypassPermissions— allow everything (the escape hatch; use with care).
You'll build the rule matcher (step 02), then decide (step 03) which layers the mode default under the rules, then protected paths (step 04), then wire it into the loop (step 05).
A Bash(rm *) call matches both an allow rule and a deny rule in settings.json. What is the decision?