Agent infrastructure

Platform integration

Least privilege for AI agents: what the boundary actually looks like in production

Least privilege for AI agents is a runtime boundary, not a policy. How scoped permissions, delegated auth, and identity flow actually hold in production.

10 minute read
Decorative imagery showcasing Pontil's brand

When an agent calls a tool, who is it acting as, and what is it allowed to do? That question sounds obvious. In most agent projects it isn't answered until security review sends the design back.

Least privilege for AI agents is the discipline of scoping every tool call to the smallest set of permissions the task genuinely needs — and no more. In production, that's harder than it sounds. Agents span multiple products, act on behalf of different users across a single trajectory, and compose tools in orders no one wrote down at design time. The old model — one service account, one broad API key, one integration user with admin rights — collapses under agent load.

Our view: least privilege for agents isn't a policy you write, it's a boundary the runtime holds. Prompt-layer intent checks don't count. Scopes on a shared token don't count. The boundary that survives audit sits between the agent and the tool call, executes as the authenticated user, and denies anything outside the current task's authorised surface.

This piece walks through five things: why the old identity model breaks, what scoped agent permissions actually mean, where authorisation belongs in the stack, the trade-offs of the three enforcement patterns teams try, and what a production-grade least-privilege model looks like end-to-end.

Why the service-account model breaks for agents

Most SaaS integrations were built on a shared identity. A partner app authenticates once with an API key or a client-credentials OAuth flow, gets a token scoped to the whole tenant, and calls the API on behalf of "the integration." That model has worked for a decade because the caller was another system, doing narrow, predictable work, on a schedule.

Agents break every assumption in that sentence. The caller is a model — non-deterministic, capable of composing tool calls the integration author didn't imagine. The work isn't narrow — a single trajectory might touch billing, provisioning, and customer data. And the identity isn't the integration — it's a specific user who asked the agent to do something specific.

When the token is shared, three things go wrong at once. Audit logs show the integration acting, not the user, so incident response can't attribute actions. Rate limits and quotas collapse into one bucket that noisy agents blow through and quiet users get throttled by. And the blast radius of a prompt-injection attack is the entire tenant, because the token has permissions the current task doesn't need.

The fix isn't a bigger token. It's a smaller one — issued per user, per task, held only for the duration of the tool call, and denied any capability outside the current authorisation. That's what least privilege means in this world.

What scoped agent permissions actually mean

"Scopes" is an overloaded word. In OAuth, a scope is a string on a token declaring what the token can do. In least-privilege terms, scope is a much finer thing: the intersection of who is calling, what tool is being invoked, which resources it touches, and when the call is happening.

A useful way to think about this is four dimensions:

Identity
Capability
Resource
Time

What it answers

Who is acting?

What action is allowed?

On which records?

For how long?

Old model

Integration user

Broad API scope

Whole tenant

Long-lived token

Least-privilege model

Authenticated end user

Specific tool + params

Only records this user can already see

Scoped to the current task


Most teams get identity right (they use OAuth), sometimes get capability right (they use scopes), and almost always miss resource and time. A crm.contacts.write scope on a token that lives for 24 hours and can touch any contact in the tenant is not least privilege. It's least privilege in the shape of a slogan.

What matters in production is that the resource dimension resolves at call time, against the authenticated user's real permissions in the product — not against a static allowlist. If the user can't see contact 42 in the UI, the agent acting on their behalf shouldn't be able to read it through a tool either. That's the boundary. Everything else is decoration.

This is why we've argued the agent identity vs user identity boundary matters more than most teams treat it. Collapsing the two makes least privilege impossible from the first line of code.

Where authorisation belongs in the stack

There are three layers a team could plausibly enforce least privilege at. Only one of them holds.

The prompt layer. "You are an assistant. Only take actions the user has explicitly requested. Do not access customer data unless asked." This is the version that fails first. Models don't reliably follow negative instructions under adversarial input, and prompt injection can override the guardrail entirely. The prompt layer is a hint, not a boundary. Treat it that way.

The tool-definition layer. Some teams try to enforce scope by curating which tools the agent can see — a smaller tool registry for lower-trust contexts, a bigger one for higher-trust. This is better than the prompt layer because it's deterministic, but it's still coarse. Tool-level gating can't express "this user can call update_contact but only on contacts in their own team." The moment a tool takes an ID as a parameter, tool-level gating stops being sufficient.

The runtime layer. The agent picks a tool and fills in parameters. Before the call executes, the runtime resolves the authenticated user's identity, checks the tool's declared capability against the user's permissions in the target product, and checks the requested resource against what that user can actually see. Anything that fails a check is denied — not surfaced to the model with a helpful error the model could try to route around, but denied at the layer the model can't reach.

That last point matters. If your "authorisation check" happens inside a tool implementation the model can call variants of, the model can find variants that skip the check. The check has to sit at the runtime boundary, outside the model's reachable surface. This is the same argument we made about AI agent guardrails — the wrong layer usually gets the blame.

The three enforcement patterns, honestly compared

Teams building this today land on one of three architectures. Each has real trade-offs.

Pattern 1: Static scopes on a shared service account

One OAuth client, one token, a set of scopes granted at install time. The agent runs "as the integration."

What works: fastest to build, one auth flow, one credential to rotate.

What breaks: no per-user audit, no way to honour user-level permissions inside the target product, blast radius is the entire tenant, and every downstream security review demands remediation. This is the pattern most PoCs ship with and most production deployments regret.

Pattern 2: Delegated OAuth with fine-grained scopes

Each user authenticates the agent to act on their behalf. The token carries their identity and a set of scopes declared at consent time.

What works: audit trails resolve to real users, the target product's own permission model does most of the resource-level enforcement for free, and blast radius is bounded by what that specific user could already do.

What breaks: scope design is hard — too coarse and you're back to shared-account problems inside a single user's session, too fine and consent screens become unusable. Token refresh, revocation, and the mechanics of holding user identity across long-running agent trajectories are non-trivial. See OAuth for AI agents and MCP server authentication for the mechanics.

This is the pattern that actually holds in production, but only if the runtime does the heavy lifting.

Pattern 3: Just-in-time capability tokens

Before each tool call, the runtime mints a short-lived credential scoped to exactly that call — this user, this tool, this resource ID, valid for the next few seconds. The tool call executes against the credential and it's discarded.

What works: the boundary is as tight as it gets. Even if a credential leaks, it's useless within seconds and can't be replayed against a different resource.

What breaks: it requires infrastructure most SaaS products don't have — a token-minting service, a runtime that can issue and verify short-lived credentials, and a target API that accepts them. This is where the industry is heading, but most teams aren't there yet.

The honest recommendation

Start with pattern 2. Get delegated auth right, let the target product's permission model do the resource-level work, and put the enforcement point at the runtime layer — not the tool implementation, not the prompt. Move toward pattern 3 as the ecosystem matures.

Pattern 1 is a scale trap. It works until the first security review and then costs a quarter to unwind.

What production-grade least privilege actually looks like

Put the pieces together and a working model has six properties. Miss any of them and the boundary leaks.

  1. Identity flows end-to-end. The authenticated user's identity is carried from the agent, through the runtime, to the tool call, and into the target product's authorisation check. No hop collapses it to a shared account.
  2. Capability is declared at the tool, not the token. Each tool declares what capability it needs. The runtime checks that the calling user has that capability in the target product before the call executes.
  3. Resource authorisation happens at call time. When a tool takes an ID, the runtime verifies the user can see that specific resource — not a class of resources, that one. This usually means calling the target product's own authorisation surface, not maintaining a parallel model.
  4. Credentials are short-lived and task-scoped. Even if you're on pattern 2, tokens should live for the shortest window the target API allows, refresh transparently, and revoke on task completion.
  5. Every call is attributable. Audit logs record the user, the tool, the parameters, the authorisation decision, and the outcome. Not "the integration called update_contact." This user called this tool with these parameters and got this result.
  6. Denials are structured. When the runtime denies a call, it returns a structured error the agent can reason about without leaking why. "Not authorised for this resource" — not "user 42 doesn't have the admin role on team 7." Least privilege includes least information.

Most projects get one or two of these. Getting all six requires a runtime layer that's designed for it — which is exactly where the market is missing infrastructure.

How Pontil fits

This is the boundary Pontil's runtime is built around. Tool calls execute as the authenticated user, not a shared service account. Identity flows end-to-end from the agent through the runtime to the target product's authorisation model, so permissions, data visibility, and audit trails honour the real user. Denials happen at the runtime layer — outside the surface the model can reach.

We're a Tools-as-a-Service platform, which means the runtime, the tool contracts, and the identity boundary are one system rather than three teams' worth of integration work. For established SaaS companies whose agents span multiple products and multiple user permission models, that consolidation is usually what makes least privilege affordable to ship instead of a quarterly aspiration.

What holds when the agent does something you didn't plan for?

The test of a least-privilege model isn't whether it works on the happy path — it's whether it holds when the agent composes tools in an order no one wrote down, on data no one thought about, at 3am on a Sunday. Static policies fail that test. Prompt-layer rules fail that test. Shared service accounts fail it before the first tool call.

What holds is a runtime that treats every call as its own authorisation event, resolves the user's real permissions in the target product, and denies anything outside them without asking the model's permission. That's not a nicer version of the old model. It's a different layer entirely — one that most agent projects will discover they need after security review sends the first design back.

The good news: the boundary is buildable today with delegated OAuth and disciplined runtime enforcement. The harder news: it's not something you retrofit. Teams that put the boundary in from the start ship agent projects that survive audit. Teams that don't spend the second half of the project rebuilding what they should have built first.

Join our weekly newsletter

Stay up to date on the ever changing agentic landscape.

POSTS

Related content

Agent infrastructure

Platform integration

Agent identity vs user identity: the boundary security reviews will demand

5 minute read

Agent infrastructure

API strategy

API security best practices for AI agents: the boundaries that actually hold in production

9 minute read

Agent infrastructure

Platform integration

Agent authentication methods compared: API keys, OAuth 2.1, and delegated access

7 minute read