Deus Witness

Quickstart | Concepts | API | Verify | Coverage | Teams | Anchors | Recovery | Human approval in CI | Verifiable audit log | Unattended runs | Approval when unattended | Hooks vs deny | Skip permissions | PreToolUse hook | Deny not working | Headless permissions | MCP human-in-the-loop

Claude Code --dangerously-skip-permissions: what it skips, and a safer way to run unattended

--dangerously-skip-permissions starts Claude Code in bypassPermissions mode: tool calls run without a permission prompt, including writes to .git and .claude. It does not turn off everything: deny rules, ask rules, a PreToolUse hook that answers deny and the guard on critical-path rm still apply. It gives no protection against prompt injection, so the official advice is to use it only in a container or VM. For most unattended jobs, --permission-mode dontAsk with an allow list, plus the sandbox, does the job with far less risk. Checked against Claude Code 2.1.312 and the official Permission modes, Permissions, Hooks guide and Sandboxing pages.

What the flag turns off

The CLI reference: the flag is "Equivalent to --permission-mode bypassPermissions". The Permission modes page: this mode "disables permission prompts and safety checks so tool calls execute immediately, including writes to protected paths". In practice:

What still blocks

What it does not do: think for you. The page's own warning: "bypassPermissions offers no protection against prompt injection or unintended actions." A README, an issue or a web page that tells the agent to run curl ... | sh gets the same treatment as your own request.

Why people use it, and what to use instead

The usual reasons are a cron job, a GitHub Actions step, or a long task where you do not want to answer 200 prompts. Each has a narrower tool.

CI and cron: deny what is not on a list. dontAsk "denies every call that would otherwise prompt"; listed tools still run.

claude -p "Run the tests and fix the failing one" \
  --permission-mode dontAsk \
  --allowedTools "Read,Edit,Bash(npm test),Bash(git diff *)" \
  --permission-prompts none \
  --output-format json

--permission-prompts none tells Claude Code nobody is there to answer, so tools that need a person are removed. Refused calls are listed under permission_denials in the JSON; count them and fail the job if needed. More on -p runs: Claude Code headless mode: permissions, approvals and CI.

Long interactive tasks: auto mode. The official alternative for "far fewer permission prompts": claude --permission-mode auto. A classifier reviews each action instead of you. It needs a supported model, and your organization can turn it off.

A real boundary: the sandbox. Rules read the text of a command; the sandbox limits the process. Turn it on and close the retry:

{"sandbox": {"enabled": true, "allowUnsandboxedCommands": false,
  "failIfUnavailable": true, "network": {"strictAllowlist": true}}}

It covers shell commands. The built-in Read, Edit and Write tools follow permission rules instead, so keep deny rules for secrets and for .claude/**.

If you really need bypass mode, do what the docs say: a container or VM, a non-root user, no secrets you would not hand to a stranger, and an egress allowlist on the runner. Add deny rules anyway, because they still apply:

{"permissions": {"deny": ["Bash(curl *)", "Bash(ssh *)", "Read(.env)", "Read(.env.*)",
  "Edit(.claude/**)", "Edit(.mcp.json)"]}}

A Bash rule matches the command text and "isn't a security boundary around the program" (Permissions page): sh -c 'curl ...' slips past. Use them as a fence, not a wall.

Locking the flag out for a team. Set permissions.disableBypassPermissionsMode to "disable" in managed settings. A checked-in .claude/settings.json cannot start a session in bypass mode either: defaultMode: "bypassPermissions" there "doesn't take effect".

Side by side

--dangerously-skip-permissionsdontAsk + --allowedToolsPreToolUse hookSandbox
Promptsnonenone, unlisted calls deniedcan ask, allow or denyunchanged
Deny rulesstill applyapplyapplyapply
Decides bynothingname and pattern of the callany code you writewhat the process touches
Protects against prompt injectionnopartly: only listed tools runas good as your logiclimits the damage
Breaks howruns everythingblocks too muchfails open unless made fail-closedblocks tools that need the network

Where a person is still needed

Some actions should not run without a person at the moment of the call, in any mode: push or merge, deploy, send mail, delete outside the checkout, spend money. In dontAsk they are simply refused, and you review the job's output afterwards. If the job must do them, the call has to wait until someone approves it from somewhere other than the terminal. Claude Code's built-in tools do not do that on their own; a PreToolUse hook can, within its timeout. How to write one, and how to stop it failing open: Claude Code PreToolUse hook: examples, exit code 2 and the "hook error".

Deus Witness is one such hook. deus-witness-hook sends each matched call to the witness, compares it with a mandate you signed with a passkey, and holds a call the mandate does not cover until you approve it on your phone. The hook checks the signed receipt before it prints allow; if you do not answer in time, or the service is unreachable, the call is denied. Because hooks fire in every permission mode, this holds in bypass mode too, but the hook decides only Claude Code's tool calls: it does not see what a script does once it runs, so keep the sandbox or container under it. Setup for unattended runs: Safe way to run Claude Code unattended and Require approval before Claude Code runs a command unattended.