Zero standing privileges (ZSP) means that sensitive access to production systems exists only while someone is using it. Outside an approved task, no engineer, service account or AI agent can write to the production database, open a root shell or assume an admin role. The model is also called zero standing access, or zero standing privilege in the singular.
What are zero standing privileges?
Zero standing privileges is the target state of an access program: every privilege that could change production or expose sensitive data stays off until a specific task needs it, then turns off again without anyone having to remember. A standing privilege is the opposite: access that remains usable whether or not anyone needs it right now.
ZSP applies to human and machine identities alike. In practice, persistent access hides in three places, and ZSP has to cover all three:
- Permissions. Roles, grants and group memberships that stay assigned after the work that justified them is done.
- Credentials. Shared database passwords, SSH keys and API keys work for anyone who holds them, whatever that person’s roles say.
- Agent environments. A secret placed in an AI agent’s environment is inherited by every session the agent runs.
Zero standing privileges doesn’t mean zero access. People keep the access their daily work needs, such as read access to a staging database, and a few break-glass paths stay ready for emergencies. What changes is that production writes, root shells and admin roles each need a reason, an approver and an expiry.
Teams that remove every permission at once get flooded with requests, and approvers start saying yes without reading. Start with the privileges that do the most damage when misused, and move those first.
What is a standing privilege?
A standing privilege is an access right that stays active between uses. Someone granted it for a good reason once, and nothing takes it away when that reason ends. Most of them sit on privileged accounts, and in engineering teams they usually look like one of these:
- A Postgres superuser password in a shared team vault, so everyone who can open the vault can write to production.
- A Kubernetes
cluster-adminbinding added for a migration and never removed. - An SSH key on a bastion host that reaches every production server.
- A long-lived cloud access key saved in a CI pipeline’s settings.
- A
DATABASE_URLwith write rights in a coding agent’s environment, available to every session the agent starts.
Most of these exist because they were the quickest option when someone set them up. The problem is that each one keeps working at any hour, for whoever holds it.
Why are standing privileges a risk?
A standing privilege turns any single failure, a phished password, a leaked key or a buggy script, into production access, and an attacker who takes over the account inherits it without asking anyone. Four effects matter most:
- Exposure window. A standing privilege can be used every hour the account exists; a time-bound grant only during the window someone approved.
- Stolen credentials. A leaked password or key with standing rights works until someone notices and rotates it. Without an active grant, the same credential reaches nothing sensitive.
- Missing reasons in the audit trail. Logs show what a standing account did, not why it had the access. A time-bound grant records who asked, why, and who approved.
- Access reviews that never end. Standing grants pile up, and someone has to recertify each one. Grants that expire on their own leave far less to review.
AI agents raise the stakes. An agent runs unattended, acts quickly and may follow instructions planted in a ticket, a document or a web page it reads (prompt injection). If it holds standing database credentials, one poisoned input can become a production query with no person in the loop to notice.
Zero standing privileges vs just-in-time access vs least privilege
Least privilege, zero trust, JIT access and ZSP show up together and answer different security questions. One example runs through the table: an engineer needs to run a schema migration on a production Postgres database.
| Term | What kind of idea it is | Where it’s defined | Applied to the production migration |
|---|---|---|---|
| Least privilege | A design principle | NIST: access limited to the minimum needed for assigned tasks | The migration role can alter the tables it changes, not drop the database |
| Zero trust | An architecture | NIST SP 800-207: no implicit trust from network location; access decided per request | Being inside the VPC grants nothing; the connection and each query are checked against the engineer’s identity |
| Just-in-time access | An access model | A privilege granted for one task and a limited time | A teammate approves two hours of write access, and the grant expires on its own |
| Zero standing privileges | A target state | No sensitive privilege stays active outside a task | Outside those two hours nobody holds write access to that database, and there’s no shared superuser password to fall back on |
Zero trust and least privilege are related, not interchangeable. Least privilege is a rule for sizing access: give each identity the minimum it needs. Zero trust is a way of deciding access: NIST describes it as enforcing accurate, least privilege per-request access decisions while treating the network as compromised. Zero standing privileges adds that nothing privileged stays on between the requests that need it.
Just-in-time access is the request, approve and expire loop that grants one privilege for one task; the just-in-time access guide covers how that loop works. ZSP is broader. A team can run JIT for roles and still have standing access through a shared password, an agent secret or a grant someone created without an expiry. ZSP is reached when those are gone too.
How to reduce standing privileges
Reducing standing privileges is a sequence, not a switch. This order removes the most risk first:
- List what’s standing, in all three places. Roles and grants first, then credentials (shared passwords, SSH keys, API keys, agent secrets), then grants with no expiry. Note how often each one is used: rarely used privileges are the easiest to move.
- Take credentials away from people and agents. Put the upstream credential behind a broker or a proxy, so engineers and agents authenticate as themselves and never see the password. A shared password defeats every other step on this list.
- Deny the sensitive action by default at the resource. Block production writes, root shells or admin calls for everyone, then treat each approved grant as an exception to that block. A forgotten exception expires into a deny, not into a leftover role.
- Make every exception expire. Each grant gets a duration and a scope: one identity and, where possible, one resource or one session. The JIT implementation steps cover durations, approvers and break-glass planning.
- Name what stays standing. Baseline read access and break-glass accounts remain. Write them down, give each one an owner, and review that short list instead of every grant.
- Measure what’s left. Track how many standing privileges remain in each of the three places, and how long requests wait for approval. Slow approvals push people back to shared credentials.
What does zero standing privilege mean for AI agents?
For an AI agent, a standing privilege usually means a secret in its environment: a database URL, an API key or a cloud credential that every session can use. The agent needs that access for a few minutes of a task, holds it all day, and afterwards nobody can tell which person a given query was for.
Zero standing privilege for agents changes four things:
- No stored credential. Each session gets a short-lived OIDC token from the agent’s platform, and that token carries the identity of the person who launched it.
- Blocked by default, granted per session. A policy blocks the agent. When it needs data, the requester who launched the session says yes or no in Slack, and a yes covers that session only. A new session needs a new approval.
- A separate identity creates the grant. In Formal’s Devin guide, a dedicated workflow machine user creates the policy exception, so Devin can never create its own.
- Request fields don’t become policy. The same guide keeps fields Devin supplies out of the exception’s input condition, because Devin controls its request payload and could inject a condition of its own.
The Devin integration summarizes this setup, and the Devin setup guide has the full policy, Permission and workflow. Masking and read-only policies keep applying after approval, so an exception lifts one block, not every control. For how agents request access in a JIT loop, see JIT access for AI agents.
Formal reached the same conclusion running its own cloud agents. Describing the lesson from that internal use in a post on giving cloud agents production access with OIDC, Formal engineer Aditya Saligrama writes that “agent access should be modeled as a temporary delegation”, not as one more machine workload holding one more long-lived secret.
How does Formal approach zero standing privileges?
Formal removes standing privileges at the Formal Connector, a reverse proxy that sits between people or agents and the resources they reach. Each of the three places above maps to a documented feature, and together they form part of Formal PAM, Formal’s privileged access management product.
Credentials stay with the Connector
With Native Users, the credential lives with the Connector: it signs in to the database or server on the user’s behalf, and the person or agent never sees or sends the password. The credential is a value stored encrypted in Formal, an environment variable on the Connector, or the output of a hook that runs at connection time, for example to fetch a password from Vault.
One limit to know: the docs describe updating static credentials yourself or fetching them with a hook. They don’t describe automatic rotation of upstream passwords.
Privileged actions are exceptions to a default block
A policy blocks the sensitive action, and a policy suspension lifts that one policy for one user or group. A one-off suspension covers the first matching request; a time-bound one covers every matching request until it expires. A CEL input condition can narrow it, for example to one resource, and each suspension stores a reason for the audit trail.
Formal removes expired suspensions automatically, and deleting a suspension revokes it immediately. A suspension created without an expiration still expires, after 24 hours, so a forgotten exception can’t turn back into standing access.
Approvals run where people already work
A suspension comes from an approved Access Request, from a Workflow, or from the CreatePolicySuspension API. A workflow is a YAML definition you can edit in the Control Plane or manage as code with Terraform or the API. Its ask-in-chat action sends a Yes/No message in Slack and pauses the workflow until someone answers; a later step creates the suspension.
Identities expire too
Temporary users are deleted automatically after a set time, which fits contractors and other time-limited access. Formal access tokens are valid for 7 days by default, and the period is configurable. That’s shorter-lived than a shared password, but a token isn’t a per-task grant: the per-task control is the suspension.
Cursor’s case study reports the outcome in one line: no standing production access. “Engineers request just-in-time access for a specific operational need instead of holding permanent production permissions.”
“With Formal, we can finally manage least privilege access across our entire infrastructure through a single control plane.”
— Tom Daniels, CISO @ Cursor
To see how these pieces fit with inline masking, session logs and the resources the Connector supports, read about Formal PAM.