The Best Ways to Control What Claude Code Can Do, Ranked (2026)
Last updated: July 27, 2026.
In July 2025, Replit’s agent deleted a production database during an explicit code freeze. The same month, Gemini CLI destroyed a user’s files during a botched folder move and reported “I have failed you completely and catastrophically”. In December, Claude Code ran an rm -rf with a stray ~/ and wiped a developer’s home directory. Different agents, same shape: the constraint lived somewhere the action didn’t pass through.
So the question “how do I control what Claude Code can do” has a real option landscape now. This is the ranked list, judged on four criteria:
- Isolation strength — if the agent goes wrong, what’s the blast radius?
- Coverage breadth — which actions does the control actually see? Shell only, or edits, network, and MCP calls too?
- Audit — afterwards, can you reconstruct what the agent did and why it was allowed?
- Setup friction — how far from
installto protected?
One thing upfront: most of these aren’t substitutes. A container bounds where the agent can act; a policy layer decides which actions run inside that boundary. The ranking is “where to start and what to add,” not “pick one.”
The comparison
| Option | Isolation | Coverage | Audit | Setup | Best for |
|---|---|---|---|---|---|
1. Built-in permissions + settings.json |
None | All native tools | Session transcript only | None — built in | The baseline everyone should configure |
| 2. Anthropic sandbox runtime | OS-level (filesystem + network) | Everything inside the boundary | No | Low | Fewer prompts without losing the boundary |
| 3. ACP (local policy + audit layer) | None | Every tool call — shell, edits, MCP — across Claude Code, Cursor, Codex | Per-call log, decisions included | One command, no account | Deciding per action, and proving what ran |
| 4. Devcontainer / Docker | Full container | Everything contained, nothing decided | No | Medium | Untrusted input; worst-case containment |
| 5. dcg (destructive-command-guard) | None | Shell commands only | No | One curl | A fast tripwire on destructive shell |
| 6. Cloud sandboxes (E2B, Modal, Daytona) | Full microVM, off your machine | Whole environment | Varies | High | Autonomous fleets, not laptops |
1. Built-in permission prompts + settings.json — configure this first
Best for: everyone, as the baseline. It ships with Claude Code, costs nothing, and most people never configure it past clicking “allow.”
Claude Code asks before risky actions and reads allow/deny rules from settings.json:
{
"permissions": {
"deny": ["Bash(rm:*)", "Bash(git push:*)", "WebFetch"]
}
}
We’ve published a full posture, argued tool by tool. Do this regardless of what else you add.
The honest limits. Deny rules are string patterns, and command strings have many spellings — the bypasses are documented. The rules live per machine, so they drift across a team. And the flag people reach for when they want automation, --dangerously-skip-permissions, disables the permission system entirely, hooks included — the enforcement disappears at exactly the moment you stop watching. That December home-directory wipe happened with the flag on.
2. Anthropic’s sandbox runtime — the best native upgrade
Best for: cutting prompt fatigue without giving up a boundary.
Anthropic’s own telemetry showed users approving ~93% of permission prompts — approval fatigue makes the prompts themselves stop working. Their answer is OS-level sandboxing (Seatbelt on macOS, bubblewrap on Linux): reads allowed, writes confined to the workspace, network denied by default. Inside the boundary the agent runs without interruption; Anthropic reports an 84% reduction in permission prompts in internal use. The runtime is open source, so the boundary is auditable. In Claude Code it’s the /sandbox command.
The honest limits. The sandbox answers “where can the agent act,” not “should this action run.” A destructive command inside the workspace — the DROP TABLE you have credentials for, the force-push, the Replit-shaped write to prod through an allowed network host — is within bounds and runs. Unsandboxable commands fall back to the regular prompt flow, which is an escape hatch. And it leaves no record of what the agent did, only of where it was allowed to do it.
3. ACP — decide per action, and keep the record
Best for: the question the first two don’t answer — “should this specific call run, and can I prove what ran?”
Between per-call prompts (you decide everything, live) and full isolation (the boundary decides, coarsely) there’s a slot for deciding per action, by rule, with a record. That slot is mostly empty, and it’s the one ACP sits in. One command, free, no account, nothing leaves your machine:
curl -sf https://agenticcontrolplane.com/install.sh | bash -s -- --local
Every tool call — shell, file edits, MCP calls, not just Bash — is checked against your policy in ~/.acp/policy.json before it runs, and every call and decision lands in ~/.acp/audit.jsonl. The same policy covers Claude Code, Cursor, and Codex’s shell calls, so the rules don’t fork per machine or per agent. When someone asks what the agent actually did last Tuesday, the answer is a log written by the layer the calls passed through — not the agent’s own account of itself. In the Replit incident the agent misreported what it had done; a self-authored transcript can’t settle that. A call-path log can.
The honest limits. ACP adds no isolation — a bug in an allowed action still executes with your credentials. If the agent handles untrusted input, keep the container (option 4); the container bounds the process, the policy layer decides the actions inside it. They’re complementary, not substitutes.
4. Devcontainers / Docker — the right answer for untrusted input
Best for: untrusted repos, web content in the loop, worst-case containment. If you need one answer for “the agent might be adversarially steered,” this is it — nothing else on this list matches full isolation.
Run Claude Code inside a container and the worst case is bounded by the container: the home-directory wipe becomes a wiped container filesystem, rebuilt in a minute. Anthropic maintains a reference devcontainer with a network firewall; any Docker image with your toolchain works.
The honest limits. Setup friction is real — your credentials, dotfiles, and services have to come along deliberately, which is the point and also the tax. And containment isn’t decision-making: inside the container, with the credentials you mounted, every action is still unexamined. The Gemini CLI incident ran entirely inside the user’s working directory — a container drawn around that directory contains nothing.
5. dcg — the best pure shell tripwire
Best for: a dead-simple destructive-command blocker, installed in a minute, across every agent you run.
dcg (destructive-command-guard) is a Rust hook, a few thousand GitHub stars, one curl install from the README, that pattern-matches dangerous git and shell commands — rm -rf outside temp paths, git reset --hard, destructive migrations — and blocks them before execution with an explanation. Its breadth is the standout: a dozen-plus agents (Claude Code, Codex CLI, Gemini CLI, Copilot, Cursor, more) from one tool. If pure shell-blocklist simplicity is what you want, dcg is the honest pick over building the same thing yourself.
The honest limits. It sees shell commands only — file-edit tools, MCP calls, and network actions never pass through it, and the Gemini incident’s move disaster wasn’t on anyone’s destructive-pattern list. It’s a curated blocklist, so it’s as good as its patterns, and it hard-blocks some legitimate recovery paths (git checkout -- .). And it fails open: if the hook is missing, misconfigured, or errors, the command runs. A tripwire, not a boundary — worth having, not worth trusting alone.
6. Cloud sandboxes — for fleets, not laptops
Best for: autonomous agents at scale, where “my machine” isn’t the blast radius anymore.
E2B, Modal, and Daytona run each agent in its own disposable microVM. For a fleet of unattended agents this is the right substrate: per-run isolation, snapshots, no shared laptop state to destroy. It ranks last here only because the question was controlling your Claude Code — rearchitecting onto cloud execution is the highest-friction option on the list, and inside each microVM the per-action question returns unchanged: the sandbox bounds the run, something still has to decide and record the actions.
What to actually do
- Everyone: configure
settings.json(option 1). Ten minutes, no excuse. - Prompt fatigue is the pain: add the sandbox runtime (option 2).
- Untrusted input touches the agent: container first (option 4) — full isolation is the right call, and nothing else here provides it.
- You need per-action rules and a record that survives the session — the two things isolation can’t give you: add the policy layer on the call path (option 3).
- Unattended fleet: cloud sandbox per run (option 6), with a policy layer inside deciding the actions.
The layering follows from what each thing is. Prompts ask you. Sandboxes and containers bound where actions can land. A blocklist trips on known-bad shell. A policy layer on the call path decides each action against your rules and writes down what happened. Every incident above slipped through a gap between those layers — the fix is knowing which layer you’re missing, not picking a winner.
Frequently asked questions
What is the best way to control what Claude Code can do?
Layer two things: configure the built-in permission system (settings.json allow/deny rules) as your baseline, then add either an isolation boundary (Anthropic’s sandbox runtime or a devcontainer) or a policy-and-audit layer on the call path, depending on whether your risk is ‘the agent escapes the workspace’ or ‘the agent does something allowed-looking but wrong.’ For untrusted input, use a container. They compose — the container bounds where the agent can act, the policy layer decides which actions run inside it.
Is Claude Code's settings.json deny list enough on its own?
No. Deny rules are string patterns over commands, and command strings have many spellings — quoting, variables, and interpreters route around prefix matching. The rules are also per-machine, and –dangerously-skip-permissions disables the permission system entirely, hooks included. Treat settings.json as the baseline, not the boundary.
Does Anthropic's sandbox stop destructive commands?
It stops them from leaving the boundary. Filesystem writes outside the workspace and unapproved network hosts are blocked at the OS level. A destructive command inside the workspace — dropping a table you have credentials for, force-pushing, deleting the directory you’re working in — is within bounds and runs. The sandbox answers ‘where can the agent act,’ not ‘should this action run.’
What is dcg (destructive-command-guard)?
An open-source Rust hook that pattern-matches dangerous git and shell commands (rm -rf, git reset –hard, DROP TABLE) and blocks them before execution, across a dozen-plus agents including Claude Code, Codex CLI, and Gemini CLI. It is shell-commands-only — file-edit tools and MCP calls never pass through it — and it fails open: if the hook is absent or errors, the command runs.
How do I get an audit trail of what Claude Code actually did?
The session transcript is the agent’s own account; an audit trail needs to be written by a layer the agent routes through, not by the agent. A policy layer on the call path (ACP’s local mode logs every tool call to ~/.acp/audit.jsonl, decisions included) gives you a per-call record — what ran, what was denied, and which rule decided — that survives the session and isn’t authored by the model.
Where to read more
- Which Claude Code tools to deny out of the box — the settings.json posture, argued from blast radius
- Claude Code deny-list bypasses — why string rules aren’t a boundary
- Claude Code vs Codex CLI: permission models compared — the same analysis across harnesses
- We recreated the Replit database deletion — and held it — the freeze-as-prompt failure, enforced in the call path