Why a container isn't the only answer
The reflexive instinct is “put claude code in a container.” That works, but it's heavy. You need to mount the project, wire up your git credentials, deal with file-permission mismatches, keep the image current. The agent loop also slows down because every shell call now crosses a process boundary you didn't need.
A policy sandbox is the lighter version. Almost every “the agent did something terrible” incident reduces to a small set of command classes: recursive deletion, sudo escalation, curl | sh, force pushes, terraform destroy, DROP TABLE, .env reads. Block the classes and the failure mode goes away. No kernel boundary needed.
The three sandbox layers
failproof ai composes the sandbox out of three hook layers, each doing one job.
PreToolUse: refuse unsafe calls before they run
- block-read-outside-cwd - constrains reads to project root. Exceptions allowed (e.g.
~/.aws/config) - block-secrets-write - refuses writes to
.key,.pem, and other secret-shaped files - block-rm-rf, block-sudo, block-curl-pipe-sh - the destructive-command core set
- block-env-files, protect-env-vars - refuses
.envandprintenv-style dumps - block-push-master, block-work-on-main, block-force-push - git safety
- block-terraform, block-kubectl, block-aws-cli, block-gcloud, block-az-cli, block-helm, block-gh-pipeline - opt-in infra blockers
PostToolUse: sanitize what comes back
- sanitize-api-keys (Anthropic, OpenAI, GitHub, AWS, Stripe, Google), sanitize-jwt, sanitize-connection-strings, sanitize-private-key-content, sanitize-bearer-tokens
If a secret made it past the PreToolUse layer, the sanitizers redact it before the result re-enters the agent context. The model never sees the raw value, so it can't propagate it.
Stop: gate the way out
- require-commit-before-stop - refuses to end with uncommitted changes
- require-push-before-stop - refuses to end with unpushed commits
- require-pr-before-stop - requires an open PR
- require-no-conflicts-before-stop - refuses to end with a conflicted PR
- require-ci-green-before-stop - refuses to end with red CI
Install (two commands)
The default set is the sandbox for a developer machine. For a production-adjacent project, enable the cloud and infra blockers from the dashboard at http://localhost:8020.
When you also want a container
Stack them. The container gives you a kernel boundary against whatever binaries the agent compiles and runs. The policy sandbox gives you fine-grained refusal at the command level - the container can't tell the difference between rm -rf /tmp/build and rm -rf /home/dev/project, but failproof can. Use both for agents driving production deploys; use policies only for day-to-day dev.
Tightening the sandbox per project
Drop a .failproofai.json at the project root to override the default policy set. The schema is documented at docs.befailproof.ai/configuration. Common overrides:
- Block
npm publishhard (promotewarn-package-publishto deny) - Allow
rm -rf .nextby adding it toblock-rm-rf's safe-path list - Disable
require-ci-green-before-stopon a repo without CI - Add a custom JS policy that blocks
npm install <non-pinned>
Related guides
- stop claude code from running dangerous commands
- claude code safety hooks setup
- how to block rm -rf in claude code
- prevent claude code from accessing .env
- prevent ai agent force push
- stop praying in prompts. start enforcing with hooks.