# Zero standing privileges make AI agent access safer

> Zero standing privileges limit AI agents with JIT credentials, bound approvals, and audit trails that connect each request to its exact effect.

AI agents should start each task unable to touch production, customer data, source control, billing, or internal administration. When an agent needs access, a policy service should grant a narrow credential for one bounded action, then let that credential expire. That is what zero standing privileges means in practice. It is an operating model, not a nicer name for rotating a permanent API key.

The hard part is not setting a token lifetime. A safe design has to decide what the agent may do, whose authority it borrows, when a human must approve the action, what changes between approval and execution, and which evidence survives afterward. I have seen teams focus on the credential broker and leave those decisions implicit. They end up with short-lived credentials that still authorize an entire account, approvals that amount to a blank check, and logs that prove a token existed but cannot explain what it changed.

## Standing access turns every prompt into an authorization decision

A permanently privileged agent makes prompt handling part of your security perimeter. Any untrusted issue, email, document, tool result, or web page can influence the agent while the credential waits in memory or the environment. The model does not need to reveal the secret for damage to occur. It can simply call the allowed tool with the wrong arguments.

This is why secret storage and authorization are different controls. A vault protects credential material. It does not make a broad credential narrow, connect its use to a task, or decide whether a refund, deployment, or user export was intended. Moving an administrator token from a configuration file into a vault improves custody, but the agent still has standing privilege if it can retrieve that token whenever it wants.

Zero standing privileges removes that default authority. The agent keeps an identity that can authenticate to an access broker, but that identity cannot directly perform business actions. It requests a capability for a specific resource and operation. Policy evaluates the request against task context, risk, environment, and any required approval. The broker issues a temporary credential only after those checks pass.

NIST SP 800-207 says zero trust should grant no implicit trust from network location or asset ownership and should authenticate and authorize before a resource session begins. That principle is useful, but an agent forces a sharper interpretation: authentication proves which workload made the request, while authorization must describe what this particular run may cause. Treating a known agent identity as sufficient trust recreates the static perimeter around a service account.

The consequence is blunt. If an agent can wake up at 3 a.m. and deploy, delete, transfer, invite, or export without obtaining fresh authority, it still has standing privilege. A token that rotates every hour does not change that when renewal is automatic and policy asks no new question.

## Define the access unit before choosing a broker

The smallest useful access unit combines an actor, task, action, resource set, constraints, and time window. Teams often begin by buying or building a credential broker, then discover that their application exposes only roles such as admin and editor. The broker cannot create precision that the target system does not understand.

Write the grant as data before discussing vendors. This simplified request is concrete enough for a policy review:

```yaml
request_id: req_7f31
agent_id: release-agent-prod
task_id: change_1842
requested_action: deployment.promote
resources:
  service: checkout-api
  environment: production
constraints:
  artifact_digest: sha256:91c2...
  source_environment: staging
  max_executions: 1
requested_ttl_seconds: 600
```

Every field closes a different escape route. `agent_id` identifies the workload, while `task_id` connects access to the business request. The action avoids handing over a general deployment role. The resource fields stop the credential from reaching another service or environment. The digest binds authority to reviewed code, and `max_executions` blocks reuse after the intended promotion.

The distinction between a role and a capability matters here. A role describes a continuing relationship, such as production operator. A capability describes permission to perform one operation on one target under stated conditions. You may implement the capability with a cloud session, a database token, a signed request, or a brokered API call. The security property comes from the boundaries, not the token format.

Do not force every system into perfect row-level or command-level authorization on day one. Put a narrow proxy in front of a coarse API when that is faster and easier to verify. For example, the agent can call `promote(artifact_digest, service)` while the proxy owns the broader deployment credential. The proxy validates the approved digest and service, performs the fixed sequence, and refuses arbitrary command execution. This often removes more risk than issuing a temporary version of the same administrator credential.

You also need an explicit nondelegation rule. An agent that receives a task capability must not mint a second credential for another agent unless policy approved that chain. Otherwise a planner can pass authority to a worker that has different instructions, tools, or isolation, and your original decision no longer describes the actor that creates the effect.

Turn every grant shape into executable policy tests before enforcement. Test the expected allow case, then change one field at a time: swap production for another environment, request a second service, increase the lifetime, omit the task ID, reuse the approval, alter the artifact digest, and call after expiration. Each mutation should produce a named denial reason. These tests catch a dangerous class of policy that looks precise in review but ignores one of the supplied attributes during evaluation.

Test target behavior separately from broker behavior. A broker may issue exactly the right claim while an integration maps it to a shared administrator session or fails to check the audience. Use a disposable test resource, present grants with the wrong action and resource, and confirm that the target rejects them. Then inspect the resulting audit events. A denied call that disappears from the target log leaves responders unable to distinguish enforcement from a network failure.

Keep the policy repository under the same change discipline as application code. Require review from someone who understands the target action, run mutation tests in continuous integration, and attach a version or digest to each decision. Emergency exceptions need an owner, a reason, and an automatic expiration. Permanent exception lists quietly become a second role system, usually with less review than the first one.

## JIT credentials should be narrow, short, and disposable

A just-in-time credential should exist only after policy approves a request, carry the least authority the task needs, and become useless quickly without renewal. All three properties matter. A five-minute administrator token is broad. A read-only token that lasts a year is standing access. A narrow ten-minute token that renews silently forever is also standing access.

A typical issuance path has six decisions, even if the user sees one button. The agent authenticates with its workload identity. It submits the structured access request. Policy checks the agent, task, resource, action, environment, and risk tier. An approval service collects a human decision when policy requires it. The broker exchanges the approved request for a target credential. The target service enforces the credential and returns an effect that can be reconciled with the request.

OAuth 2.0 Token Exchange, RFC 8693, defines an HTTP and JSON protocol for exchanging one security token for another and supports delegation through an actor token and the JWT `act` claim. That gives teams a standard vocabulary for the subject and current actor. The RFC also warns through its semantics that impersonation and delegation are not the same: if a token makes the agent indistinguishable from a user, investigators lose the actor boundary. Preserve both identities whenever the target permits it.

Set the lifetime from the operation, not from a universal company default. A database read might need two minutes. A deployment may need fifteen because the target platform polls slowly. Long-running analysis should receive separate read grants or renewed grants with fresh policy checks, not one credential that remains powerful throughout the job. Put a hard maximum in policy so the agent cannot ask for a day because its plan says the task may take a while.

Delivery matters as much as issuance. Prefer a brokered call or an in-memory credential passed directly to a constrained tool process. Do not place the token in the prompt, conversation history, task description, shell profile, shared workspace, or verbose tool output. Redact it from exceptions and tracing. A short lifetime reduces exposure, but a leaked token can still do its full job during that window.

Treat renewal as a new authorization decision. The broker should confirm that the task remains open, the approval has not been withdrawn, the resource and artifact have not changed, and the previous grant did not already reach its execution limit. Renewal that checks only a refresh token turns JIT issuance into permanent access with extra network calls.

## Approval belongs at the risky action

Human approval should bind to the exact action and inputs that will create an irreversible or externally visible effect. Asking someone to approve an agent session at login is too early. The agent may form a different plan after reading new data, and the approver cannot assess arguments that do not yet exist.

Divide tool operations by consequence. Low-risk reads can run automatically within a narrow scope. Preparation actions can build a migration, deployment plan, refund proposal, or account-change preview without committing it. Commit actions need explicit approval when they affect production, money, customer access, sensitive exports, or destructive state. This split keeps humans away from routine exploration while placing them at the point where judgment matters.

An approval card needs enough detail for a decision: actor, task, target, operation, important arguments, expected effect, expiration, and a stable digest of the proposed payload. Show the difference from current state for configuration or code changes. If the underlying artifact, destination, amount, recipient, or command changes, invalidate the approval. Never let the interface say only `Allow agent to continue?`

Approval and execution must be cryptographically or transactionally bound. A practical approval record might contain `approval_id`, `request_digest`, `approver_id`, `decision`, `decided_at`, and `expires_at`. The broker accepts it only for the matching request digest and records the approval ID in the issued grant. This prevents an agent from securing approval for a harmless preview and substituting a riskier payload afterward.

Do not require approval for every tool call. That popular policy feels safe because a person remains in the loop, but repeated low-information prompts train people to click approve. Move routine, reversible actions into preapproved policy with strict scope. Escalate novel destinations, high-impact changes, policy exceptions, and sensitive data access. A useful approval queue is quiet enough that a human treats each item as a decision rather than an interruption.

Give approvers a deny path that teaches the system nothing automatically. A denial reason can help an operator or policy owner revise the request, but the agent should not repeatedly mutate arguments until it slips past a tired reviewer. Rate-limit retries, preserve related request IDs, and send materially changed requests through a fresh review.

## A useful audit trail joins intent to effect

An audit trail must connect the original task, policy decision, human approval, credential issuance, tool call, and resulting state change. Separate logs from the identity provider, agent framework, and application rarely answer a simple incident question unless they share durable correlation identifiers.

Record structured events at each boundary. The following JSON Lines shape is small enough to adopt without buying a new log platform:

```json
{"event":"access.decision","request_id":"req_7f31","task_id":"change_1842","agent_id":"release-agent-prod","action":"deployment.promote","resource":"checkout-api/production","request_digest":"sha256:4ab1...","decision":"allow","policy_version":"prod-agent-v12","approval_id":"apr_2901","occurred_at":"2026-08-09T14:03:11Z"}
{"event":"access.effect","request_id":"req_7f31","grant_id":"grt_8820","target_event_id":"deploy_6627","result":"succeeded","effect_digest":"sha256:9dd0...","occurred_at":"2026-08-09T14:06:42Z"}
```

`request_id` follows the access attempt, and `task_id` groups the wider job. `grant_id` identifies the issued authority without logging the secret. `request_digest` proves what policy and the human reviewed. `target_event_id` lets an investigator find the authoritative application event. `effect_digest` captures the normalized result when the target cannot offer a stable event identifier.

Log denials and expired requests too. They reveal agents that ask for excessive scope, broken plans that keep retrying, and policy rules that force unnecessary escalation. Do not log raw prompts, credentials, full customer records, or arbitrary tool output by default. An audit system can create a second sensitive-data store if nobody defines field-level collection and retention.

Protect audit integrity with append-only storage, restricted writers, synchronized clocks, and a documented retention period. Export policy versions or immutable policy hashes so you can reconstruct why a decision passed at that time. A current policy file is weak evidence after twelve edits.

Test the trail as a product. Pick one production effect and ask an engineer who did not build the system to identify the initiating task, agent, policy version, approver, grant, exact request, and target result. If that takes an afternoon of joining timestamps by hand, you have telemetry, not an audit trail.

## Failures collect at the seams

The most instructive failure starts with a valid request and ends with an effect that nobody approved. Imagine a release agent preparing version A, producing a digest, and receiving approval to deploy it. Before execution, a build hook updates the artifact to version B under the same mutable tag. The broker issues a ten-minute deployment credential for the service and environment, but the target accepts any artifact. The agent deploys B. Every component worked according to its local rules, while the system violated the human decision.

Binding the approval to an immutable artifact digest fixes that path only if the target verifies the same digest. Checking it in the approval UI but passing a mutable tag to the deployment API creates decorative assurance. The enforcement point must receive or derive the approved value, compare it immediately before the effect, and reject a mismatch.

A second seam appears between credential scope and tool scope. The token may permit writes only to one repository, yet the shell tool that holds it can read the whole filesystem and send data to arbitrary destinations. Least privilege at the API does not contain the process. Isolate high-risk tool execution, restrict egress where practical, mount only required files, and prevent sibling tasks from sharing memory, caches, or environment variables.

Replay is another seam. An approval may be single use in the user interface while the broker accepts the same signed approval repeatedly until expiration. Make the broker consume a nonce atomically when it issues the grant, and make the target reject a second execution when the operation supports idempotency keys. `max_executions: 1` is a policy statement; a shared counter or target guarantee has to enforce it.

Watch for fail-open behavior during outages. If the approval service or policy engine is unavailable, production writes should stop or follow a narrowly documented emergency procedure. Falling back to a cached broad credential preserves availability by discarding the control you built. Read-only paths may use carefully bounded cached decisions, but write paths need an explicit risk choice rather than a hidden fallback.

Agent retries create another boundary failure. A tool call can time out after the target commits the change but before the agent receives the response. The agent may conclude that nothing happened and request a new grant. Require an idempotency key derived from the task and intended effect for operations that support one. When the target cannot enforce idempotency, the tool should query authoritative state before retrying, and policy should send ambiguous high-impact outcomes to a human instead of allowing blind repetition.

Identity confusion also appears when an agent works for several users or tenants. A workload identity tells the broker which process is calling, but it does not prove which customer context the task may access. Carry the initiating subject and tenant as separate signed claims, bind them to the task, and enforce both at the target or proxy. Never accept a tenant identifier solely because the model placed it in a tool argument. Resolve it from trusted task context and compare it with the requested resource.

Queued work deserves the same scrutiny as direct calls. If a temporary credential authorizes the agent to publish a message that a permanently privileged worker consumes later, the queue has moved the standing privilege rather than removed it. Put the bounded task context, action, resource, expiration, and request ID in a signed job envelope. The worker should validate the envelope at execution time and refuse expired or already consumed jobs. Do not assume that authorization at enqueue time remains valid after a long delay.

Finally, separate policy errors from model errors during review. A model can choose the wrong operation within a perfectly enforced grant, and a policy can authorize too much even when the model follows instructions. Capture enough structured context to identify which boundary failed. Otherwise every incident becomes an argument about prompt quality, and the access flaw survives the next model update.

## Revocation must beat the agent's next move

A kill switch is useful only if it stops new grants immediately and limits already issued authority within a known interval. Teams often expose a dashboard toggle that disables the agent scheduler while its current tool process and cloud session continue running. That stops future jobs, not the active incident.

Design revocation in layers. Disable new issuance for the agent identity, task, action, or resource. Cancel queued approvals and mark existing approvals unusable. Terminate brokered sessions and tool workers. Revoke target credentials when the target supports it. Apply a temporary deny at the target or network boundary for credentials that cannot be recalled. Each layer covers a different race.

Short expiration is the reliable backstop, so measure worst-case residual authority rather than nominal token lifetime. A credential valid for ten minutes can remain useful longer if the target exchanges it for another session, queues work for later, or begins a long transaction before expiry. Trace downstream sessions during design and include them in revocation tests.

Run a drill with an active agent, not a tabletop description. Start a permitted long-running operation, trigger revocation, then measure when new requests fail, when the current process stops, whether queued effects execute, and which audit events appear. Record the maximum containment time for each integration. If the team cannot state that number, it cannot make a credible incident promise.

Keep a manual emergency path for incidents where the broker itself is compromised. It should use separate administrator identity, independent authentication, and direct controls at major targets. Store the procedure where responders can reach it without the affected agent platform. Test it rarely enough to protect it and often enough that it works.

## Roll out by shrinking privilege, not chasing perfection

A useful rollout starts with the agent actions that combine broad credentials and serious consequences. Inventory agent identities, where their secrets live, which tools receive them, target permissions, renewal behavior, and owners. Runtime observation usually finds credentials and call paths missing from architecture diagrams.

Choose one workflow with a clear prepare and commit boundary, such as production deployment, customer export, or payment refund. Keep preparation automatic. Put a broker or constrained proxy in front of the commit call, define the request schema, issue a short-lived grant, and correlate the target effect. This creates an end-to-end control slice that the team can attack and measure.

Run policy in observation mode before enforcement when existing behavior is poorly understood. Produce allow, deny, and approval-required decisions without blocking, then compare them with actual calls. Observation is not a months-long excuse. Set an owner and a date for enforcement, and treat unmatched agent activity as a defect.

Measure controls that expose residual risk: percentage of sensitive actions using standing credentials, maximum grant lifetime, grants with resource and action scope, approvals invalidated by changed payloads, effects lacking a correlated request, and revocation containment time. Request volume alone says nothing about safety. A falling approval count can be good if policy absorbed routine reads, or bad if a broad role bypassed the broker.

Assign an owner to every standing credential that remains during migration. Record the target, scope, storage location, consumers, rotation method, last observed use, replacement plan, and removal date. Alert on use outside the documented workflow and after the planned removal. Unknown ownership should increase priority, not justify keeping the secret indefinitely.

Review those exceptions in an operational meeting where application and platform owners can remove blockers. A security spreadsheet alone will age quietly because the people who can change the target API do not see it. Close an exception only after runtime evidence shows the old credential is no longer used, then revoke it and test that the intended agent workflow still succeeds. Configuration search cannot prove that a copied secret has disappeared from every worker or queue.

During a Team & AI Audit, I look for this control path alongside engineering throughput because adding agents without changing access design makes a smaller team faster at both good and bad changes. The practical goal is a team that can use Claude Code, Codex, MCP tools, or multi-agent pipelines while keeping production authority explicit and reviewable.

Do not wait for every vendor API to support perfect scopes. Remove static credentials from the easiest high-impact path, wrap coarse systems, and document exceptions with owners and expiration dates. Each migrated action should leave the agent with less ambient authority than it had before.

## Zero standing privileges changes who owns access

Zero standing privileges works when application owners define safe actions, security owners define policy and evidence, platform owners operate issuance, and workflow owners decide where human judgment belongs. If one security team tries to infer all of that from generic roles, the project stalls or produces a broker that hands out the same broad access on a timer.

Make the application interface carry business meaning. `refund_order(order_id, amount, reason)` supports better policy and approval than `POST arbitrary URL`. `promote_artifact(digest, service, environment)` is safer than a shell credential. Purpose-built actions cost engineering time, but they also reduce prompt ambiguity and make failures easier to contain.

Keep the agent identity separate from the requesting user and the approving user. One person may initiate a task, another may approve its effect, and an agent may execute it. Your audit trail should preserve all three without pretending the agent was the person. Separation also lets policy block self-approval and identify automation-specific failure patterns.

The first policy question for any new agent action should be simple: what authority does this process have before a task begins? If the answer includes a reusable production credential, shrink that access before raising autonomy. An agent that can reason for an hour can also make mistakes for an hour. Give it authority for the few seconds when a specific, reviewed effect needs to happen, then take that authority away.
