CCLearn

The Bypass Reflex

verified against Claude Code 2.1.261 · 2026-09-0913 min

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):

MechanismWhat it controlsWhat replaces the prompt
/sandboxWhat a Bash command can access once it runsThe sandbox boundary itself, in auto-allow mode
Auto modeWhether each tool call runsA classifier that reviews actions
--dangerously-skip-permissionsWhether each tool call runsNothing

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.

bypassPermissions mode skips permission prompts, including for writes to protected paths such as .git and .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.allow rule or a PreToolUse hook that returns "allow" approve an rm or rmdir command 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.

bypassPermissions offers 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-permissions sessions 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):

ApproachWhat is isolatedDockerReach for it when
Sandboxed Bash toolBash commands and their child processesNoYou want fewer prompts during everyday work on your own machine
Sandbox runtimeThe whole Claude Code process, including file tools, MCP servers, and hooksNoYou need MCP servers and hooks isolated too, without Docker
Dev container or custom containerFull development environmentYesYou are running unattended, or standardising across a team
Virtual machineFull operating systemNoThe 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:

json
{
  "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 frictionThe narrower fix
The same command, prompt after promptAn 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 reviewingacceptEdits — but see Lesson 10.1 on what else it approves
Builds and tests touching the filesystem constantlyThe Bash sandbox in auto-allow mode: /sandbox, Mode tab
A long task where you do not want to sit and approveAuto mode. The docs' own alternative: "For background safety checks with far fewer permission prompts, use auto mode instead"
A genuinely unattended runclaude -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

  1. You add an rm pattern to permissions.deny and also start the session with --dangerously-skip-permissions. Which of those two settings still affects an rm Claude proposes — and what would change if the rule had been in permissions.allow instead?

    Show answer

    The deny rule. Deny rules block in every mode, bypassPermissions included — that is the asymmetry at the centre of this mode, because "allow rules have no effect in bypassPermissions". 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.allow would 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 a permissions.allow rule or a PreToolUse hook returning "allow" approve an rm or rmdir that targets a critical path.

  2. A colleague runs unattended overnight jobs with --dangerously-skip-permissions and says it is fine because /sandbox is enabled. Name the categories of thing still running directly on their host, and what they would change to close that.

    Show answer

    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.failIfUnavailable too — by default a sandbox that cannot start produces a warning and commands run unsandboxed.

  3. 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 answer

    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.json at 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 as Bash(git push *) still force a prompt, and critical-path rm and rmdir and any command that cannot be sandboxed fall back to the regular permission flow.

  4. In bypassPermissions mode, an rm -rf against your home directory still stops and asks you. Does that mean the mode is safer than advertised? What is actually guaranteeing that prompt?

    Show answer

    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.allow rule or a PreToolUse hook that returns "allow" approve an rm or rmdir command 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 .git and .claude are 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.

Sources