Manav.id
Comparison · 4 min read

Building an agent kill-switch that actually stops the agent

Building an agent kill-switch that actually stops the agent

An agent enters a loop. It is making valid API calls with valid credentials, writing records that will take a week to unwind. Somebody has to stop it, and the available controls are a sledgehammer and a slower sledgehammer.

What makes a kill-switch actually stop an agent?

Revoking the authorisation rather than the credential, and checking it at the point of effect. A firewall rule is slow and incomplete; revoking a shared token stops every agent at once. Neither is what you want at three in the morning.

Key takeaways
  • Network-level blocking is slow to deploy, coarse, and often incomplete because agents reach services by several paths.
  • Credential revocation is fast but shared: revoking a token stops every agent using it, including the ones behaving correctly.
  • Per-authorisation revocation — checking a nonce or authorisation identifier at the point of effect — gives sub-second, surgical stopping without collateral damage.

What you need at 3am

Firewall or token revocationMinutes to deploy, or instantStops everythingOften incomplete — other pathsOperators hesitatePer-authorisation revocationMillisecondsStops one agentComplete within the authorisationOperators act immediatelyvs

Four properties, and existing controls give you at most two.

PropertyFirewall ruleToken revocationPer-authorisation revocation
Fast to applyNo — minutesYesYes
SurgicalNoNoYes
CompleteOften notYesYes within the authorisation
Verifiable after the factWeaklyWeaklyYes — signed revocation entry

The "complete" row is where firewall rules quietly fail. An agent reaching a service through a proxy, a queue, a cached connection or a second egress path is not stopped by a rule written against the path you knew about.

Why token revocation hurts

Most deployments give a fleet of agents credentials from a small number of service identities. Revoking one stops the misbehaving agent and every well-behaved agent sharing that identity.

In practice this means operators hesitate. The control that works is the one nobody wants to pull, so the incident runs longer than it should while somebody tries to scope the blast radius first.

The nonce model

If every consequential action carries an authorisation identifier, revocation can target the authorisation rather than the credential.

# At the point of effect, before the write:

auth_id = action.authorization.id          # from the signed authorisation

if revocations.contains(auth_id):
    raise Halted("authorization revoked", auth_id)

if nonces.seen(action.nonce):              # replay protection
    raise Halted("nonce already consumed", action.nonce)

nonces.consume(action.nonce)
execute(action)

Revoking is then a write to a small set that the effect path already reads. The latency floor is whatever that lookup costs — typically single-digit milliseconds against an in-memory store, plus propagation.

Making it actually fast

Three implementation details separate a design that stops an agent in under a second from one that stops it in a minute.

  1. Check at the effect, not at the gateway. A gateway check is bypassed by anything already past the gateway: in-flight requests, queued work, retries from a local buffer.
  2. Fail closed on lookup failure. If the revocation store is unreachable, halt. An agent that continues because the check failed is the failure mode the control exists to prevent.
  3. Propagate by push, not poll. A 30-second poll interval is a 30-second floor on your kill time. Push invalidation with a short-TTL cache as the fallback.

Scope levels worth having

RevokeStopsUse when
One authorisationA single approved actionA specific approval was obtained fraudulently
One delegationEverything under that delegation chainOne agent instance is misbehaving
One principal's delegationsAll agents acting for that humanThe human's credential is suspected compromised
A tool or action classThat action everywhereA systemic defect in a tool

The second row is the common case and the one no existing control provides. It is also the one that makes operators willing to act quickly, because it has a known blast radius.

The evidence side

A revocation entry signed by the revoking party and timestamped answers a question that comes up after every incident: when did you stop it, and who decided?

A firewall change and a token rotation leave that question to change logs and chat history. A signed revocation makes it a verification, which matters when an insurer or regulator asks about containment timing.

What this does not fix

Actions already executed. Revocation stops the next action; it does not unwind the previous thousand. Containment speed reduces the size of the cleanup and does not eliminate it.

It also does not help if consequential effects happen through paths that do not carry an authorisation identifier. The control is only as complete as the coverage of the identifier, which is an argument for instrumenting the small set of irreversible effects rather than every call.

Three implementation details that decide the kill time

Where the seconds go
DetailIf wrong
Check at the effect, not the gatewayIn-flight work, queues and retries continue
Fail closed on lookup failureAn unreachable store means the agent keeps running
Push invalidation, not pollingA 30-second poll is a 30-second floor

Four revocation scopes are worth having: one authorisation, one delegation, all of a principal's delegations, and one tool or action class. The second is the common case and the one no existing control provides.

Objections and honest limits

“Token revocation is already instant.” The record changes instantly; every agent sharing that identity stops too. That collateral damage is why operators hesitate, and hesitation is what makes incidents long.

“This does not undo what already happened.” Correct. Containment speed reduces the size of the cleanup; it does not eliminate it. Nothing at this layer does.

Building a usable kill-switch

  1. Give every consequential action an authorisation id. So revocation can target it.
  2. Check the revocation set at the point of effect. Before the write, not at the gateway.
  3. Fail closed. An unreachable store halts rather than permits.
  4. Push invalidation with a short-TTL fallback. Polling sets the floor on your kill time.

Terms used here

Authorisation id
An identifier carried by an action, allowing revocation to target the authority rather than the credential.
Blast radius
How much stops when you pull a control — the property that determines whether an operator is willing to pull it.
Push invalidation
Notifying verifiers of a revocation rather than waiting for them to poll.

Frequently asked questions

What latency is realistic? The check itself is a set lookup, typically single-digit milliseconds. End-to-end kill time is dominated by propagation, which is why push invalidation matters more than the lookup.

Why not just revoke the token? It works and it stops every agent sharing that identity. That collateral damage is why operators hesitate, and hesitation is what makes incidents long.

Should the check fail open or closed? Closed. An agent that keeps running because the revocation store was unreachable is exactly the scenario the control exists to prevent.

Does this need signatures? The revocation check does not. Signing revocation entries gives you evidence of containment timing and authority, which matters after the incident rather than during it.

Where this fits in Manav

Manav binds a named human to an agent's consequential actions through a signed delegation with scope and expiry, and a per-action receipt where the effect is irreversible.

See delegation chains →

Sources and further reading