· 8 min read
Set Approval Rules by Blast Radius, Not by Tool Name
By K. Jung
- tools
Set approval rules around what can change and where it can reach, not around whether the agent is called “autonomous.” Auto-approve reads, searches, diffs, and local tests; require a fresh decision for credentials, network egress, destructive commands, protected paths, publishing, and anything that touches production.
That’s the practical answer, but the implementation matters: a permission prompt that appears after the agent has already inherited your cloud keys is theater. Put the agent in a directory-scoped, least-credentialed environment first, then use approval rules to make the remaining risky actions noisy.
Start from a read-only session that is actually useful
The default profile should let an agent understand a repository quickly without needing you to click through 30 prompts. Reading files, searching text, inspecting Git history, and running deterministic checks are normally low-cost and reversible. That does not mean every command containing test belongs on an allow list: a project’s test script can seed a database, start containers, or make network calls. Check what your package scripts and Make targets really do before you bless them.
For a one-off Claude Code investigation, start the session with only read and inspection commands pre-approved. Claude Code’s CLI supports --allowedTools and --disallowedTools, so this is a real command rather than an aspirational policy:
claude \
--allowedTools "Read" "Glob" "Grep" \
"Bash(git status:*)" "Bash(git diff:*)" "Bash(git log:*)" \
"Bash(npm test:*)" \
--disallowedTools "Bash(rm:*)" "Bash(git push:*)" \
"Bash(terraform apply:*)"Run that against a bug report and see where it interrupts. If it can diagnose the problem, gather evidence, and propose a small patch without another click, the starting point is doing its job. Let file edits prompt by default. You can approve a narrowly scoped edit when you have seen the diff target, instead of granting a long-lived right to edit every file the next time the model gets impatient.
This is bad at large mechanical refactors. A session that asks permission for each edit is tedious when you are intentionally renaming 200 symbols. Do not solve that by globally allowing all writes. Create a short-lived refactor profile or use a disposable worktree, grant write access only there, and delete the profile when the migration is finished.
Use three buckets: deny, ask, allow
Write the policy as a small operating manual. A rule should answer “can this happen without me here?” rather than “does this command look developer-ish?” GitHub Copilot’s managed settings make the precedence explicit: deny beats ask, which beats allow; once a managed permission rule or allow list exists, unmatched supported operations require approval. That is a good default shape even if you are using another agent.
- Deny always: reads of
.env, credential stores, SSH configuration, cloud-token directories, and production configuration; writes to CI workflow definitions, deployment manifests, release configuration, and access-control files; destructive operations such as recursive deletion; and direct production commands. - Ask every time: installing dependencies, changing lockfiles, starting or stopping containers, database migrations, network requests, Git pushes, creating pull requests, changing generated code, and edits outside the assigned directory.
- Allow without interruption: repository-local reads and search;
git status,git diff, andgit log; formatters that only change the task files; and test or lint commands you have inspected and can run without credentials or external side effects.
The important distinction is that “ask” is not a weaker deny. It is for actions that may be right today but should not become a standing capability. git push is a good example: perfectly normal at the end of one task, a bad surprise during diagnosis of another. Treat npm install the same way. Lockfile churn, post-install scripts, registry access, and native build steps are material changes, not housekeeping.
Make filesystem and network boundaries do the real work
Permission rules decide whether to ask. Sandboxes decide what the agent can do after an approval mistake. Set both. In Codex, a project or user configuration can combine approval_policy = "on-request" with sandbox_mode = "workspace-write"; on managed machines, organization requirements can prohibit permissive combinations. That is a safer baseline than accepting a broad “never ask” mode because an agent is running in your regular terminal.
# .codex/config.toml
approval_policy = "on-request"
sandbox_mode = "workspace-write"For remote or long-running jobs, make the workspace disposable. Mount only the repository or a task-specific checkout. Use a separate non-human service identity with no production access. Do not mount your home directory because the task might eventually need one file; copy that file into the workspace instead. If the agent needs a private package registry, put a proxy in front of it and scope the proxy to the single host and operation required.
Network access deserves its own rule. OpenAI-hosted sandboxes can disable outbound access or restrict it to an exact allowlist of one to 100 host names. Exact-host policies are refreshingly boring: allow your package registry and documentation mirror; do not allow the entire internet because a test might download a fixture. Subdomains and redirects need explicit entries, which forces you to discover dependencies rather than accidentally turning a sandbox into a browser with shell access.
Also keep actual secrets out of the environment whenever possible. OpenAI’s sandbox guidance makes the uncomfortable point plainly: agent-generated code can read environment variables. A vault or proxy that supplies a scoped credential only to an approved destination is meaningfully better than putting AWS_ACCESS_KEY_ID in the shell and hoping the agent never runs env, a test helper, or a pasted command that exposes it.
Protect the files people forget are production controls
Most teams remember .env and forget the files that decide what happens after merge. Put .github/workflows/, deployment directories, Terraform or Pulumi state configuration, Helm charts, container publish scripts, package publishing configuration, database migrations, and repository agent configuration in the ask-or-deny bucket. An agent can make a syntactically valid workflow change that silently changes who can obtain an OIDC token or what runs on pull requests. That is not a change to auto-approve because the diff is only six lines.
Apply the same caution to MCP servers and plugins. They are not harmless context providers; they expose tools and often credentials. Give a read-only research agent only repository search and file-view tools. Give a release agent its registry tool in a separate profile. GitHub’s custom-agent configuration supports an explicit tools list, while an omitted list enables all available tools. Treat an omitted tools list as a review finding, not a convenient default.
Test the policy before trusting it
Create a throwaway repository with a fake .env, a CI workflow, a dummy deployment file, and a script that attempts an outbound request. Then ask the agent to “fix tests, update the deployment, and push the branch.” You are testing the boundary, not its coding taste. It should read and diagnose freely, pause or fail on the sensitive paths and network action, and never find credentials because there are none in its environment.
Run that exercise whenever you add an MCP server, enable a new agent mode, or upgrade the tool. GitHub’s Copilot hooks can programmatically allow or deny tool requests before normal approval handling, and OpenAI’s agent guidance recommends evaluating the exact action, target, arguments, identity, and time window at the side-effect boundary. Those controls are worth adding when your agent runs unattended in CI; for an individual laptop workflow, a small deny/ask/allow policy plus a sandbox gets you most of the benefit with much less machinery.
Finally, watch for approval fatigue. If you approve the same safe command ten times a day, narrow it into the allow bucket. If a prompt makes you stop and read the command, leave it in ask. And if an action would wake you up at 2 a.m. after the agent performed it while you were away, it belongs behind a hard boundary—not behind a more politely worded confirmation dialog.
Sources & citations
- [1]Anthropic Claude Code CLI reference
- [2]GitHub Copilot enterprise managed settings
- [3]GitHub Copilot custom agents configuration
- [4]OpenAI Codex configuration basics
- [5]OpenAI-hosted sandbox network controls
- [6]OpenAI sandbox security guidance
- [7]OpenAI guardrails and human review guidance
- [8]GitHub Copilot hooks reference