Permission Modes & Safety

Default, acceptEdits, plan, auto, bypass

Intermediate 13 minBuilder
What you'll be able to do
  • Name all six permission modes and state precisely what each one auto-approves and what it still asks about
  • Switch modes with Shift+Tab, --permission-mode, and --dangerously-skip-permissions, and pick the right one for the task
  • Explain the auto-mode research preview: its model/provider requirements, its safety classifier, and its fallback triggers
  • Allow-list commands and manage rules with settings.json, --allowedTools, and the /permissions manager
  • Encode hard guarantees as deny rules instead of conversational instructions that compaction can drop
At a glance

Claude Code's permission system decides what runs without asking you. This lesson maps every mode — default, acceptEdits, plan, auto, dontAsk, and bypassPermissions — explains exactly what each one skips, walks the Shift+Tab cycle and allow-listing, and shows why any hard safety guarantee belongs in a deny rule, not in conversation.

  1. 1The mental model: a dial, not a switch
  2. 2The six permission modes
  3. 3Switching modes: Shift+Tab and the launch flags
  4. 4Auto mode: the safety-classified research preview
  5. 5Protected paths and allow-listing
  6. 6Hard guarantees go in deny rules, not conversation

The mental model: a dial, not a switch

Claude Code is an agent — it edits files, runs shell commands, and chains many steps without checking in after each one. The permission system is the steering wheel for how much of that it does on its own versus how often it stops to ask you. The key idea: it is a dial, not an on/off switch. You are choosing a point on a spectrum from "ask me before every action" to "skip every check," and the right point depends on the task, the blast radius, and where you are running.

There are two layers, and they work together:

  1. Permission modes — a session-wide posture (the dial). One mode is active at a time. It sets the default behavior: ask, auto-accept edits, read-only, and so on.
  2. Permission rules — explicit allow / ask / deny entries in your settings that override the mode for specific tools or commands. A deny rule wins no matter what the mode is (with one extreme exception we'll cover).

Think of the mode as the house thermostat and the rules as the per-room overrides. The mode says "generally, behave like this," and the rules carve out exceptions: "always let git status run" or "never touch the secrets file." The rest of this lesson walks every mode, then the rules — because the rules are where the guarantees live.

Key insight

Why this matters more for an agent than for autocomplete

Autocomplete can't do anything you don't accept keystroke by keystroke. An agent runs a loop — gather context, take action, verify — and a single prompt can trigger dozens of tool calls. Permission modes are how you decide how much of that loop runs unattended.

The six permission modes

Every Claude Code session is in exactly one of six modes. Here is the full table — what each auto-approves, and what it still stops to ask about:

ModeWhat it doesStill asks about
defaultAsks before edits and before running commands. The safe baseline.Everything not already allow-listed.
acceptEditsAuto-accepts file edits (create/edit/rename) so you stop clicking Accept on every diff.Most other commands — notably Bash.
planRead-only. Claude explores and proposes a plan but makes no edits and runs no mutating commands.Nothing is executed; you approve the plan to proceed.
autoResearch preview. A background safety classifier auto-approves low-risk actions and routes risky ones back to you.Scope escalation, unknown infra, hostile content (the classifier blocks these).
dontAskAuto-denies everything not in an allow rule. Built for CI/headless.Nothing — but explicit asks become denies, not prompts.
bypassPermissionsSkips all permission checks. Equivalent to --dangerously-skip-permissions.Effectively nothing — container/VM use only.

A few things that trip people up:

  • acceptEdits is about edits, not commands. It auto-accepts file writes, but it still asks before most Bash commands. If you want a command to run unprompted, allow-list it (next section). Don't reach for a more permissive mode when a targeted rule is what you need.
  • plan mode runs nothing. It is the cheapest way to let Claude investigate an unfamiliar codebase safely — it can read and search, but it cannot change or break anything. Press Ctrl+G to open the proposed plan in your editor and tweak it before approving.
  • dontAsk is not the same as bypass. dontAsk is the strict automation mode: anything not explicitly allowed is denied outright (no prompt). bypass is the permissive one: nothing is checked at all.
  • bypassPermissions is the loaded gun. It exists for sandboxed containers and disposable VMs where there is nothing valuable to wreck. Even here, deletions targeting / or your home directory ~ still prompt as a last-ditch guard.

Watch out

bypassPermissions is for throwaway environments only

--dangerously-skip-permissions (= bypassPermissions) disables every check. Use it only in an isolated container or VM, ideally one without network access. The flag name is a warning, not decoration: in a real working directory it lets Claude run anything, edit anything, and delete almost anything without asking.

Switching modes: Shift+Tab and the launch flags

There are two ways to set the mode: cycle it live during a session, or pin it at launch.

Live, mid-session — Shift+Tab. Pressing Shift+Tab cycles through the modes. The core cycle is:

text
default  →  acceptEdits  →  plan  →  (back to default)

auto and bypassPermissions join the cycle only when they are enabled for your setup (auto needs the right model/provider; bypass needs to be turned on). On some terminal configs the binding is Alt+M instead. The current mode is shown in the status line, so you always know which posture you're in. To leave plan mode, press Shift+Tab again to continue cycling.

At launch — --permission-mode. Pin a mode when you start a session:

bash
claude --permission-mode plan          # start read-only for safe exploration
claude --permission-mode acceptEdits   # start auto-accepting edits
claude --permission-mode dontAsk -p "run the test suite"   # strict headless

The accepted values are exactly: default, acceptEdits, plan, auto, dontAsk, bypassPermissions.

The dangerous shortcut. --dangerously-skip-permissions is a dedicated flag that is identical to --permission-mode bypassPermissions — same effect, scarier name to make you think twice.

You can also enter plan mode with /plan [description] as a slash command, which is handy when you're already mid-conversation and want to drop into read-only planning for the next chunk of work.

Tip

The everyday rhythm

A common loop: start in plan to explore and let Claude draft an approach, approve the plan, then Shift+Tab to acceptEdits so you're not clicking Accept on every file — while Bash commands still prompt. This is the Explore → Plan → Implement workflow expressed through permission modes.

Auto mode: the safety-classified research preview

auto is the newest and most nuanced mode — a research preview, not a finished feature. Instead of a fixed rule ("accept all edits," "ask for all commands"), a background safety classifier judges each action in context and decides whether to auto-approve it or route it back to you for a prompt. The goal is fewer interruptions on the obviously-safe stuff while still stopping the risky stuff.

What the classifier blocks (routes back to you):

  • Scope escalation — actions that reach beyond what the task plausibly needs.
  • Unknown infrastructure — touching systems or services it can't identify as safe.
  • Hostile content — inputs that look like prompt injection or otherwise adversarial.

What the classifier can and can't see: it reads your messages, the tool calls Claude proposes, and your CLAUDE.md — but it strips tool results before judging. (That's deliberate: tool output is exactly where injected, hostile instructions tend to hide.) If you ever see "cannot determine safety," that's a transient classifier hiccup, not a verdict.

The fallback rule. Auto mode is not a one-way door. It automatically falls back to normal prompting after 3 consecutive denials or 20 total denials in a session — a signal that the classifier keeps disagreeing with the direction of work, so it hands control back to you rather than fighting you.

Requirements (this is strict):

Where you're runningWhat auto mode needs
Anthropic API (direct)Opus 4.6+ or Sonnet 4.6
Cloud providers (Bedrock / Vertex / Foundry)Opus 4.7–4.8 onlySonnet 4.6 is not supported hereand the env var CLAUDE_CODE_ENABLE_AUTO_MODE=1
bash
# Cloud-provider example: opt in explicitly
export CLAUDE_CODE_ENABLE_AUTO_MODE=1
claude --model opus --permission-mode auto

Watch the provider gap. Sonnet 4.6 unlocks auto mode on the direct Anthropic API only. On Bedrock, Vertex AI, and Foundry the only supported models are Opus 4.7 and 4.8 — Sonnet 4.6 will not enable auto there no matter the env var. Plan deployments accordingly. (Model support shifts over time, so confirm against the official permission-modes docs.)

If your model or provider doesn't meet the bar, auto simply won't be available in the Shift+Tab cycle.

Note

Research preview = treat it as assistive, not authoritative

The classifier is a helpful filter, not a hard boundary. It reduces interruptions and catches obvious overreach, but it is probabilistic. For anything you need to be guaranteed never happens, don't rely on auto's judgment — write a deny rule (covered next).

Protected paths and allow-listing

Two mechanisms sit underneath the modes and shape what actually gets auto-approved.

Protected paths — never auto-approved (except bypass). Some files are sensitive enough that Claude Code will always prompt before writing to them, regardless of mode — the only exception being bypassPermissions, which skips everything (under auto, writes here are routed to the classifier rather than auto-approved; under dontAsk they're denied). The protected set is deliberately broad — it covers VCS internals, your Claude Code config, shell startup files, and a wide range of package-manager and tooling configs that can run code on your behalf:

CategoryExamplesWhy it's protected
VCS internals.git, .config/git, .gitconfig, .gitmodulesCorrupting these can destroy history; configs can define aliases/hooks that run commands
Claude Code config.claude (see carve-out below), .mcp.json, .claude.jsonYour modes, rules, and MCP/agent configuration
Shell startup files.bashrc, .bash_profile, .zshrc, .zprofile, .profile, .envrc, …Run on every shell launch
Editor / dev-env.vscode, .idea, .devcontainer, .devcontainer.jsonCan auto-run tasks or define container behavior
Package / tooling configs.npmrc, .yarnrc, .yarn, .cargo, .mvn, bunfig.toml, .pnpmfile.cjs, .husky, .pre-commit-config.yaml, lefthook.yml, bazel/gradle/maven wrapper files, …Can carry registry tokens or run install/commit hooks

This list is long and changes over time — treat the table as representative, not exhaustive, and check the official permission-modes docs for the complete, current protected set before assuming a given config file is guarded.

Note the carve-out: .claude itself is protected, but a few subdirectories where Claude routinely creates content — .claude/commands, .claude/agents, .claude/skills, and .claude/worktrees — are not, so Claude can manage things like .claude/skills/ without a prompt while your top-level config stays guarded.

Allow-listing — make safe commands stop prompting. The complement to protection is permission: tell Claude Code which tools and commands it may run without asking. You can do this two ways.

At launch with --allowedTools (permission-rule syntax):

bash
claude --allowedTools "Bash(git log *)" "Bash(git diff *)" "Read"

Or persistently in .claude/settings.json under a permissions block with allow / ask / deny lists:

json
{
  "permissions": {
    "allow": ["Bash(git status)", "Bash(npm test)", "Read"],
    "ask":   ["Bash(git push *)"],
    "deny":  ["Bash(rm -rf *)", "Read(./.env)"]
  }
}

Manage all of this interactively with /permissions (alias /allowed-tools) — it shows recent denials and lets you turn a denial into a rule (press r to retry). And to build a sensible read-only allow-list automatically, run /fewer-permission-prompts: it scans your transcripts for the safe, repetitive commands you keep approving and proposes an allow-list for settings.json.

Tip

Allow-list the boring stuff, not the dangerous stuff

The point of allow-listing is to remove friction on read-only and idempotent commands — git status, git log, git diff, npm test, ls. Resist the urge to allow-list broad wildcards like Bash(git *) that would sweep in git push or git reset --hard. Narrow rules keep the safety net intact.

Hard guarantees go in deny rules, not conversation

This is the single most important habit in this lesson, so it gets its own section.

It is tempting to enforce a boundary by telling Claude: "Never push to main," "Don't run migrations," "Leave the production config alone." In a long session this feels sufficient — Claude agreed, after all. But conversational instructions live in the context window, and the context window is not permanent. When the session compacts (summarizes older turns to free space), the precise wording of a one-off boundary can get dropped or softened. Hours later, Claude no longer reliably "remembers" the rule you stated once at the top.

A deny rule has none of that fragility. It lives in settings.json, it is evaluated on every single tool call, and it wins over the active mode — even acceptEdits, even auto, even dontAsk. It does not depend on Claude's cooperation or memory at all; it's a wall, not a request.

json
{
  "permissions": {
    "deny": [
      "Bash(git push *)",
      "Bash(rm -rf *)",
      "Read(./.env)",
      "Read(./secrets/**)"
    ]
  }
}

The rule of thumb:

  • Advice / preferences ("prefer 2-space indent," "explain before large refactors") → fine in conversation or CLAUDE.md.
  • Hard guarantees ("never push," "never read this secret," "never delete that path") → deny rules.

If the cost of the rule being forgotten is real, the rule does not belong in a sentence. It belongs in deny.

Watch out

Compaction silently drops boundaries

The failure mode is quiet and dangerous: nothing errors when a boundary is dropped during compaction — Claude just proceeds as if the rule never existed. By the time you notice, the irreversible action may already be done. A deny rule is re-checked on every tool call and can never be compacted away.

Try it: Build a safe permission profile for a real repo

Pick a project you actually work in and harden it through the permission system — no conversation-only rules allowed.

  1. Explore safely. Launch with claude --permission-mode plan and ask Claude to summarize the repo's build/test commands and risky scripts. Confirm it makes zero edits. Press Ctrl+G on its plan to see in-editor plan editing.
  2. Cycle the dial. Use Shift+Tab to move default → acceptEdits → plan and watch the status line change. Make a trivial edit in acceptEdits and confirm it applies without a prompt; then ask it to run a Bash command and confirm it still asks.
  3. Allow-list the boring stuff. Run /fewer-permission-prompts, review what it proposes, and commit a narrow allow list to .claude/settings.json (e.g. Bash(git status), Bash(git diff *), Bash(npm test), Read). Open /permissions and verify the rules are active.
  4. Add the guarantees. Add a deny block for the things that must never happen in this repo — for example Bash(git push *), Bash(rm -rf *), and Read(./.env). Then test it: in default mode, ask Claude to push, and confirm the deny rule blocks it regardless of mode.
  5. Reflect. Write one short paragraph: which boundary did you (wrongly) feel tempted to handle by just telling Claude, and why does it belong in deny instead? If you have access to it, repeat step 1 under --permission-mode auto and note which actions the classifier auto-approved versus bounced back.

Key takeaways

  1. 1There are six modes — default (ask), acceptEdits (auto-accept edits only), plan (read-only), auto (classifier-judged preview), dontAsk (deny-all-not-allowed), and bypassPermissions (skip everything, container-only).
  2. 2Shift+Tab cycles modes live (default → acceptEdits → plan; auto/bypass when enabled); --permission-mode pins one at launch; --dangerously-skip-permissions equals bypassPermissions.
  3. 3Auto mode needs Opus 4.6+ or Sonnet 4.6 on the direct API, but only Opus 4.7–4.8 on cloud providers (Bedrock/Vertex/Foundry) plus CLAUDE_CODE_ENABLE_AUTO_MODE=1 — Sonnet 4.6 is not supported on cloud; its classifier blocks scope escalation, unknown infra, and hostile content, and falls back after 3 consecutive or 20 total denials.
  4. 4A broad set of protected paths (.git, .config/git, .gitconfig, .claude except a few subdirs, shell rc files, .npmrc and other package/tooling configs, .mcp.json, and more) is never auto-approved except under bypassPermissions — check the official docs for the full current list.
  5. 5Allow-list safe commands via settings.json or --allowedTools and manage them with /permissions and /fewer-permission-prompts — but keep rules narrow.
  6. 6Put hard guarantees in deny rules, never in conversation: deny rules survive compaction and override every mode, while spoken boundaries can be summarized away.

Quiz

Lock in what you learned

Check your understanding

0 / 4 answered

1.You're in acceptEdits mode and ask Claude to run the database migration script. What happens?

2.Which statement about auto mode is correct?

3.You tell Claude at the start of a long session, 'Never push to main.' Two hours and a /compact later, why might it push anyway, and what is the robust fix?

4.Which file would Claude Code still prompt before modifying even in acceptEdits or auto mode (but not under bypassPermissions)?

Go deeper

Hand-picked sources to keep learning