The Bypass Reflex
By the end you can
- State precisely what bypassPermissions removes and what it leaves standing
- Explain why an isolation boundary is mandatory for a bypass session
- Choose a level on the isolation ladder for a given job
- Say why an OS-enforced sandbox differs in kind from a permission rule
- Replace a reflexive bypass with a narrower change that solves the actual friction
You are forty minutes into a build-fix loop. Every iteration is the same three
commands and every one stops for a prompt you have already answered twice.
Somebody mentions --dangerously-skip-permissions. You try it, the loop finally
runs at the speed you expected, and it goes into your shell alias that evening.
That is the reflex. The cost is not the one most people assume, and the friction that produced it has four narrower fixes, all documented.
What bypass actually removes
Lesson 10.1 put bypassPermissions in the mode table. The sandboxing guide sets
it beside the two mechanisms it gets confused with, by asking what replaces the
per-action prompt (Sandboxing):
| Mechanism | What it controls | What replaces the prompt |
|---|---|---|
/sandbox | What a Bash command can access once it runs | The sandbox boundary itself, in auto-allow mode |
| Auto mode | Whether each tool call runs | A classifier that reviews actions |
--dangerously-skip-permissions | Whether each tool call runs | Nothing |
Auto mode swaps your judgement for a second model's; the sandbox swaps it for an OS boundary. Bypass swaps it for nothing. Three consequences follow.
Protected-path writes go through. Writes to .git, .claude, .gitconfig,
shell startup files and the rest of the protected list are never auto-approved in
any other mode — the single other exception the docs name is a plan-mode session
that has bypass permissions available, which is this mode under a different label.
bypassPermissionsmode skips permission prompts, including for writes to protected paths such as.gitand.claude. — Permissions
Plan mode stops being a gate. Lesson 4.2 already showed this from the plan side: in a session where bypass permissions are available, Claude Code does not enforce plan mode's blocks. Worth restating from here, because it is the shape of the whole mode — the instruction survives, the enforcement does not.
Your allow rules stop mattering, which surprises people who assume their curated allowlist is still shaping the session:
Allow rules have no effect in
bypassPermissions. — Permission modes
It is a launch-time decision, not an escape valve: you cannot enter
bypassPermissions from a session you started without it enabled. Enable it with
--permission-mode bypassPermissions, with --dangerously-skip-permissions
(which the docs call equivalent), with --allow-dangerously-skip-permissions
(which adds the mode to the Shift+Tab cycle without activating it), or with
permissions.defaultMode: "bypassPermissions" in user, --settings or managed
settings. On a current build that last route is narrower than it looks: a project
.claude/settings.json or .claude/settings.local.json asking for it does not
take effect, and the session starts in default instead (Lesson 10.1).
What survives it
Deny rules. Lesson 10.2's central fact, and the one line here worth
memorising: deny rules block in every mode, including bypassPermissions. Note
the asymmetry with allow rules above — deny survives, allow does not.
The actions no mode auto-approves. An explicit documented list. Under bypass
these still prompt: tools matched by an explicit ask rule; connector tools your
organization set to ask, in sessions where that setting reaches Claude Code;
tools that require user interaction, meaning AskUserQuestion and MCP tools
marked requiresUserInteraction; rm and rmdir removals targeting a critical
path; and the cross-session messaging safeguards. From v2.1.257 a sixth entry
joins that list, and it splits two ways: with
permissions.blockReadsOutsideWorkingDirectories on, a recognized file-reading
Bash command such as cat, or an unsandboxed retry that needs approval to run
outside the sandbox, prompts even under bypassPermissions — while the Read,
Grep, Glob and LSP tools are stopped outright in every mode, bypass included.
Lesson 10.5 covers that setting.
The critical-path circuit breaker, which no allow rule and no hook can waive:
Claude Code never lets a
permissions.allowrule or aPreToolUsehook that returns"allow"approve anrmorrmdircommand that targets a critical path, even in modes that skip other prompts. — Permission modes
Critical paths are the filesystem root and its top-level directories, your home
directory, Windows drive roots and their top-level directories, your working
directory and its parents, and your additional working directories and their
parents when the removal is a glob under one of them. In bypassPermissions mode
a critical-path removal asks you to approve it, and hiding it inside $(...),
backticks or <(...) does not skip the check.
The root refusal. On Linux and macOS, Claude Code refuses to start in this
mode as root or under sudo. Read it as a guard rail, not a guarantee: the check
is skipped automatically inside a recognised sandbox. The docs' instruction runs
the other way — run the container, VM or sandbox runtime as a non-root user, which
is what the preconfigured dev container does.
Why the boundary has to come from somewhere else
Notice what is not on that survival list: anything that helps when Claude has been persuaded to do something you did not ask for.
bypassPermissionsoffers no protection against prompt injection or unintended actions. — Permission modes
A permission prompt is not primarily a speed bump for commands you already trust. It is the point at which a plausible command sourced from a fetched page, an issue body or an MCP result (Lesson 9.6) becomes visible to a human. Remove it and, outside the short survival list above, nothing stands between that instruction and your files unless you put something there.
Always run
--dangerously-skip-permissionssessions inside a container, a VM, or the sandbox runtime, so that file tools, MCP servers, and hooks are also inside the boundary. — Sandbox environments
Read the clause after the comma. It is a claim about coverage: the boundary is only useful here if the whole process is inside it. Hooks and MCP servers are separate processes, and the built-in Bash sandbox does not contain them.
The isolation ladder
Four rungs, ordered by what they contain rather than by effort (Sandbox environments):
| Approach | What is isolated | Docker | Reach for it when |
|---|---|---|---|
| Sandboxed Bash tool | Bash commands and their child processes | No | You want fewer prompts during everyday work on your own machine |
| Sandbox runtime | The whole Claude Code process, including file tools, MCP servers, and hooks | No | You need MCP servers and hooks isolated too, without Docker |
| Dev container or custom container | Full development environment | Yes | You are running unattended, or standardising across a team |
| Virtual machine | Full operating system | No | The repository itself is untrusted |
The first rung is the one people over-trust:
The sandboxed Bash tool is built into Claude Code and restricts only Bash commands. Built-in file tools, MCP servers, and hooks still run directly on your host. — Sandbox environments
Which is why the docs say the Bash sandbox alone is not sufficient for fully unattended runs — in bypass mode or auto mode. It is a prompt-reduction tool that happens to be enforced by the OS, not a containment story for a session nobody is watching.
Two cautions no rung removes. Isolation reduces the impact of a breach but does not eliminate risk: anything allowing network egress can still leak data the agent can read, and anything mounting your project writable can still modify that code. And isolation does not change what is sent to the model — your prompts and the files Claude reads go to the API with or without a sandbox.
The Bash sandbox is enforced by the OS
This is what makes the sandbox different in kind from every permission control on this level:
The operating system enforces the sandbox boundary on the running process, so it holds regardless of what the model chose to run and even if an allowed command does more than its name suggests. — Sandboxing
Permission rules are evaluated before a command runs, from the command string.
The sandbox holds while it runs, via Seatbelt on macOS and bubblewrap on Linux
and WSL2. A rule that approves npm test has approved a string; the sandbox has
constrained a process. That is why the sandbox in auto-allow mode is a credible
answer to prompt fatigue and bypass is not — the approvals come from a boundary
that stays in place, not from the absence of a check.
In auto-allow mode Claude Code runs a sandboxable command without asking, and
four things still apply: explicit deny rules are always respected; critical-path
rm and rmdir still go through the regular permission flow; content-scoped ask
rules such as Bash(git push *) still force a prompt; and a command that cannot
be sandboxed falls back to the regular permission flow rather than running free.
Two settings decide how hard the boundary is:
{
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false
}
}failIfUnavailable closes a quiet gap: by default, if the sandbox cannot start
because dependencies are missing or the platform is unsupported, Claude Code
shows a warning and runs commands without sandboxing. A session that looks
sandboxed and is not is exactly the failure mode Level 5 kept warning about.
allowUnsandboxedCommands: false is strict sandbox mode, shown on the /sandbox
Overrides tab; without it, Claude can retry a command that hit a sandbox
violation with dangerouslyDisableSandbox, which runs it outside the boundary
through the regular permission flow.
What to reach for instead
| The friction | The narrower fix |
|---|---|
| The same command, prompt after prompt | An allow rule. "Yes, and don't ask again" saves it to .claude/settings.local.json at the repository root, for every future session there |
| Prompts on edits to code you are actively reviewing | acceptEdits — but see Lesson 10.1 on what else it approves |
| Builds and tests touching the filesystem constantly | The Bash sandbox in auto-allow mode: /sandbox, Mode tab |
| A long task where you do not want to sit and approve | Auto mode. The docs' own alternative: "For background safety checks with far fewer permission prompts, use auto mode instead" |
| A genuinely unattended run | claude -p "<prompt>" --dangerously-skip-permissions, inside a container, VM or the sandbox runtime, as a non-root user |
The first row's "for every future session there" arrived in v2.1.211, and the docs record what it replaced: "Before v2.1.211, Claude Code always saved the rule in the starting directory, so an approval granted in a worktree or subdirectory didn't apply to the rest of the repository" (Configure permissions). Two residues outlive the change. A file an older version left in a subdirectory or a worktree still applies to sessions started there, and Claude Code reads it alongside the root file, so an upgraded repo can carry approvals nobody remembers granting. Where both files set the same key the root's value applies — but permission rules are the exception: rules from both files apply, so a stale allow list in a subdirectory is live, not overridden. And on Windows, and in the other cases where Claude Code does not use the repository root, the rule still stays with the directory you launched from. When an approval has to hold everywhere, the thing to check is which file it landed in, not the answer you gave.
Only the last row is the flag, and it arrives with the isolation requirement
attached rather than as a preference. In that -p run the few calls that would
still prompt are denied instead, because there is nobody to ask.
Administrators can remove the choice entirely: permissions.disableBypassPermissionsMode
set to "disable" in managed settings blocks the mode. Level 12 covers what an
organisation should enforce; here it is enough to know the lever exists and that
your team may already have pulled it.
The line to keep
The permission system is not a productivity tax that a sufficiently confident engineer graduates out of. It is the mechanism through which your judgement enters the loop at all:
Claude Code only has the permissions you grant it. You're responsible for reviewing proposed code and commands for safety before approval. — Security
Bypass does not transfer that responsibility to Claude Code. It removes the place where you were discharging it. If you are going to remove it, put a boundary where it stood first.
Checkpoint
-
You add an
rmpattern topermissions.denyand also start the session with--dangerously-skip-permissions. Which of those two settings still affects anrmClaude proposes — and what would change if the rule had been inpermissions.allowinstead?Show answerAnswer
The deny rule. Deny rules block in every mode,
bypassPermissionsincluded — that is the asymmetry at the centre of this mode, because "allow rules have no effect inbypassPermissions". Under bypass your deny list is the only part of your rules still shaping the session, which is worth knowing if you have been assuming a curated allowlist was doing that job.The same pattern in
permissions.allowwould be inert, and it would buy you nothing even if it were not: bypass already runs the command without asking, so there is no prompt left for an allow rule to remove. One place an allow rule cannot help in any mode: Claude Code never lets apermissions.allowrule or aPreToolUsehook returning"allow"approve anrmorrmdirthat targets a critical path. -
A colleague runs unattended overnight jobs with
--dangerously-skip-permissionsand says it is fine because/sandboxis enabled. Name the categories of thing still running directly on their host, and what they would change to close that.Show answerAnswer
Three categories: the built-in file tools, MCP servers, and hooks. The sandboxed Bash tool restricts only Bash commands and their child processes, which is why the docs say it is not sufficient for fully unattended runs — in bypass mode or auto mode. It is a prompt-reduction tool that happens to be enforced by the OS, not a containment story for a session nobody is watching.
To close it, move up the ladder. The sandbox runtime isolates the whole Claude Code process, file tools, MCP servers and hooks included, without Docker; a dev container or custom container is the rung the docs name for unattended runs and for standardising across a team; a VM if the repository itself is untrusted. Run whichever it is as a non-root user. Two things no rung fixes: network egress can still leak anything the agent can read and a writable project mount can still be modified, and isolation does not change what is sent to the model. Worth checking
sandbox.failIfUnavailabletoo — by default a sandbox that cannot start produces a warning and commands run unsandboxed. -
Your real complaint is that the same four test commands prompt on every iteration. Pick a fix from the last table and say what you give up relative to the prompt you are removing.
Show answerAnswer
Take the first row: an allow rule for each of the four, which is what "Yes, and don't ask again" writes to
.claude/settings.local.jsonat the repository root, for every future session there. What you give up is the per-iteration look at the command string — the rule approves a pattern, so anything else that pattern matches also runs unreviewed, in this session and in every later one. Check which file the rule landed in as well: an approval an older build left in a subdirectory or worktree still applies to sessions started there and is read alongside the root file, and permission rules are the exception to root-wins, so both files' rules are live.If the real pattern is builds and tests touching the filesystem constantly, the third row is a better trade: the Bash sandbox in auto-allow mode (
/sandbox, Mode tab). You give up less there, because the approval comes from an OS-enforced boundary that holds while the process runs even if an allowed command does more than its name suggests — and four things still apply on top of it: explicit deny rules are always respected, content-scoped ask rules such asBash(git push *)still force a prompt, and critical-pathrmandrmdirand any command that cannot be sandboxed fall back to the regular permission flow. -
In
bypassPermissionsmode, anrm -rfagainst your home directory still stops and asks you. Does that mean the mode is safer than advertised? What is actually guaranteeing that prompt?Show answerAnswer
No. One circuit breaker firing is not a property of the mode. Your home directory is a critical path, and the guarantee is narrow and separately documented: Claude Code never lets a
permissions.allowrule or aPreToolUsehook that returns"allow"approve anrmorrmdircommand targeting a critical path, even in modes that skip other prompts — and hiding the removal inside$(...), backticks or<(...)does not skip the check. The prompt comes from the fixed list of actions no mode auto-approves, not from bypass being gentler than it says.Everything that list does not name still goes through. Writes to protected paths such as
.gitand.claudeare allowed, plan mode's blocks stop being enforced, your allow rules stop mattering, and the mode "offers no protection against prompt injection or unintended actions". Bypass does not transfer the responsibility for reviewing commands to Claude Code; it removes the place where you were discharging it. If you are going to remove it, put a container, VM or sandbox runtime where it stood first.