Coding Agent Permissions: The Access Ladder I Use Before I Press Approve
The dangerous permission prompt is usually the convenient one.
Your coding agent asks whether it can run commands without asking again. You are halfway through a bug. You have already approved git status, bun test, and one harmless file read. You click "allow for this session" because confirming every command is annoying, and ten minutes later the agent has enough authority to install a package, read an environment file, send a request anywhere on the internet, and push a branch if you forgot what was in your shell.
Nothing bad may happen. That does not make it a good security model.
An agent is not just a text editor with opinions. Once it can act, its effective permission is the combination of every tool you gave it. Read access to the repo plus a shell plus your credentials plus network access is not four small permissions. It may be enough authority to inspect, modify, and send out everything the current user can reach.
The goal is not to make agents useless. The goal is to give them the narrowest set of abilities that completes the task in front of them, then widen it deliberately when the task changes.
The Permission Question Nobody Asks
Before enabling an agent, ask one question in a very literal way:
What could it read, change, execute, or send if it made the worst plausible decision while following an untrusted instruction?
That last clause matters. An agent that browses an issue, a webpage, a dependency README, or a support ticket may encounter text that tries to redirect it. It does not need to be a malicious hacker note. A misleading install command or a stale docs page can do enough damage when the agent has broad permissions.
The model cannot reliably infer a safe authorization boundary from the task alone. Research into coding-agent permission boundaries has found the same uncomfortable pattern: models can omit a permission required by a task while also granting unrelated sensitive permissions. More reasoning does not make that tradeoff disappear. Authorization needs to come from policy and tooling, not the agent's confidence.
That is just least privilege, applied to a new kind of collaborator.
The Access Ladder
I think about agent access in seven levels. Start at the lowest level that makes the task possible. Move up only when the next level has a clear, named reason.
Level 1: Read the working tree
The agent can inspect the project files, configuration, and relevant logs. It cannot change anything.
This is enough for code review, explaining a system, making a plan, diagnosing a failing build, and locating the likely source of a bug. It is the right default when somebody asks, "What is wrong?" rather than, "Fix it."
The trap at this level is scope. Reading the repository is not the same thing as reading your whole home directory. A project folder often contains more than code: local exports, test fixtures, screenshots, customer samples, and accidentally committed secrets. Mount only the directory the task needs.
Level 2: Edit the working tree
The agent can create and change files inside the project, but it cannot run arbitrary commands.
This is suitable for a content change, a small refactor, a documentation update, or a component you will review manually. It is more powerful than it sounds. File writing can modify configuration, CI workflows, scripts, and code that runs later, so treat it as a real change permission rather than a harmless drafting permission.
Keep package manifests and CI configuration in the review scope even if the task seems unrelated. Dependency changes are code changes with a delayed execution path.
Level 3: Run a constrained local command set
The agent can run the checks that prove its change works: type checking, unit tests, a formatter, a local build, or a narrowly scoped development server.
This is where I draw the first hard boundary. bun run build and bun test are not the same category as an unrestricted terminal. Know which package manager the repository uses. Know what the scripts run. Do not assume a test command is harmless because it has the word test in it. A project script can execute migrations, call a remote service, rewrite snapshots, or do anything else a shell command can do.
Use an allowlist for predictable commands. Let the agent propose everything else. This creates a little friction, and the friction is doing work for you.
Level 4: Local browser and localhost
The agent can open the application it is running, inspect the rendered UI, and test a local flow. It cannot log into external services or browse arbitrary sites.
Visual verification belongs here. A code change that looks right in a diff can be broken in the browser. This level is still local: the agent can test localhost, capture the error, and go back to the source without carrying your authenticated browser profile along with it.
Keep the distinction between local browser access and your daily browser profile. The second one includes every active session you have forgotten about. Your inbox, GitHub, banking tab, billing dashboard, and customer admin panel are not test fixtures.
Level 5: Narrow outbound network access
The agent can reach a listed domain for a concrete reason. Maybe it needs official framework docs, a package registry, or a staging API endpoint.
The right setting is not "internet access". It is an allowlist of domains, methods, and where possible paths. An agent changing a React component does not need the ability to post to Slack, download a binary from an arbitrary release page, or call every service reachable from your laptop.
This is also where you should make a decision about package installation. Dependency installation is remote code execution from a supply chain you did not author. It may be warranted, but it deserves its own approval, a pinned version, and a look at whether a small local utility solves the problem instead.
Level 6: Source control and third-party tools
The agent can create a branch, commit code, open a pull request, read an issue tracker, or call an MCP server.
Give read and write access separately. Reading a GitHub issue does not require pushing to main. Reading a calendar does not require creating an event. Searching a CRM does not require exporting every contact.
This is where tool design becomes access control. In my guide to agent tool design, I argued that tool schemas need to state what they do and when not to use them. Permissions need the same clarity. A tool called manage_repository is a permission accident waiting to happen. create_pull_request_from_current_branch is a much narrower promise.
If a tool offers a write action, make the write target visible before it runs. The model should not be the only place where the difference between reading a record and deleting it exists.
Level 7: Deployments, customer data, and production
The agent can affect real users or access secrets.
This is not a normal default. It should be an exceptional, task-specific permission with a human confirmation at the final boundary. A staging deployment is not production. A read-only production query is not a write. A token that may access one test project is not a token that can access every customer account.
The word "production" hides too much. Break it down into deploy, read logs, read metrics, read data, write data, rotate credentials, and change infrastructure. An agent may need exactly one of those. Giving it the bundle because a dashboard offers one API key is a design failure, not a necessity.
Secrets Are Not Agent Input
The cleanest rule I have found is this: agents need access to a capability, not the secret that represents it.
Do not paste API keys into prompts. Do not let the agent print environment files to prove it found the configuration. Do not attach a production .env to a context window. Once a secret becomes model input, you have widened the exposure from a process boundary to every system that handles the conversation.
Use short-lived, scoped credentials injected into the execution environment. Prefer a service identity that can call one staging endpoint over your own personal token. Rotate the credentials when the task finishes if they were minted for it. Log the action, but never log the secret.
This is less dramatic than it sounds. Most of the time it means creating a narrower token once instead of reusing the convenient admin token forever.
A Permission Set Should Expire
Long-lived approval is where convenience turns into permission creep.
The task that needed package registry access last Tuesday does not prove the next task needs it. The temporary database credential that made a migration review easier does not belong in a reusable configuration. Agents are unusually good at turning a small convenience into a repeated action because they follow the environment we leave behind.
Make permission state part of the cleanup:
- remove temporary tokens
- stop local servers
- revoke network exceptions
- remove copied fixtures containing customer data
- review changed workflow files and package manifests
- record what the agent was allowed to do when it mattered
That last point helps incident response and ordinary debugging. When a surprising change lands, you want to know whether the agent could have made it, not reconstruct the answer from a vague chat transcript.
The Default Setup I Would Use Today
For ordinary application work, I would start an agent with project-only file access, edit permission, a small allowlist of local checks, and localhost browser access. No external browser profile. No arbitrary network. No deployment. No production data. No Git write access until the change has passed review.
Then I would grant more only in response to a need I can name. "It needs to read the official Next.js documentation" is a reason. "It might be useful" is not.
That setup does not prevent every bad outcome. Nothing does. It reduces blast radius, makes approvals meaningful, and keeps one confused instruction from becoming a company-wide event.
An agent that must ask before doing something consequential is not less autonomous in the way that matters. It is working inside a boundary you understand.
Frequently Asked
Should a coding agent have access to my .env file?
Usually no. Give it the required capability through a scoped, temporary environment instead. Secrets should not be copied into prompts, logs, or model context.
Is it safe to enable unrestricted terminal commands for a coding agent?
Only in an isolated environment with no sensitive files, credentials, or broad network access. In a normal development machine, allow predictable checks and require approval for the rest.
Can I let an agent deploy to production?
Only for a tightly defined deployment path with a final human confirmation, scoped credentials, and clear rollback. Deployment access should not imply access to production data or infrastructure administration.