Permissions for agents that act: sizing the blast radius before you hand over the keys
An agent is an identity that takes instructions from text it did not write. Scope it like a service account you assume is partly compromised, because in the injection case it is.
The security model for an agent is not the security model for a user, and it is not the one for a service either. A service does a fixed set of things with fixed credentials. A user is accountable and can be told about policy. An agent has a service's credentials and a user's open-ended instruction set, and its instructions can arrive inside the data it is processing.
That last part is the whole design constraint. Assume any agent that reads external content will, at some point, be told to do something by that content. Then size what happens next.
Start from the reachable set, not the tool list
The useful question is not "which tools does it have" but "what is the union of everything those tools can touch". An agent with a single run_query tool against a warehouse role that can read every schema has a reachable set of the entire company's data, from one tool.
Write it down per agent: which systems, which records, read or write, and what the worst single action is. In most of the reviews we do this document does not exist, and producing it changes two or three things immediately — usually a credential that was convenient during the prototype and never narrowed.
Its own identity, always
Never let an agent borrow a human's session or a shared admin key. Give it a dedicated service principal, its own IAM role, its own API keys. Three things follow from this that you cannot get any other way: the audit log attributes actions to the agent, you can revoke it without disrupting a person, and you can apply a policy that would be unacceptable on a human account.
Where the agent acts on behalf of a specific user, propagate that user's identity rather than flattening it — the agent's role should be able to do less than its caller, never more. An agent that can read documents its requester cannot is a data leak with a chat interface, and it is the single most common design error we find in internal assistants.
Read and write are different systems
The most effective control is boring: two credentials. The agent reasons with a read-only role; writes go through a separate, narrow, deterministic path that validates against rules you wrote.
Concretely, in cloud operations: the agent gets ReadOnlyAccess minus the secret-reading actions, and anything that changes state is a proposed Terraform plan or a queued action that a pipeline applies. The agent's output is a pull request, not an API call. This turns every dangerous capability into a reviewable artefact and costs you very little in usefulness — see agents on cloud operations.
Bound the writes you do allow
When an action must happen without a human, bound it three ways:
Scope — the write can only touch resources matching a filter the agent cannot change. Tags, a project prefix, a customer id taken from the request rather than from the model's output.
Magnitude — a refund tool with no maximum is a maximum of your bank balance. Every write tool needs a limit expressed in the units of the business.
Rate — a per-hour cap enforced outside the agent. The injection case and the runaway-loop case both look the same from the outside: many actions, quickly. One counter catches both.
Log the intent, not just the call
An audit trail that shows POST /customers/4471 tells you nothing about why. Log the tool call together with the model's stated reason, the prompt hash, the run id and the user it was acting for. When something goes wrong — and the first interesting incident always involves an action that was individually authorised and collectively wrong — this is the difference between a two-hour investigation and a two-day one.
The review cadence
Agent permissions rot faster than service permissions, because capabilities get added during experimentation and never removed. Put the reachable-set document on a quarterly review with the same seriousness as a privileged access review, and use access analyzer tooling to diff granted against used — least privilege from actual usage applies here more than anywhere, since agents use a small fraction of what they are given.
What to do this week
Pick your most-used internal agent and answer one question: if an attacker could write arbitrary instructions into its input, what is the worst thing it could do in the next sixty seconds? If the answer involves customer data or production infrastructure, the fix is not a better prompt. It is a smaller role.