As Chapter 1 explained, Claude Code passes a permission gate every time it calls a tool. Picture that as one dial and, the moment it gets noisy, you turn it to the loosest setting. In reality there are how often you are asked, permission per tool, and isolation at the OS level—three of them, with different jobs. Tighten one and you can afford to loosen another, so "safe or fast" is not a choice of two.
Delegation is not all or nothing
The starting point is not the name of a mode. It is "in this piece of work, where does it stop being recoverable?" Three things tell you.
- Can you undo it — an edit git can revert costs you little. Permanent deletion, a force push, a wrecked database cannot be undone
- How far does it reach — does it stay inside the working directory, or does it reach production, a shared branch, someone else's environment?
- Does it touch secrets — a leak happens when being able to read them and being able to send them out hold at the same time
The tedious operations are the ones that are safe on all three axes (tests, formatting, local edits), and the dangerous ones barely come up. Loosen uniformly and you open the dangerous side too. Loosen by frequency, tighten by nature is the skeleton.
Permission modes — what actually changes
The biggest dial is the permission mode, which sets the broad frequency of the prompts. Shift+Tab cycles through them (explained in Claude Code permission modes).
Reads go through automatically, edits and command execution are confirmed every time. The default for a repository you are new to.
Auto-approves edits inside the working folder. Anything outside it, protected paths, and other commands still get a prompt.
It investigates but does not edit source. You approve the plan, then it moves to carrying it out.
A separate judging model looks at each operation before it runs and stops only the dangerous ones. Conditions apply.
Passes neither the prompts nor the safety checks. For isolated environments only. Active only when launched with the dedicated flag.
There is also a settings-only dontAsk, which runs the allowlist and read-only commands and refuses everything else.
Watch the protected paths too. Writes to .git, .claude, and your shell configuration are not auto-approved in the ordinary modes. You can put the default mode in your settings, but auto mode alone is ignored in project settings—a line drawn so that a repository cannot switch it on by itself.
Auto mode is not a faster default
The prompts nearly disappear, but nothing is running loose: the judging model stops operations that go beyond the scope of your request, operations that touch infrastructure it does not know, and operations steered by content it has read. What passes easily is file work inside the working folder and read-only HTTP. What gets stopped is sending confidential data out, deploying to production, and destructive git operations.
Boundaries you state in conversation are not saved. "Do not push" can be grounds for a block, but it is reread from the current conversation each time, so if compaction drops it, the boundary goes with it. On top of that, auto mode is a research preview, and Anthropic itself says it reduces the prompts without guaranteeing safety. It is not a substitute for review.
Why you still get asked in bypass mode
You passed --dangerously-skip-permissions, which was supposed to skip the confirmations, and Claude still asks "may I run this?" Nothing is broken. Permission has two independent layers, and bypass can remove only one of them (why Claude still asks for permission in bypass mode).
"May I edit this file?"—the interactive dialog that appears just before a tool is called. Something the Claude Code program puts up.
"This will change the production database, shall I go ahead?"—a check that comes back as conversational text. It comes from the model's principles of behavior, so no flag stops it.
You tell them apart by whether it is UI or reply text. What triggers layer 2 is close to the three axes above—irreversible, wide-reaching, high security risk. On a permanent deletion, a force push, or a migration against production, Claude pauses even with permissions wide open.
This is design, not a defect. What bypass hands over is the key to using the tools, not a key that switches off Claude's judgment. Layer 2 cannot be reduced to zero.
You can lower the frequency. Put in CLAUDE.md only the premises you can state as fact, and make requests specific—"delete the .log files in /tmp/" draws fewer prompts than "clean this up". But bypass is not the answer to "the prompts are annoying" (the risks of bypass mode and how to use it safely).
Rules in settings.json — never answer the same prompt twice
If a mode is the overall frequency, permission rules are the exceptions per tool and per command. You write allow (no prompt) / ask (prompt every time) / deny (forbidden) into settings.json and share it (setting up permission rules (allow/ask/deny)).
Evaluation runs deny → ask → allow, and the first match wins; being more specific does not change the order—a broad deny on Bash(aws *) beats a specific allow on Bash(aws s3 ls). A deny cannot carry exceptions. "I allowed it and I still get asked" is usually just another ask matching first. Because ask forces a prompt even in auto mode, it is where operations you cannot undo belong.
// .claude/settings.json
{
"permissions": {
"defaultMode": "acceptEdits",
"allow": ["Bash(npm run *)"],
"ask": ["Bash(git push *)"],
"deny": ["Read(.env)", "Read(~/.ssh/**)"]
}
}
Where you put them has a ranking too. Strongest first: managed settings (cannot be overridden) → CLI → settings.local.json → settings.json → ~/.claude/settings.json. But a deny at any level always beats an allow at any other level. In the specifiers, a space plus * is a word boundary (Bash(ls *) matches ls -la but not lsof).
What the rules do not protect you from
All a rule looks at is the text of the command about to run, and any route that steps outside that text goes straight past it.
- Indirect access cannot be stopped — a deny on
Read(.env)works against the built-in file tools and againstcat, but it does not work against a script that opens the file - Environment runners hide their contents —
devbox run *anddocker execrun their arguments verbatim, so an allow onBash(devbox run *)permitsdevbox run rm -rf .as well - Restricting URLs by argument is fragile — reordering or variable expansion slips through. Denying
curlandwgetoutright and naming the permitted destinations withWebFetch(domain:)is sturdier
Writing "do not read .env" in CLAUDE.md is not a rule. CLAUDE.md changes what Claude will try to do; it does not change what it is allowed to do. What changes that is rules, modes, and the PreToolUse hook from Chapter 6 (deny and ask are evaluated regardless of what the hook returns).
The sandbox — what it can fence in, and what it cannot
What closes off the routes rules cannot reach is the sandbox—instead of asking "what should I confirm?", it fences off "how far can anything reach?" up front. The enforcement comes from the OS (the kernel) rather than from Claude, so the boundary does not move even when an already-permitted command does more than its name suggests (the complete guide to the Claude Code sandbox).
It can write to the working directory and a temporary folder, and nowhere else (the initial setting). ~/.bashrc and system areas cannot be rewritten.
The initial state is deny by default, with zero destinations. Connecting to a new domain raises a prompt, and registering it in allowedDomains stops the asking.
These two always go together—with only one of them, a secret it could read gets out. There are two ways to approve. Auto-allow mode runs the Bash inside without prompting, while ordinary permission mode isolates and still puts the prompts through. Even under auto-allow, deny always takes priority, an rm aimed at an important path is confirmed, and a content-specific ask still forces a prompt.
Beware that there are two things called "auto". The sandbox's auto-allow mode means "let it through because the OS boundary contains it", while the permission system's auto mode means "let it through because the judging model examined it".
Learn what it does not protect first
The sandbox is not complete isolation. Leaving that vague and keeping auto-allow switched on permanently is the most dangerous state to be in.
- It covers Bash and its child processes only — the built-in Read, Edit, and Write, MCP servers, and hooks are outside it (control those with permission rules). To wrap the whole process, use
@anthropic-ai/sandbox-runtime - The default for reading is wide — what it fences in is mainly writing, and
~/.sshand~/.aws/credentialsare readable as things stand. Close them withdenyRead - Allowing too much becomes the hole — traffic is judged by hostname, and encrypted contents are not inspected by default. Permit a broad domain and you leave room for things to get out
- It depends on your environment — macOS needs nothing extra. Linux and WSL2 need
bubblewrapandsocat, and native Windows is not supported
The sandbox is not a wall built to face an attacker; it is a safety harness that cuts accidents and runaway behavior by an order of magnitude. Anthropic has stated that it safely reduced permission prompts by 84% internally, but that is a report of fewer prompts, not a guarantee that it cannot be broken.
Accidents happen in roughly four shapes
Here is the machinery lined up again from the accident side. Choose settings by whether they work against these four.
1. Destructive commands
rm sweeping a path nobody expected, git push --force wiping out someone else's work. What they share is being impossible to undo. What works is deny and ask rules plus filesystem isolation. Dropping operations you cannot undo into ask is the reliable move.
2. Leaking secrets
This only becomes an accident when can read and can send line up together. The countermeasure is two-part as well—do not let it read (a deny on Read(.env), denyRead) and do not let it send (deny on curl and wget, a narrow list of permitted domains). One of the two is not enough.
3. Reaching a different branch or a different environment
What you meant as a one-file local fix grows into a push to a shared branch or a deploy to production—escalation of the operation. In the ordinary mode a prompt sits at each step, but open everything up and the steps flatten out. Putting push and deploy into ask is cheap insurance.
4. Instructions smuggled in through files it reads
A README, an issue, a web page, or a PDF that Claude reads can have instructions aimed at Claude planted in it (prompt injection). Even if "send this to that address" is written in white text, from Claude's side it is text arriving through the same door as your request, and data and instructions cannot be told apart on their face.
What does not work is bypass mode (the planted text reaches execution untouched) and a ban written in CLAUDE.md (it does not change what is allowed). What does work is a boundary at the OS level—it cannot write where it cannot write, and cannot reach what it cannot reach—together with deny rules. Planted text is invisible, so do not rely on noticing it: read untrusted material with permissions narrowed for that session only.
Safe to delegate / must be stopped
Apply the three axes and the line falls here.
Tests, type checks, linters, builds / edits inside the working directory / reading and searching / local commits
Pushes and force pushes to shared branches / production deploys and migrations / reading secret files / commands that send data outside / writes outside the working directory
- Pick one everyday mode. acceptEdits for repetitive editing, default for sensitive work. Write it in
defaultMode - Answer the same prompt three times and move it to allow. Conversely, any operation that made you think "that looks risky" even once gets pinned to ask
- Treat anything you forbade in conversation as something that will disappear.
- When you loosen something, always tighten something else. The one state never to create is no prompts and no boundary
When bypass is acceptable. Inside a container, VM, or CI runner you can throw away if it breaks, with only the working directory mounted, and the host's .env and SSH keys kept out of it. "The prompts are a nuisance" is not a reason. Always read the diff afterwards.
Everything covered here reduces accidents; none of it eliminates them. Layer 2's judgment, auto mode's verdicts, and the sandbox's boundary all have shapes that get through them. Keeping yourself in a state you can undo is what works best.
Summary
- Permissions are three layers: modes, rules, and the sandbox. The axes are can you undo it, how far does it reach, does it touch secrets, and the rule is loosen by frequency, tighten by nature
- Permission has two layers. Bypass removes only the layer 1 UI, and the check that comes back as conversation text is working as designed
- Rules run deny → ask → allow and the first match wins; specificity does not change the order
- Rules only look at strings. What closes that gap is the sandbox—though it covers Bash and its child processes only, and the default for reading is wide
- Accidents take four shapes: destructive commands, leaking secrets, reaching a different environment, and smuggled-in instructions
Once the scope of what you delegate is settled, the rest is growing it to fit your environment. Move on to Chapter 6, "Extending It".