Permissions and unattended runs

Verified against Claude Code 2.1.311, Codex CLI 0.164.0 · Updated

Unattended means nobody is there to click Allow. If the agent can prompt, the job hangs. If you skip every prompt, the agent can push to main, exfiltrate secrets, or wipe the runner. I want the middle: write the repo, run the tests, stop.

The pattern I use is restricted autonomy. The agent edits files. A deterministic step commits, opens the PR, and talks to the network. Permission denylists sit in front of the agent. Cost caps sit next to it.

In Claude Code

Permission rules are deny, then ask, then allow. A matching deny wins even if a narrower allow exists. Modes I opened on the permissions page:

  • default / manual: prompt on first use of a tool
  • acceptEdits: write files and common filesystem commands without a prompt
  • plan: read files and run read-only shell commands, without editing source files. When auto mode is available, classifier-approved commands also run
  • auto: a classifier reviews actions
  • dontAsk: deny every call that would otherwise prompt, though a mod you installed can still approve some of those calls. File reads in your working directories and other actions that need no approval still run, and so do tools you pre-allow. AskUserQuestion, MCP tools marked requiresUserInteraction, network-path reads, and connector tools your organization set to ask are denied even when an allow rule matches
  • bypassPermissions: skip prompts, including writes to .git and .claude. Deny rules still block. These are not auto-approved: an explicit ask rule, a connector tool your organization set to ask, AskUserQuestion, MCP tools marked requiresUserInteraction, rm or rmdir of a critical path, and the cross-session messaging safeguards. Use it in an isolated container or VM

For -p (non-interactive) on v2.1.302 or later, the built-in starting mode is Manual. You must pass the mode you want. A call that needs approval is denied unless something approves it, such as an allow rule, a hook, or --permission-prompt-tool.

claude --bare -p "Run the test suite and fix failures" \
  --permission-mode dontAsk \
  --permission-prompts none \
  --allowedTools "Read,Edit,Bash(npm test *),Bash(npm run build *)" \
  --max-budget-usd 5

--bare skips auto-discovery of hooks, skills, custom commands, subagents, plugins, MCP servers, auto memory, and CLAUDE.md. Use it in CI so a teammate’s ~/.claude does not change the job. --permission-prompts none (v2.1.259+) denies anything that would wait on a host, unless a PermissionRequest hook allows it. --max-budget-usd is compared to the same client-side session cost figure /usage shows. That figure can differ from the bill, and spend can pass the cap.

Auth in bare mode does not read OAuth credentials or the system keychain. Set ANTHROPIC_API_KEY, or pass an apiKeyHelper in --settings.

Do not use bypassPermissions on a shared GitHub runner. dontAsk plus an allowlist is the minimum I would check in.

In Codex

The CLI reference names codex exec (alias codex e) for scripted runs. codex exec starts read-only. Sandbox values: read-only, workspace-write, danger-full-access. Approval values: on-request and never. approval_policy = "untrusted" is retired, and a config that still sets it can stop Codex from starting. --full-auto is no longer supported. Prefer --sandbox workspace-write.

codex exec --sandbox workspace-write --ask-for-approval never --ignore-user-config \
  "Run the test suite and fix failures. Do not commit or push."

--ignore-user-config skips $CODEX_HOME/config.toml. Auth still uses CODEX_HOME. --ignore-rules skips user and project execpolicy .rules files. Do not pass --dangerously-bypass-approvals-and-sandbox on a shared runner.

Permission profiles (:read-only, :workspace, :danger-full-access) do not compose with the older sandbox settings. If sandbox_mode is set in a loaded config or the selected config profile, or you pass --sandbox, Codex uses the sandbox and ignores default_permissions. Managed allowed_permission_profiles is the exception: it makes Codex use profiles.

In Cursor

Headless CLI:

export CURSOR_API_KEY=...
agent -p --force "Apply the lint fixes and stop"

-p / --print is non-interactive. Without --force (alias --yolo) the agent proposes edits and does not apply them. Output formats: text, json, stream-json.

Cloud agents load project hooks from .cursor/hooks.json. They do not load ~/.cursor/hooks.json. User-level hooks stay on your laptop. Put the build gate in the repo or it will not run in the cloud.

Minimum safe CI config

  1. Isolated runner. One job, one checkout, no shared workspace credentials beyond GITHUB_TOKEN.
  2. Allowlist the tools the agent needs. Deny everything else.
  3. Agent writes files. A later step with a fixed script runs git commit, gh pr create, and any network install.
  4. A cost cap (--max-budget-usd for Claude Code). Fail the job when it trips.
  5. A verify command after the agent exits. The hook or the workflow, not the model, decides if the PR opens.

If you cannot write the allowlist, you are not ready for unattended. Stay interactive.

Sources