Skip to content
DevPedia

Context and controlGuide 6 of 12

Permissions in Claude Code

How to define which actions the agent can run, which ones need confirmation, and which ones must stay blocked.

Updated 6 min read

The question that defines this article is simple: how much do you trust what Claude is about to do without asking? Claude Code splits that answer into two layers that are worth understanding separately: the modes (default behavior) and the rules (specific exceptions).

The 6 permission modes

ModeWhat it allowsWhen to use it
default, shown as “Manual” since v2.1.200Read-only without asking; any edit or command requires approvalSensitive work, unfamiliar repos
acceptEditsReads and edits files without interruptionsYou’re iterating on code you actively review yourself afterwards
planRead-only, neither edits nor runs anythingExploring a new codebase or planning a refactor before touching anything
autoEvery action, with an automatic classifier checking in the backgroundLong tasks where you want to reduce the fatigue of approving everything
dontAskOnly runs explicitly pre-approved toolsCI/CD pipelines and automated scripts
bypassPermissionsEverything, with no checksOnly in isolated containers or VMs — never on your main machine

How to change it:

# During a session: Shift+Tab cycles through the available modes.
# bypassPermissions only enters the cycle if you started with
# --dangerously-skip-permissions.

claude --permission-mode plan
claude --permission-mode acceptEdits
claude --permission-mode auto
claude --permission-mode dontAsk               # meant for CI
claude --permission-mode bypassPermissions     # only in isolated environments

Two values aren’t accepted from .claude/settings.json or settings.local.json: auto is ignored, and bypassPermissions makes the session start in Manual. That’s deliberate: they’re decisions for whoever launches the session, not for a file committed to the repo.

Custom rules: deny → ask → allow

On top of the modes, there’s a system of granular per-tool rules. They’re evaluated in this order, and the first matching rule wins:

01. Does it match a deny rule?

Yes → Blocked. Full stop.No → keep going

02. Does it match an ask rule?

Yes → Asks for confirmationNo → keep going

03. Does it match an allow rule?

Yes → Runs without askingNo → keep going
If no rule matches: the active mode's default behavior

deny always wins: bypassPermissions can’t override a deny from managed configuration, and acceptEdits can’t lift a denied Edit(...).

Syntax: Tool or Tool(specifier).

{
  "permissions": {
    "allow": [
      "Bash(npm test*)",       // any command starting with "npm test"
      "Bash(docker compose *)",
      "Bash(git log*)"
    ],
    "deny": [
      "Read(./.env)",
      "Read(~/.ssh/*)",
      "WebFetch(domain:vault.mycompany.internal)"
    ]
  }
}

A few details worth keeping in mind:

  • The space before * matters: a rule Bash(ls *) requires a space or end of string after ls (it doesn’t match lsof); Bash(ls*) matches both. Bash(ls:*) is the modern syntax equivalent to Bash(ls *).
  • Shell operators are evaluated separately: a Bash(safe-command *) rule doesn’t enable safe-command && dangerous-command, because Claude Code understands &&, ||, and ; and evaluates each subcommand.
  • Read/Edit use .gitignore-style syntax: //path is absolute from the filesystem root, ~/path from your home, /path is relative to the project root (careful, not the filesystem root — /Users/alice/file is relative to the project, not a real absolute path), and path or ./path is relative to the current directory.
  • An important limit: deny rules on Read/Edit block Claude’s internal tools, but they don’t block what you do through Bash (a cat .env in Bash isn’t stopped by a Read(./.env) rule). For real OS-level blocking you need a sandbox, which combines the deny rules with its own allowlist of network domains.

What each mode adds on top of the rules

The modes are the master switch; the rules are the fine filter. An example of how they interact:

  • In plan, even with Edit(/src/**) in allow, the mode doesn’t let it edit anything — it’s more restrictive than permissive rules.
  • In auto, entering the mode automatically discards dangerous allow rules like Bash(*) or Agent(*), because they’d give arbitrary execution before the classifier had evaluated them. Specific rules (Bash(npm test)) are kept. On exit, they’re restored.
  • In dontAsk, even ask rules turn into denial — anything not explicitly in allow is rejected. Ideal for CI.

This is one of the corners of Claude Code that evolves fastest. Before setting a permission policy for a whole team, it’s worth confirming the current behavior in the official documentation.

Related documentation: complete rule syntax and settings.json reference, where all these rules live on disk.

Share this guide

Search by concept, pattern or practice.