Agent Permission Models: What Runs Without Asking
A permission model for an agent decides which operations run unattended and which pause for a human. The workable axis is reversibility, not trust: reads and additive writes run freely; deletes, publishes and payments prompt. Enforce it by allowlisting tools per session, scoping and time-bounding credentials, and keeping an escape hatch a human can reach mid-run.
What a permission model is
A permission model for an agent is the set of rules that decides, before and during a run, which operations the agent may execute on its own and which must stop and wait for a person. It is enforced by the harness, the tool surface and the credentials the agent holds, not by the agent’s own judgement. The OWASP Top 10 for LLM applications files the failure of getting this wrong under Excessive Agency and splits it into three causes worth keeping apart: excessive functionality (tools the agent does not need), excessive permissions (tools that can do more than the task requires) and excessive autonomy (consequential actions taken without confirmation). Each of the points below addresses one of those three.
The axis is reversibility, not trust
The question a permission model has to answer is not whether the agent is trusted. Trust is a property of the model, the prompt and the day’s inputs, and it changes with all three; a model that behaves for a thousand runs will still, on run one thousand and one, act on a string it read from a web page. Designing around trust therefore collapses into one of two failure modes. Either the operator decides the agent is trustworthy and lets everything through, or the operator decides it is not and prompts on everything.
The second mode fails just as completely as the first, only more slowly. A scheme that asks for approval on every file write, every shell command and every API call trains the human to press allow without reading. This is approval fatigue, and it is not a discipline problem to be solved with better people; it is the predictable result of asking a person to make hundreds of identical low-stakes decisions per hour. Within a session or two, the approval step carries no information, and the one prompt that mattered — the destructive one — is waved through with the same reflex as the previous four hundred. A prompt-on-everything model is, in practice, a prompt-on-nothing model with extra latency.
So the useful question is a different one: for each operation the agent could take, what does it cost to undo? That is the axis the rest of the model hangs from.
Reads and additive writes run unattended; the destructive set prompts
Sorting operations by reversibility produces a short list of things worth a prompt and a long list of things that are not.
Reads are free in the reversibility sense. Listing a directory, fetching a document, querying a database, calling a search API — none of these change state that has to be restored. They still carry a confidentiality cost, which is a separate control (what the agent may read is bounded by filesystem access rules and by network egress, not by an approval prompt), but there is nothing to roll back.
Additive writes are nearly as cheap. Creating a new file, appending to a log, inserting a row, opening a draft pull request, writing to a scratch branch: if the result is wrong, the fix is to delete what was added, and the previous state is intact underneath it. An agent that can only add can make a mess, but it cannot destroy anything that existed before it started. Letting these run without asking is what makes an agent worth having; a coding agent that has to ask before every file it creates is slower than typing.
The operations worth a prompt form a small, recognisable set:
- Deletes and overwrites — removing a file, dropping a table, force-pushing a branch, replacing a document in place. The old state is gone, and recovery depends on a backup existing and being recent.
- Publishes — anything that crosses from a private working area into a place other people or systems read from: merging to a release branch, deploying, sending an email, posting to a channel, pushing to a public registry. A publish is reversible in theory and irreversible in effect, because the audience has already seen it, cached it or acted on it.
- Payments and anything that spends — purchases, provisioning billable resources, calls to metered services beyond a set budget. Money is the clearest case of an action that a later step cannot take back.
That is roughly the whole list. Everything else — the reads and additive writes that make up the large majority of any agent’s actions — runs unattended, and because prompts are rare, the human still reads them when they come. Anthropic’s write-up on building effective agents makes the same point from the harness side: it recommends letting agents run autonomously between defined checkpoints, pausing for human feedback at those checkpoints rather than at every step, and testing in sandboxed environments with guardrails so that the checkpoints can be sparse. A checkpoint on the destructive set is what a sparse, informative approval scheme looks like.
Two details keep this axis honest. First, reversibility is decided by the environment, not the operation name. Deleting a file inside a fresh sandbox that is discarded at the end of the run is reversible; deleting the same file on a shared volume is not. The classification should follow where the operation lands. Second, a sequence of individually additive writes can amount to a destructive one — adding a migration that drops a column is a file create, and it is also a delete waiting for the next deploy. Where that pattern is known, the model should treat the eventual effect, not the immediate write, as the thing being approved.
Allowlist the tools; do not instruct the agent to avoid them
Once the destructive set is identified, there are two ways to keep the agent out of it. One is to give the agent every tool and tell it, in the system prompt, not to use the dangerous ones without asking. The other is to not advertise the dangerous tools to the session at all, or to advertise them only behind a wrapper that stops and asks.
The second is stronger, and the difference is not one of degree. An instruction lives in the same channel as everything else the model reads: the task, the tool results, the contents of the files it opens, the pages it fetches. Prompt injection — the first entry on the OWASP list — is precisely the observation that text arriving through that channel can carry instructions that override the ones the operator wrote. If delete_bucket is in the tool list, then whether it gets called depends on the model correctly weighing the operator’s don’t against whatever it just read that said do. That is a contest the operator can lose on any given run.
A tool that is not in the session’s list cannot be called, whatever the model reads. There is no argument that produces a function the runtime does not expose. Injected text can make the model want to delete the bucket; it cannot make a missing tool appear. This is the mechanism behind the OWASP guidance to address excessive functionality by removing tools rather than instructing around them: the control lives outside the model’s reasoning, so it holds when the reasoning is compromised.
The cost of allowlisting is that someone has to enumerate what a task needs, and an agent that hits a missing tool will fail or improvise instead of asking. That is the right trade. A failed run is visible and cheap; a run that succeeded at something it should not have done is neither. Where a tool is genuinely needed but sits in the destructive set, the pattern is to expose it through a wrapper whose implementation is pause, show the human the call and its arguments, wait — so the tool is available and the approval is structural, not textual.
Scope permissions per session, not per agent
A common shortcut is to attach permissions to the agent as a named thing: the deploy agent may deploy, the research agent may only read. It fails because the same agent does jobs of very different reach. A coding agent running a database migration needs write access to the schema and a credential that can execute DDL. The same agent, five minutes later, answering a question about how the codebase handles retries needs read access to source and nothing else. If the permission set is attached to the agent, it has to be the union of everything the agent ever does, and the question-answering session runs with the migration’s reach.
Scoping per session means the permission set is decided when the run starts, from what this run is for, and dies with the run. The migration session gets the schema credential and the tool that runs it; the question session gets a read-only checkout and a search tool. Neither can be widened from inside. This is the natural place to apply the reversibility sort: the session’s declared purpose says which additive writes are in scope and whether anything from the destructive set will be needed at all, and most sessions need none of it.
Per-session scope also makes the audit trail legible. When every session carries a scope statement, a reviewer reconstructing a run can see what it was allowed to do next to what it did, and a call outside the declared scope is a defect in the harness rather than a judgement call about the agent’s behaviour. That reconstruction is its own subject — see replaying and debugging an agent run — but the scope statement is what gives it a baseline to compare against.
Credential scope is the real boundary
Everything above operates at the harness layer: which tools are advertised, which calls pause. Underneath it sits the credential the agent actually holds, and that is the boundary that decides what happens when the harness has a bug.
An agent restricted at the prompt and at the tool list but holding an admin token is one bug away from full access. The bug can be anywhere: a tool wrapper that forwards an argument it should have rejected, a shell tool that lets the model run curl against the same API the wrapper was guarding, a path traversal in a file tool, a model that finds the token in an environment variable and uses it directly. Each of those turns may only call these three functions into may do anything the token can do, and an admin token can do everything.
The corrective is to make the credential the agent holds match the session’s scope, so that even a complete bypass of the harness lands on a token that cannot do more than the session was meant to. The OWASP Secrets Management cheat sheet sets out the general discipline — least privilege per secret, secrets scoped to the workload that uses them, no long-lived shared credentials, access to secrets logged and auditable — and none of it was written specifically for agents, which is rather the point: an agent is a workload that reads untrusted input and executes actions, and it deserves the same treatment as any other service in that position, not a lighter one because it is new.
Concretely, this means a read-only session holds a read-only token; a session that may add rows holds a token that can insert but not delete; a session that may open pull requests holds a token that cannot merge them. If the platform in question cannot mint a credential that narrow, that is a fact about the platform to be weighed before an agent is pointed at it. It also argues for the agent having its own identity to hold those credentials under, rather than borrowing a person’s — the agent identity and audit page covers why a human’s token in an agent’s hands is the wrong shape even before scope is considered.
The cost is real: narrow credentials are more work to provision, and a task that turns out to need one more permission stops until someone issues it. That stop is the model working. It is the same trade as the missing tool — a visible failure in exchange for the removal of an invisible one.
Time-bound the credential as well as scoping it
Scope says what a credential can do; lifetime says for how long. Both matter, and the second is easy to forget once the first is done.
Agents produce transcripts. The tool calls, their arguments, the environment the agent inspected and the responses it received are recorded — for debugging, for audit, for the next model to learn from, sometimes just because logging was on. Anything the agent held is likely to appear in that record: a token pasted into a request header, an environment dump that included the secret, a shell command with the key inline. The transcript is then read by people and systems whose access was never part of the permission model — a colleague debugging a week later, a log aggregator, an evaluation pipeline, another agent asked to summarise what happened.
A credential valid for the length of a task cannot be replayed by whatever reads the transcript later. If the token expired when the run finished, its appearance in a log is an untidiness rather than a breach. If it was a long-lived key, every reader of that log now holds it. The Secrets Management cheat sheet’s preference for short-lived, dynamically issued secrets over static ones is the general form of this; for agents, the natural lifetime is the session, because the session already has a beginning and an end and a declared purpose. Issue the credential at session start with an expiry at or shortly after the session’s expected end, and revoke it explicitly on completion rather than waiting for the clock. Where the platform supports it, bind the credential to the session’s origin as well, so that a copy used from somewhere else is refused even inside its lifetime.
Time-bounding also limits a class of failure that scoping does not: a session that runs longer than intended, or that is left attached to a resource nobody remembers, holds nothing usable once the credential lapses. Combined with per-session scope, it means the permission an agent held is a fact about a bounded interval, and a reviewer can state precisely what was possible and when.
Every model needs an escape hatch a human can reach mid-run
The points above are all decided before the run: which tools are advertised, what the credential can do, how long it lasts, which calls pause. Every one of them encodes what the person writing the policy anticipated. The failure that matters is the one nobody anticipated when the policy was written — the additive write that turns out to be destructive in a context the classification did not foresee, the allowed tool combined with another allowed tool into an effect neither had alone, the task that drifts from its declared scope without ever making a call outside it.
A permission model that has no way for a human to intervene once the run has started is betting that the policy was complete. It never is. So the model needs a stop that works from outside the agent’s loop: a way to halt the run, revoke the session’s credential, and cut its network reach, that does not depend on the agent noticing, cooperating or reaching its next checkpoint. Practically that means the harness exposes a control the operator can reach while the run is in progress, the credential can be revoked from the issuing side without the agent’s participation, and the sandbox’s egress can be closed independently of the process inside it. Anthropic’s guidance for agentic systems assumes exactly this — that autonomy is granted between checkpoints inside an environment the operator can observe and stop, not granted once and left alone.
The escape hatch also has to be reachable in the sense of being noticed in time, which is why unattended runs need some form of live signal — a stream of tool calls, a progress channel, an alert on calls that pass the harness but match a watch pattern — rather than a transcript reviewed after the fact. A stop button nobody is watching is documentation.
What a reader checking this page looks up next
The reversibility axis decides what pauses; the tool allowlist and the credential decide how that is enforced; per-session scope and time-bounding decide for how long and how far; the escape hatch covers what the policy missed. Each of these leans on an adjacent control that this page only names. Bounding what the agent may read on disk is its own subject, as is bounding where it may connect. Whose identity the credential is issued under, and how that identity is audited, determines whether narrow, short-lived tokens are even available. And the record of what a session was allowed to do is only useful if the record of what it actually did can be reconstructed beside it. The OWASP entries on prompt injection and excessive agency and the Secrets Management cheat sheet are the standing references for the threat model and the credential discipline respectively; the agent-specific harness patterns are best read in Anthropic’s write-up, which is short and current enough to check directly.
Sources
- OWASP Top 10 for LLM applicationsowasp.org
- building effective agentsanthropic.com
- OWASP Secrets Management cheat sheetcheatsheetseries.owasp.org
See also
Treat tool names and parameter schemas as agent-facing APIs: classify breaking changes, deploy versions in parallel, and retain per-version traces.
Restore agent sessions after WebSocket loss with stable session IDs, acknowledged event cursors, and ordered replay without UI gaps or duplicates.
A practical method for capturing agent tasks, state, tools, production failures, labels, privacy review, and a prompt-tuning holdout.
Release model, prompt, tool, and policy changes to a sticky cohort, measure the result, and roll back without stranding side effects.