Manav.id
Agents ยท 16 min read

You revoked the agent. Somewhere it is still running.

Revocation is an event in one system and a rumour everywhere else. The identity provider account goes dark in ninety seconds while the OAuth grant still refreshes, the CI token still builds, and the delegation a supplier verified last month still verifies. This is the mechanics of why, what the certificate world already learned the hard way, and how anchoring authority to a root changes the shape of the problem.

The contractor engagement ends on a Friday. At 4:02pm someone in IT clicks disable in the identity provider, the account goes dark, and the offboarding ticket closes with a green tick. Ninety seconds, start to finish. This is what a mature offboarding process looks like and the team is right to be pleased with it.

Three weeks later a supplier sends an email that begins "just confirming the order your system submitted on Tuesday". Nobody at the company submitted anything on Tuesday. What submitted it was a procurement agent the contractor had configured in March, running under a delegation the supplier verified once, holding an API key that was pasted into an environment variable, refreshing an OAuth grant that was issued by the supplier's own authorisation server rather than by the identity provider anyone thought to check.

The account was disabled in ninety seconds. The authority took twenty three days to notice, and it only noticed because a human wrote an email.

This gap has a name in this series. It is called Revocation Non-Propagation, and it is the reason "we disabled the account" is one of the least informative sentences in an incident review.

How do you revoke an AI agent's access across every system at once? Not by disabling the account. Disabling reaches the identity provider and whatever it directly controls, while OAuth grants, API keys, CI tokens and already issued signed tokens keep working. The durable fix is to anchor every delegation to a root key and publish a signed revocation of that root, so any verifier walking the chain rejects every descendant, offline, without needing to know the descendants exist.

What does "revoked" actually mean in practice?

Ask an engineer to revoke a person's access and they will do something specific and local. Ask five engineers and you will get five specific and local things, none of which is aware of the others. Revocation is not one operation. It is a set of independent operations across systems with different owners, and no participant can tell you whether the set is complete.

Here is the inventory that typically survives a disabled account. Read it as a representative picture from common enterprise stacks rather than as measured data, because the honest situation is that almost nobody measures this.

ArtifactWho issued itWhen it actually stops working
Identity provider sessionThe IdPSeconds to minutes. This is the part that works.
Downstream app session from SSOThe applicationAt session expiry, commonly eight to twenty four hours.
OAuth refresh token at a third party serviceThat service's authorisation serverWhen revoked at that service. Frequently never, because nobody asked.
Personal access token in a CI runnerA code hostOn manual rotation. Often outlives the person by years.
API key in an agent's environmentWhichever service issued itOn manual rotation, if anyone knows it exists.
Signed JWT already in flightAny issuerAt the expiry claim. Nothing can recall it before then.
Delegation a supplier verified last weekThe delegating partyWhen the supplier next checks, if it ever checks.
Token a sub agent minted for another sub agentAn agentUnknown. In many stacks nobody knows the token exists.

Only the first row is fast, and it is the only row most offboarding metrics measure. The bottom two rows are the ones that will appear in your next incident review, and they are the two nobody has an inventory of.

Vendor surveys and independent reporting have repeatedly put the share of former employees retaining access to at least one system at very high levels, in some write ups approaching ninety percent. Treat those figures as vendor and survey reported rather than established, because the sampling behind them is rarely disclosed and the vendors publishing them sell the remedy. The direction is not in dispute even if the magnitude is.

Why does disabling an account not revoke everything?

Because two desirable properties of modern authorisation are in direct tension, and every system in your stack has quietly chosen a side.

Stateless verification is fast, scalable, and cannot be undone

A signed token carries its own claims. The verifier checks the signature and the expiry and needs nothing else: no database, no network call, no shared state. This is why signed tokens took over. It is how a service handles a hundred thousand requests a second without a lookup, how a request survives a partition, and how one team's outage stops being every other team's outage.

The property that makes it fast is the property that makes it permanent. A verifier that consults nothing cannot learn anything. Once a token is signed and handed over, the only thing that will stop it is the clock. You have traded revocability for independence, usually without a meeting where anyone said so out loud.

Stateful checking is revocable and makes the issuer load bearing

The alternative is to ask. OAuth token introspection, specified in RFC 7662, lets a verifier ask the issuer whether a token is still good, and RFC 7009 defines how to revoke one. Revocation becomes immediate. The cost arrives in four instalments: a round trip on every verification, an availability dependency where the issuer's outage becomes everyone's outage, a bottleneck that grows with traffic, and a privacy leak where the issuer learns every place the token is used.

Most architectures split the difference by shortening lifetimes, which is not a resolution. It is a decision to accept a fixed window of unrevocable authority in exchange for not having to ask. That is a reasonable trade, and it is worth being precise that this is what it is.

The industry already ran this experiment, on certificates

This exact tension played out in public over two decades in TLS, and the history is worth knowing because it is not an analogy, it is the same problem with different nouns.

Certificates are signed statements with expiry dates, so revoking one early needs out of band machinery. The first attempt was the certificate revocation list: publish every revoked serial number, have clients download it. The lists grew unwieldy and clients fetched them rarely, so a revocation could take days to matter.

The second attempt was the Online Certificate Status Protocol, RFC 6960, which let a client ask about one certificate. This is textbook stateful checking, and it collected the textbook costs: a round trip added to connection setup, and the certificate authority learning which sites you visited.

Then came the flaw that should be printed above every architecture whiteboard. Because a blocked or slow responder would break browsing, clients adopted soft fail: if the responder is unreachable, accept the certificate. Consider what that means against an attacker positioned to intercept traffic. That attacker can also drop the status request. The check that exists specifically to stop them is the check they can switch off. For an active attacker, soft fail revocation checking provides approximately nothing.

The industry's eventual answer was not a better question protocol. It was to push compact, signed, aggregated status to clients ahead of time, so verification stayed local, offline, and fast, while still being revocable. Chrome's CRLSets and the general move to stapled and pushed status are that answer.

The lesson transfers exactly: do not make the verifier phone home. Give the verifier something signed, compact, and current, and let it decide locally. Anyone designing agent revocation today who reaches for online introspection is proposing OCSP in 2026, and should at least know they are about to rediscover soft fail.

What SCIM actually reaches

Automated deprovisioning is genuinely good and genuinely partial. It reaches applications you have connected to it, which in most estates is the applications procurement knows about and someone integrated. It does not reach the API key a developer pasted into a script, the personal access token in a CI runner, the OAuth grant a user approved directly with a service, or any authority an agent minted for another agent. Those categories are not edge cases in an agent estate. They are the majority of it.

What is revocation lag, and why does nobody measure it?

Revocation lag is the interval between the moment an organisation decides an identity should lose authority and the moment the last artifact carrying that authority actually stops working.

It deserves a name because named things get measured. Today the metric that gets reported to leadership is time to disable the account, which measures the first row of the table above and none of the others. It is a genuinely excellent metric for the one system that was never the problem.

The measurement is not hard, it is just nobody's job. Pick a departed identity from last quarter. For each artifact class, establish empirically whether authority still functions: try the refresh token, try the CI token, ask the supplier whether the delegation would still verify. Record the interval per class. The distribution you produce will be the first honest picture of your own exposure anyone in the organisation has seen, and the tail will be much longer than the leadership deck implies.

Two incident patterns show why the tail is the part that matters. In the campaign against Snowflake customer tenants, widely reported and analysed by incident responders including Mandiant, the credentials used were harvested from contractor machines and remained valid long after the engagements and the machines that leaked them had moved on. In the Shai-Hulud npm worm, analysed by Elastic Security Labs and others, rotating a compromised maintainer token was not sufficient, because the compromised credential had already been used to establish further persistence that survived the rotation. In both cases the local revocation was correct and the propagation was not.

Why do AI agents make this dramatically worse?

Three multipliers, and they compound.

Volume. One departing engineer used to mean a handful of artifacts. One departing engineer who ran an agent fleet means every artifact that fleet accumulated, which as covered in identity fan out can be two orders of magnitude larger.

Agents mint descendants. A human requests credentials through a process with a record. An orchestrating agent issues authority to a worker agent at runtime, at machine speed, with no ticket and often no log that anyone reads. Your inventory is now a moving target being updated by a program.

Nobody is watching the clock. A human whose access should have ended tends to stop using it, because they have a new job. An agent has no such instinct. It will keep executing its loop with whatever authority still functions, indefinitely, politely, at three in the morning, until something external stops it. The procurement agent in the opening scene was not malicious. It was diligent.

What is chain anchored revocation?

The structural fix is to stop treating each grant of authority as an independent fact and start treating it as a link with a traceable ancestry.

A delegation is a signed object. It names the key being delegated to, the actions in scope, the constraints, the validity window, a maximum chain depth, and a revocation identifier. A sub delegation is a new signed object, issued by the delegate, that can only narrow what it received, never widen it. Every object names its parent. The result is a chain that terminates at a human's root key, which is the machinery described in how delegation tokens work.

Once authority has ancestry, revocation gets a property it has never had: revoking an ancestor invalidates every descendant, including descendants you have never heard of. A verifier walking the chain encounters the revoked link and rejects, without needing an inventory of the leaves. This is the difference between hunting for every key a departed contractor's agents created, and publishing one signed statement that makes all of them fail closed.

def verify(chain, revoked_ids, now):
    """Walk leaf to root. Any revoked ancestor kills the branch."""
    scope = None
    for i, link in enumerate(chain):                 # chain[0] is the leaf
        if not ed25519_verify(link.parent_key, link.bytes, link.sig):
            return False, "bad signature at depth %d" % i
        if not (link.notBefore <= now <= link.notAfter):
            return False, "expired at depth %d" % i
        if link.revocationId in revoked_ids:         # local set, no network call
            return False, "revoked ancestor at depth %d" % i
        if i >= link.maxChainDepth:
            return False, "chain too deep"
        # authority may only narrow as it descends
        scope = link.scope if scope is None else (scope & link.scope)
    return (chain[-1].parent_key == HUMAN_ROOT_KEY), scope

Four checks, each closing a specific hole. Signature verification stops forgery. The validity window bounds every link independently, so a compromised leaf cannot outlive its grant. The revocation check is the subject of this post. The depth limit stops an agent from laundering authority through an arbitrarily long chain until nobody can audit it. And the scope intersection is what makes the whole structure safe to hand around, because authority strictly narrows as it descends: a worker agent can never hold more than the orchestrator that spawned it, which can never hold more than the human at the root.

The honest part: the verifier still needs a status source

Look at revoked_ids in that code. It is a local set, and something has to keep it current. Chain anchoring changes the shape of the problem in three ways that matter, and it does not make the problem disappear.

What changes: you check status for a small number of roots instead of an unbounded number of leaves, so the list stays compact. You do not need to enumerate descendants, so authority created by agents at runtime is covered by a revocation written before it existed. And the check is a local set membership test, so verification stays offline and fast.

What does not change: a verifier that never refreshes its list will honour revoked authority until it does. There are three defensible strategies, and the right answer is usually a mix rather than a religion.

StrategyHow it worksCostBest for
Short lived chainsDelegations expire in minutes or hours and must be reissuedReissue traffic and a hard dependency on the issuer being upHigh frequency internal calls where the issuer is close
Signed revocation listsEach root publishes a compact signed list, verifiers fetch on a schedule and apply offlineBounded staleness equal to the fetch intervalCross organisation verification, suppliers, offline settings
Status check on high value actionsCheap actions verify locally, expensive ones fetch fresh status firstLatency exactly where it is affordablePayments, privileged changes, irreversible operations

The third row is the one most teams should reach for first, because it puts the cost where the risk is. Nobody needs a fresh status check to read a dashboard. Everybody wants one before a wire leaves.

This mirrors the kill switch design already published here, with one difference worth being precise about. A kill switch revokes what you issued. Chain anchoring extends that reach to authority you never saw, provided it descended from a root you control. It does nothing about authority that never descended from anything.

Honest limits

Four, and the first is the big one.

On product status, to be exact: delegation chains with revocation identifiers and offline chain verification are shipped, and the delegation.revoked webhook is documented. Production grade webhook delivery and an enterprise revocation console are planned work rather than shipped features, and anyone evaluating this should ask that question directly rather than take a blog post's word for it.

A revocation readiness checklist

  1. Measure your lag once. Take one identity that left last quarter and test each artifact class for whether authority still functions. One afternoon produces a number nobody in your organisation currently has.
  2. Change the metric you report. Retire time to disable the account as the headline. Report time to last working artifact instead. Metrics select behaviour, and the current one selects for the easy row.
  3. Inventory what SCIM does not reach. List the systems holding authority that automated deprovisioning cannot touch. That list is your actual offboarding scope and it is probably the first time it has been written down.
  4. Find the agent minted credentials. For every agent framework in production, answer one question: when this agent grants authority to a sub agent, what record exists and who can revoke it? If the answer is silence, that is the highest priority item here.
  5. Put a fresh status check on irreversible actions. Payments, privilege grants, deletions, credential issuance. Everything else can verify against a cached list.
  6. Make new grants carry a parent. Do not try to fix the existing sprawl first. Require that authority issued from now on descends from something revocable, and let the loose keys age out.
  7. Ask suppliers one contract question. "If the delegation you verified is revoked, when will you notice?" A supplier who cannot answer has just told you their revocation lag is unbounded.
  8. Try it end to end in the last mile kill switch demo, and read the chain and revocation fields in the developer documentation.

The uncomfortable conclusion about short lived tokens

Short lifetimes are treated as the modern answer to revocation, and they deserve a harder look than they get.

A five minute token does bound the damage window, which is real value. But consider what a fleet of short lived tokens actually does: it re issues authority continuously, automatically, without anyone re deciding that the authority should still exist. The contractor's procurement agent from the opening scene would happily refresh a five minute token every five minutes for twenty three days. Short lifetimes would have converted one long lived unauthorised grant into six thousand short lived unauthorised grants, and the supplier would have seen a perfectly fresh credential every single time.

Short lifetimes bound damage from a stolen token. They do nothing about authority that should have ended, because the re issuance is automatic and the decision is not. Those are different problems, and treating one as the answer to the other is how the procurement agent kept ordering.

Fix revocation properly and lifetimes can be long again, which is the outcome operators actually want: fewer moving parts, less reissue traffic, and an authority graph where the reason something still works is that nobody has revoked it, rather than that nobody has looked.

Frequently asked questions

How do you revoke an AI agent's access across every system at once? You cannot do it by disabling an account, because most of the agent's authority was never issued by the identity provider. The durable approach is to anchor every delegation to a human root key and publish a signed revocation of that root. Any verifier that walks the chain then rejects every descendant, including credentials the agent minted at runtime that nobody knew existed.

Why doesn't disabling an account revoke everything? Because authority in a modern stack is issued by many parties, not one. The identity provider controls its own sessions and whatever automated deprovisioning reaches. OAuth grants live at the service that issued them, personal access tokens live at the code host, API keys live wherever they were pasted, and already signed tokens are valid until their expiry claim. Disabling reaches the first category only.

What is revocation lag? It is the interval between deciding an identity should lose authority and the moment the last artifact carrying that authority stops working. Most organisations measure time to disable the account, which covers the fastest artifact and none of the slow ones. Measuring the full distribution usually reveals a tail of weeks or longer for tokens and grants nobody inventoried.

Are short lived tokens a good enough answer to revocation? They bound the damage from a stolen token, which is real, but they do not end authority that should have stopped. Short lifetimes re issue authority automatically without anyone re deciding it should exist, so an agent with an unauthorised grant simply refreshes it continuously. Bounding theft and ending authority are different problems.

Why is online revocation checking a bad default? The certificate world already ran this experiment. Online status checks added latency, created an availability dependency and leaked usage to the issuer, and because an unreachable responder would break connections, clients adopted soft fail. An attacker able to intercept traffic can also block the status request, so the check fails open exactly when it matters. The industry's answer was to push compact signed status ahead of time and keep verification local.

What does chain anchored revocation not cover? Any authority that never descended from a chain. An API key pasted directly into an agent's environment has no parent and no ancestry, so revoking a root does nothing to it. Chain anchoring is a discipline about how authority is granted rather than a scanner for authority granted badly, and verifiers that never check the chain will never see the revocation.

Sources

  1. RFC 7009, OAuth 2.0 Token Revocation, IETF. datatracker.ietf.org
  2. RFC 7662, OAuth 2.0 Token Introspection, IETF. datatracker.ietf.org
  3. RFC 6960, X.509 Internet Public Key Infrastructure Online Certificate Status Protocol, IETF. datatracker.ietf.org
  4. W3C, Bitstring Status List, a credential status mechanism for verifiable credentials. w3.org
  5. Elastic Security Labs, analysis of the Shai-Hulud npm supply chain worm and its persistence after token rotation. elastic.co/security-labs
  6. Mandiant threat intelligence on the campaign against Snowflake customer tenants using credentials harvested by infostealers. cloud.google.com
Disabling the account is the one row of the table that was never the problem.