$ backgroundclaude
blog · 2026-04-10 · 9 min read

Claude Code permission modes: which one should your CI use?

Most posts about Claude Code CI setup reach for --allowedTools, pile on a list of permissions, and call it done. It works. It also misses a whole axis of control: the permission mode sets the baseline behavior for a session, and the right mode shrinks your --allowedTools list to almost nothing. The wrong mode makes it a Swiss cheese of allowlists you forget to audit.

There are six modes. This is the field guide, ordered from strictest to loosest.

The six modes, at a glance

ModeRuns without askingBest for
defaultReads onlyGetting started, sensitive work
dontAskOnly pre-approved tools via permissions.allowLocked-down CI and scripts
planReads only; proposes without editingExploring a codebase before changing it
acceptEditsReads, file edits, common filesystem cmds (mkdir, touch, rm, mv, cp, sed)Iterating on code you're reviewing
autoEverything, with background classifier safety checksLong tasks, reducing prompt fatigue
bypassPermissionsEverything except protected pathsIsolated containers and VMs only

Set a mode at startup with --permission-mode:

claude -p "..." --permission-mode dontAsk

Or pin it in settings.json:

{
  "permissions": {
    "defaultMode": "acceptEdits"
  }
}

The only one you should consider for unattended CI: dontAsk

From the docs, verbatim:

dontAsk mode auto-denies every tool that is not explicitly allowed. Only actions matching your permissions.allow rules can execute; explicit ask rules are also denied rather than prompting. This makes the mode fully non-interactive for CI pipelines or restricted environments where you pre-define exactly what Claude may do.

That is exactly the property you want in CI. Every other mode is either interactive (default, plan) or loose (acceptEdits, auto, bypassPermissions). dontAskis the only one that combines “no human in the loop” with “no silent escalation.”

The tradeoff: you must have a permissions.allow list that actually covers what the job needs, or the run aborts on the first unapproved tool. Which is a feature. A dontAsk run that fails loudly is better than an acceptEdits run that quietly writes a file nobody expected.

claude --bare -p "Run the test suite and fix any failures" \
  --permission-mode dontAsk \
  --settings '{
    "permissions": {
      "allow": [
        "Read",
        "Edit",
        "Grep",
        "Bash(npm test)",
        "Bash(git diff *)",
        "Bash(git add *)"
      ]
    }
  }'

Paired with --bare mode, dontAsk gives you the two properties that make a production Claude Code run safe to run unattended: reproducibility (bare) and bounded authority (dontAsk).

acceptEdits: looser, for interactive work

acceptEdits auto-approves file writes inside your working directory plus a specific list of filesystem commands: mkdir, touch, rm, rmdir, mv, cp, sed. Also the same commands wrapped in timeout, nice, nohup, or prefixed with safe env vars like LANG=C.

Everything else — arbitrary shell, network requests, writes outside your working directory — still prompts. That makes acceptEditsa decent choice for a human pairing with Claude in a long session where you'll review the diff at the end anyway. It is not a CI mode. If nothing is watching for the prompts, the run stalls.

plan: Claude as consultant, not contractor

Plan mode tells Claude to research and propose changes without making them. Reads files, runs shell commands to explore, writes a plan, does not edit your source. Permission prompts still apply the same as default mode, so humans still see the interactive permission requests.

Not a CI mode either, but a fantastic pattern for a pre-commit hook or a PR bot that comments on a diff: run Claude in plan mode with read-only tools, capture the plan as output, post it to the PR for a human to approve explicitly before a second non-plan run does the work.

auto: the research preview

Auto mode is the interesting one. Claude executes without permission prompts, but a separate classifier model reviews each action before it runs, blocking anything that escalates beyond your request, targets unrecognized infrastructure, or looks like it was driven by hostile content Claude read somewhere.

The classifier blocks a specific list of dangerous patterns by default: curl | bash, sending sensitive data to external endpoints, production deploys and migrations, mass deletion on cloud storage, granting IAM or repo permissions, modifying shared infrastructure, irreversibly destroying files that existed before the session, force push, and pushing directly to main.

Allowed by default:

The catches:

Auto is a serious research preview, and the classifier architecture is impressive, but the docs are clear it's not a replacement for review on sensitive operations. For a production background agent, dontAsk + a narrow allowlist is still the safer bet.

bypassPermissions: the footgun

bypassPermissions disables all permission prompts and safety checks. Only writes to protected paths (.git, .claude, shell rc files, etc.) still prompt. The docs are unambiguous:

Only use this mode in isolated environments like containers, VMs, or devcontainers without internet access, where Claude Code cannot damage your host system.

--dangerously-skip-permissions is the equivalent flag. The word dangerouslyin the name is not marketing. This is not a CI mode, unless your CI job runs in an ephemeral container with no network, no secrets, and no side effects you'd regret.

Protected paths, in every mode

Regardless of mode (except bypassPermissions, where they still prompt), writes to a fixed set of paths are never auto-approved. These guard your repo state and Claude's own configuration from accidental corruption.

Directories:

Files:

In dontAsk these writes are denied outright, which means a background agent trying to modify any of them is telling you something important about its prompt.

The rule I use

For any unattended Claude Code run:

  1. --bare for reproducibility (no hidden state)
  2. --permission-mode dontAsk for bounded authority
  3. A narrow permissions.allow list (or an equivalent --allowedTools)
  4. --max-turns and --max-budget-usd as the safety net behind the safety net
  5. --output-format stream-json + --verbose + --include-partial-messagesso you can watch the run from wherever you're watching it

Anything less strict is a choice worth justifying in a comment next to the cron entry.

Where this leaves you

You now know the permission axis. If you're running a single scheduled job on your own machine, that's all you need — pair these flags with cronand you're done.

If you're routing Claude Code runs from an issue tracker, you also need webhook signature verification, per-team budgets, mid-run approvals, audit trails, worktree isolation, and streamed output back to the issue. That's where background agents start, and most of the hard parts aren't about permissions — they're about all the plumbing nobody enjoys writing.

bounded authority, scaled

Cyrus ships the plumbing. You scope the permissions.

Cyrus runs Claude Code in isolated git worktrees per Linear issue, streams activity back, and supports mid-run approvals and dropdown interactions so “rich interactions” actually means “ask the human before doing the scary thing.” BYOK across Claude, Codex, Cursor, and Gemini. Community self-hosted is free forever.

Try Cyrus free →