Skip to content
Agentic Control Plane

Agent Access Control: Least-Privilege Scoped Tools

David Crowe · 4 min read
access-control least-privilege governance architecture

Access control for AI agents tends to get framed as a policy problem — write the rules, enforce them at runtime. That’s half of it. The other half, the half that decides whether the rules can be enforced at all, is a design decision you make before any policy exists: what tools does the agent get?

Give an agent a shell, a generic HTTP client, and read/write to the whole filesystem, and no access-control system can save you — because “the agent’s permissions” are now “anything those tools can do,” which is everything. Give it three narrow, typed tools and you’ve made access control possible: now permissions are a property of the tools, and the tools are a list you control. Least privilege isn’t a hardening step you add later. It’s the precondition.

Why broad tools defeat access control

The principle of least privilege is old and uncontroversial: give each component the minimum access it needs, nothing more. For agents it has a sharp, specific consequence, because agent tools vary wildly in how bounded they are.

A typed tool has an effect set you can read off its interface. customer.getProfile(id) reads one customer. invoice.send(id) sends one invoice. You can write a policy about it — this user, this tool, these arguments — and the policy means something.

A broad tool has an effect set that exceeds its interface. bash(cmd) is the worst case: one call, unbounded effects. But it’s not alone — a generic http_request(url, body) can hit your internal network and pivot; a python_exec is a shell by another name; an unrestricted read_file(path) can read ~/.aws/credentials. The moment one of these is in the toolset, your access-control system is only as tight as that tool is loose — which is to say, not tight at all. The agent doesn’t even have to be adversarial; a wrong turn routes through the broad tool and out the side.

So the first move in agent access control isn’t writing policy. It’s removing the broad tools and replacing them with narrow ones that do the specific jobs.

What scoped access control looks like once tools are narrow

With a typed tool surface, access control becomes tractable and layered:

  • Per-agent tool allowlist. Each agent gets exactly the tools its job requires — a research agent gets web.search and doc.read, not db.write. The toolset is the first permission boundary.
  • Per-user scope. The agent acts for a verified end user, and its permissions are a strict subset of that user’s. The intern’s agent inherits intern access; the admin’s agent inherits admin access. Identity flows to every tool call, so “who is this acting for” is always answerable — not a shared service account.
  • Per-argument policy. Narrow tools have arguments you can gate. refund.issue(amount) can be allowed under $50 and escalated above it. You can’t write that rule against bash.
  • Narrowing across delegation. When one agent hands off to another, scope and budget only ever shrink down the chain — a sub-agent can’t acquire a capability its caller didn’t have.

Every one of those depends on the tools being narrow enough that a policy about them is meaningful. That’s why the toolset comes first.

The boundary that least privilege can’t reach — and what to do with it

Be honest about the limit: some agents genuinely need to run arbitrary code (a coding agent, a data-analysis agent with a Python sandbox). You can’t make that surface narrow, so a tool-level access-control layer can’t completely mediate it. That’s not a failure of the approach — it’s a sign you’re at the wrong boundary.

For genuinely open-ended execution, the right control is isolation: run it in a sandbox where the blast radius is contained and its network egress goes through one door you control. Access control there happens at the boundary the agent can’t route around (the wire), not at the tool call. Least-privilege tools and a sandbox aren’t alternatives — you use narrow typed tools for everything you can, and isolate the irreducibly-broad rest.

Where to start

  1. List every tool your agent can call. Delete the broad ones. Replace shell / generic-HTTP / whole-filesystem with the specific typed tools the task needs. This single change does more for access control than any policy.
  2. Scope per agent, then per user. Give each agent only its tools; thread the end user’s verified identity so each agent’s reach is a subset of that user’s.
  3. Gate arguments, narrow delegation. Put policy on the arguments narrow tools expose, and ensure scope only shrinks across hops.
  4. Sandbox what’s irreducibly broad. If a shell must exist, isolate it; put the control on egress.

Access control for agents is mostly tool design. Get the surface narrow and least-privilege, and policy, tool-call governance, cost attribution, and audit all become achievable. Leave it broad and none of them are. See what an agentic control plane is for the enforcement layer that sits on a narrow surface.

Get the next post
Agentic governance, AgentGovBench updates, the occasional incident post-mortem. One email per post. No marketing fluff.
Share: Twitter LinkedIn
Related posts

← back to blog