An agent that has to ask before every command is too slow to be useful. An agent that never asks is a risk. We ran every session in Claude Code's auto permission mode, and the guardrails around it are why we were comfortable doing that.
Auto mode, with a referee
In auto mode the agent runs commands without asking each time. A classifier reviews each action first and blocks the risky ones.
On the web portal alone, in one month it blocked 24 actions:
- exposing or materialising credentials
- modifying shared resources
- real-world transactions, such as a purchase on a test environment
- weakening TLS or authentication, such as typing a certificate-bypass phrase into a browser
- bypassing CI and writing production feature flags during a release
- destructive git commands
- an unrequested commit in a connected app
Separate safety checks also stopped three rm -rf commands.
It said no even after we said yes
Need Help Implementing This?
Our team can help you put these ideas into action. Get in touch for a free consultation.
The most interesting blocks came after the developer had approved something in chat.
- A test-environment write. The developer said a first real write against the shared test environment would do no harm. The classifier still blocked the agent's script. The agent didn't look for a way around it. It suggested the developer do the action in the app on a real phone while it captured the logs. That turned out to be the better test, because it exercised the whole feature, not just the API call.
- A local merge. "go, do it locally" would have bypassed branch protection during a release. The classifier blocked it, and the agent handed the exact commands to the developer to run.
We'd rather have a referee that sometimes blocks too much than one that trusts a chat message.
The agent doesn't hunt for credentials
- A second Google profile. When the agent's Google account lacked API access, it tried another profile on the machine. The classifier blocked that as credential exploration. The agent stopped and asked, and the developer chose which account to use.
- An unusable test account. A live probe failed because a test account still needed setting up. Searching for other credentials was blocked, and the agent's report said so plainly: "I didn't look for other credentials."
- A memory rule. Memory now holds the rule "ask the user; do not hunt for credentials".
Even shared credential files need care. Once the agent ran probes using a test account that belonged to another developer, found in a shared environment file. The developer caught it by asking "which environment do you test?", and the agent corrected its report and its memory.
Secret hygiene in every command
- Masked output. Environment files are printed only through a masking
sedcommand. To confirm a setting reached a container, the agent printed that one key withjqand nothing else. - No token-printing tools. A browser-debugging tool's network inspector printed bearer tokens, so we stopped using it. Browser checks moved to Playwright scripts that read credentials from the environment themselves, and use of that tool dropped to zero.
- No secrets in chat. When the developer offered an API key, the agent asked for it to go into a gitignored file instead of the chat or an
echocommand. - A scan before every push. The agent scans every diff for credentials before pushing.
- Signing stays human. The developer ran signed release builds and protected pushes themselves, typing shell commands directly into the session.
Humans pushed back too. When the agent's CI instructions asked the developer to store a private key in the build system, the developer asked "what exactly do you want me to store?" and refused. The agent redesigned the job so the second connection hop runs on the jump host, and no key is needed in CI.
Security as ordinary work
- A security review became small PRs. One review of the consumer app turned into a handful of small pull requests, each tested on a real phone. One device test found that the iOS keychain kept a login session after the app was uninstalled, so reinstalling logged the user straight back in. A fresh-install check now clears it.
- Signed over-the-air updates. Before rollout, the developer confirmed on a beta build that an unsigned bundle was refused and a signed one installed.
- Rules written down. "Never add a plaintext fallback when secure storage throws." A scheduled daily job checks the server's certificate chain for drift.
- Security notes stay outside client repositories. Our own security review notes live outside any repository we sync to the client.
Guardrails elsewhere in this series
- Tickets are redacted before any agent sees them (Part 5).
- Live probes follow rules such as "a GET can write" and "restore what you change" (Part 6).
- QA agents run behind a write guard that fakes errors for any write not on an allow-list (Part 9).
- Agents open pull requests, and humans merge (Part 10).
What to copy
- Use auto mode with a classifier, and don't route around its blocks, even with a "yes" in chat.
- When something is blocked, look for the safer path, like a human doing the action while the agent watches.
- Keep credentials in files the agent reads, never in chat or transcripts.
- Retire any tool that prints secrets.
- Keep signing keys and protected merges with humans.