Zero Standing Privileges: What It Is, How to Implement It, and Why AI Agents Need It

- Share:





2938 Members
Standing permissions create a specific architectural defect: an identity holds access rights during the long interval between legitimate uses. The user is not actively approving anything. The workload is not executing a specific job. The agent is not in the middle of a bounded workflow. Yet the permission still exists. That gap between "this identity has permission" and "this identity is actively using permission right now for a known task" is the attack surface.
Zero Standing Privileges changes the default state. Instead of treating access as a durable property of an identity, ZSP treats access as a runtime consequence of a task. An identity begins with nothing. A grant is created only when there is a reason to act, only for the action required, only against the relevant resource, and only for the time window in which that work should occur. Then it disappears.
That sounds close to least privilege, but it is not the same idea with a stricter haircut. Least privilege asks whether the scope is too broad. ZSP asks why the permission exists when no work is happening. A role sitting unused in a directory group may be perfectly scoped and still be wrong.
Least privilege limits what an identity can do. Zero Standing Privileges limits when an identity can do it.
This distinction matters because many teams proudly satisfy least privilege while still leaving privileges standing all over the system. A service account may only be able to rotate one class of secrets. A developer may only be able to access one production namespace. An automation agent may only be able to open support tickets and read customer metadata. All of that can be least privilege. None of it is ZSP if the permission remains present between uses.

| Dimension | Least Privilege | Zero Standing Privileges |
|---|---|---|
| Core question | What is the smallest set of permissions this identity should have? | Why does this identity have any permission when no task is active? |
| Time model | Permissions may be long-lived if narrowly scoped. | Permissions are created just in time and expire automatically. |
| Typical implementation | Roles, groups, IAM policies, scoped service accounts, entitlement reviews. | Runtime authorization, JIT grants, ephemeral credentials, task-bound tokens, automatic revocation. |
| Failure mode | The permission is broader than necessary. | The permission exists longer than necessary. |
| Example violation | A developer has admin access when read-only access would be enough. | A developer has production read-only access all quarter instead of for the 20-minute debugging session. |
| Best use | Reducing blast radius by narrowing allowed actions and resources. | Removing idle privilege by making access temporary and task-bound. |
Most organizations need both. Least privilege without ZSP leaves stale access lying around politely, like a loaded tool in a locked cabinet where too many people still have keys. ZSP without least privilege can grant a short-lived permission that is still absurdly broad. A five-minute root token is still a root token. Duration does not redeem bad scope.
The useful model is simple: least privilege defines the maximum acceptable shape of access; ZSP decides whether access should exist at all right now.
In a ZSP system, the default permission set is empty. Not "empty unless the user is in the right group." Not "empty unless the service account has a baseline role." Empty.
Access is derived at runtime from several inputs that must agree with each other:
The grant is not the source of truth. The task is. The grant is a temporary artifact produced because policy decided that a specific identity may perform a specific action against a specific resource for a specific reason within a specific time window.
A task-bound token might look like this:
{
"iss": "https://auth.example.internal",
"sub": "agent:expense-reviewer-17",
"aud": "https://api.finance.example.internal",
"iat": 1779285600,
"nbf": 1779285600,
"exp": 1779286200,
"jti": "grant_01JZSP7Q2K9N4M6R8T3VY1P0XA",
"delegated_by": "user:maya.chen@example.com",
"workflow_id": "wf_invoice_reconciliation_8842",
"intent": "compare invoice totals against approved purchase orders",
"scope": [
"invoices:read",
"purchase_orders:read"
],
"resource_constraints": {
"tenant_id": "tenant_042",
"invoice_ids": ["inv_100391", "inv_100392"],
"max_records": 50,
"write_access": false
},
"consent_id": "consent_01JZSP6MZV9HR4Y4FDK3P7B2MN",
"grant_expires_at": "2026-05-20T15:30:00Z"
}
The important part is not that this is a JWT. The important part is that the token carries enough runtime context to make the authorization decision inspectable. A static role says, "this identity is a finance-reader." A ZSP grant says, "this identity may read these invoice records for this workflow, delegated by this human, until this timestamp." One is an entitlement. The other is a receipt.
A practical implementation usually follows this sequence:
read_invoice, approve_refund, rotate_secret, or query_customer_record. Roles are convenient packaging, but actions are what systems actually enforce.That last step is where many homegrown systems quietly cheat. They create temporary access and then let renewal, refresh tokens, local caches, or forgotten sessions turn it into standing access with better branding. If the permission can outlive the task without another policy decision, the system has drifted away from ZSP.
AI agents are a poor fit for old access models because they stress every weak assumption those models made about humans.
First, agents can be long-running. A human session often has an obvious beginning and end, even if the boundaries are imperfect. An agent may sit inside a workflow for hours, resume after a callback, retry failed steps, or continue operating after the user has moved on mentally. Persistent credentials in that environment are not "convenient." They are unattended authority.
Second, agents are cross-tool by design. A useful agent reads from one system, writes to another, calls a third for context, and stores intermediate output somewhere else. The credential graph becomes a small transit system. If every connector gives the agent a standing token, the agent becomes a credential bundle with a planning loop attached. That is not a security architecture. That is a shared credential with a poetry degree.
Third, agents amplify replay. A stolen human credential is bad. A stolen agent credential can be worse because the agent's normal behavior may already include rapid tool calls, retries, and machine-speed traversal across APIs. The suspicious pattern looks annoyingly similar to the intended pattern unless every grant is task-bound and short-lived.
Fourth, agents blur identity. The action is performed by software, but the authority often comes from a human, a workflow, or an organizational policy. Treating the agent as just another user loses the delegation chain. The more accurate model is: agentic identity = the delegating human + workflow context + intent. That definition matters because authorization needs all three. Remove any one of them and the decision becomes mushy.
For agents, ZSP is not an advanced hardening option. It is the difference between an agent that receives authority for a task and an agent that accumulates authority as a personality trait.
Before an agent receives a grant, the system should interrogate the request — not in the theatrical sense, but in the boring, precise, "prove why this permission should exist" sense.

The four questions are:
An implementation-level check can carry that full context into the authorization decision:
type AgenticContext = {
delegated_by: string;
workflow_id: string;
intent: string;
scope: string[];
consent_id: string;
grant_expires_at: string;
};
const agent = {
key: "agent:expense-reviewer-17",
attributes: { type: "ai_agent", runtime: "mcp_client" }
};
const resource = {
type: "invoice",
key: "inv_100391",
tenant: "tenant_042",
attributes: { sensitivity: "financial", environment: "production" }
};
const context: AgenticContext = {
delegated_by: "user:maya.chen@example.com",
workflow_id: "wf_invoice_reconciliation_8842",
intent: "compare invoice totals against approved purchase orders",
scope: ["invoices:read", "purchase_orders:read"],
consent_id: "consent_01JZSP6MZV9HR4Y4FDK3P7B2MN",
grant_expires_at: "2026-05-20T15:30:00Z"
};
const allowed = await permit.check(agent, "read_invoice", resource, { context });
if (!allowed) {
throw new Error("Runtime grant denied by policy");
}
const token = await issueTaskBoundToken({
subject: agent.key,
audience: "https://api.finance.example.internal",
action: "read_invoice",
resource: resource.key,
...context
});
The pattern is more important than the SDK shape. The authorization call is not asking, "is this agent generally trusted?" It is asking, "given this delegating human, this workflow, this intent, this action, this resource, this consent record, and this expiration, should a grant exist now?" That is the ZSP decision.
Permit manages authorization through a hybrid architecture: policy and authorization configuration are managed through the control plane, then synchronized via OPAL to local PDPs that evaluate access close to the workload. For MCP, the gateway is the natural enforcement point: every tool call already crosses a protocol boundary, so Permit can attach authentication, authorization, consent, and audit without requiring every upstream server to reinvent access control.
A ZSP enforcement stack for agents has four layers:
Gateway. The gateway is the enforcement point between the agent and the tool. It receives the tool call, extracts the actor, action, resource, workflow, consent, and temporal context, and refuses to forward the call unless policy allows it. For MCP systems, this is the natural choke point because tool invocation already passes through a protocol boundary.
PDP. The Policy Decision Point answers the runtime question: should this action be allowed now? Permit's PDP is designed to answer authorization queries using policy and contextual data, and can run near the application for local enforcement.
OPAL. OPAL keeps policy and authorization data synchronized so that changes can reach the enforcement layer without waiting for a redeploy. Permit describes OPAL as the administration layer that detects changes to policy and policy data and pushes updates to policy agents.
Audit logs. Every grant decision should create evidence: actor, delegating human, action, resource, workflow, consent, decision, and time. Permit MCP Gateway logs authorization decisions for tool calls automatically, with entries available for debugging, investigation, and compliance workflows.

Temporal access controls should be first-class policy inputs, not metadata stapled onto the side. A policy that cannot reason about grant_expires_at, nbf, exp, workflow age, consent age, or approval freshness is not enforcing ZSP. It is enforcing ordinary authorization and hoping the token layer cleans up afterward.
The hard part is not issuing a short-lived token. The hard part is deciding, consistently and at runtime, whether that token should exist. Permit gives teams the policy model, PDP, OPAL synchronization, audit logs, and MCP Gateway enforcement point needed to make ZSP operational instead of theoretical.
A clean Permit.io ZSP flow starts with actions, not roles. Roles may still exist, but they should be packaging for policy and administration, not the primary unit of runtime authority.
read_issue, create_ticket, query_customer, read_invoice, or trigger_deployment. The action name should map to something the agent can actually attempt.The key design choice is to avoid granting "agent access to GitHub" or "agent access to finance." Those phrases are too large to enforce safely. Grant the action. Bind the context. Expire the authority.
The common objection to ZSP is latency: if every action needs a runtime decision, won't the system get slow?
It can, if the architecture is careless. Sending every authorization request across a distant network path, recomputing context from scratch, and serializing the whole workflow behind a single policy service is a good way to make security unpopular. Engineers will route around a control that adds pain to every call. They should not have to.
The better pattern is local or edge PDP enforcement. Put the decision point close to the workload, keep policy data synchronized, and make the runtime check cheap. Permit documents a local PDP option for customer-controlled deployments, so authorization decisions can stay within the customer environment when that deployment model is needed.
Caching can help, but only if the cache key is honest. A ZSP authorization cache key must include at least:
identity
workflow_id
action
resource
scope
consent_id
grant_expires_at
If grant_expires_at is not part of the key, the cache can accidentally resurrect expired authority. If workflow_id is missing, one workflow can borrow another workflow's permission. If scope is missing, a narrow approval can be reused for a broader request. That is not a cache. That is a privilege laundromat.
Pre-approval can also amortize decision cost. A workflow may request a bounded set of grants at startup: read these five records, call these two tools, write only to this ticket, expire in ten minutes. The system still avoids standing privilege because the approval is task-bound and time-bound. It just avoids asking the same question 200 times in a loop.
ZSP can produce useful evidence for access governance, auditability, and sensitive-data access controls. It does not make a system compliant by itself, but it gives security and compliance teams event-level proof instead of relying only on periodic entitlement reviews.
A quarterly role review says, "this identity had this entitlement during the review period, and someone approved that it still made sense." That can be useful. It is also coarse. It does not prove that a specific invoice read, patient-record lookup, production query, or tool call was tied to a specific task with a specific approval at a specific time.
ZSP evidence is different. It can show that an identity had no default access, requested a grant for a defined purpose, received a policy decision, used the grant within its allowed window, and then lost the grant automatically. For SOC 2, that supports access governance and change accountability. For HIPAA-regulated systems, it supports tighter control and traceability around access to protected data.
The practical shift is from entitlement evidence to decision evidence. Quarterly reviews ask whether the directory looked reasonable. ZSP asks what actually happened. Auditors tend to enjoy systems that can answer the second question without archaeology.
The static-directory view of identity was built for a simpler world: users, groups, roles, resources. It still matters, but it is no longer enough. A modern access decision often depends on who delegated authority, what workflow is active, which agent is executing, what the intent is, which resource is targeted, whether consent exists, and whether the time window is still valid.
Zero Standing Privileges forces that reality into the architecture. It refuses to pretend that permission is a permanent trait of an identity. Permission becomes a temporary relationship between an actor, a task, a resource, and a policy decision.
That is especially important for AI agents, because agents do not merely hold access. They act. They chain tools. They retry. They transform one system's output into another system's input. Giving them standing privilege is an old mistake with a faster runtime.
The better default is empty. Let access appear only when work justifies it, constrain it to the task, and let it disappear without drama. A system that can do that has stopped treating identity as a static directory entry and started treating it as what it really is: a runtime relationship under policy.
Zero Standing Privileges is an access model where identities do not keep persistent permissions between uses. Access is granted just in time for a specific task, scoped to the required action and resource, and revoked automatically when the task or time window ends. The default state is no access.
Least privilege limits the scope of access, while ZSP limits the duration of access. A user can have a narrowly scoped permission that still remains active for weeks, which satisfies least privilege but violates ZSP. The strongest design uses both: narrow permissions that only exist when needed.
AI agents often run across multiple tools, execute long workflows, and act on behalf of humans or business processes. Persistent credentials give those agents authority even when no approved task is active. ZSP keeps agent access tied to delegation, workflow context, intent, and expiration.
A task-bound grant should include the requesting identity, delegating human or workflow, intended action, target resource, scope, consent or approval reference, and expiration time. It should also include constraints such as tenant, environment, record IDs, or read/write boundaries. Without that context, the system cannot reliably distinguish legitimate task execution from idle privilege reuse.
ZSP does not require eliminating roles, but it changes what roles are allowed to mean. Roles can still help organize policy, define ceilings, and simplify administration. They should not create permanent runtime authority by themselves; the actual grant should still be produced just in time for a task.
ZSP enforcement should use a nearby PDP, efficient policy synchronization, and carefully keyed caching. The cache key must include identity, workflow, action, resource, scope, consent, and expiration so that cached decisions cannot outlive or escape their approved context. For repeated steps inside one workflow, bounded pre-approval can reduce repeated checks while preserving task-based expiration.

Full-Stack Software Technical Leader | Security, JavaScript, DevRel, OPA | Writer and Public Speaker