{
 "slug": "building-agent-kill-switch-actually-works-cryptographic-nonce",
 "topic_id": "TOPIC-020",
 "cluster": "Agentic Commerce & MCP Tool-Call Gating",
 "tier": "Tier B",
 "title": "Building an agent kill-switch that actually stops the agent",
 "summary": "The usual kill-switches are a firewall rule and a token revocation. One takes minutes and stops everything; the other stops everything too. Neither is what you want at 3am.",
 "lede": "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.",
 "date": "2024-05-27",
 "category": "Comparison",
 "author_id": "priya-venkatraman",
 "tags": [
  "kill switch",
  "revocation",
  "agent operations",
  "incident response",
  "nonce",
  "reliability"
 ],
 "image_title": "Agent Kill Switch Design",
 "schema": "Article",
 "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."
 ],
 "body": [
  {
   "type": "h2",
   "text": "What you need at 3am"
  },
  {
   "type": "diagram",
   "kind": "compare",
   "alt": "Three options, and what each costs",
   "caption": [],
   "nodes": "The right column is the only one that is both fast and surgical.",
   "left": {
    "title": "Firewall or token revocation",
    "items": [
     "Minutes to deploy, or instant",
     "Stops everything",
     "Often incomplete — other paths",
     "Operators hesitate"
    ]
   },
   "right": {
    "title": "Per-authorisation revocation",
    "items": [
     "Milliseconds",
     "Stops one agent",
     "Complete within the authorisation",
     "Operators act immediately"
    ]
   }
  },
  {
   "type": "p",
   "html": "Four properties, and existing controls give you at most two."
  },
  {
   "type": "table",
   "head": [
    "Property",
    "Firewall rule",
    "Token revocation",
    "Per-authorisation revocation"
   ],
   "rows": [
    [
     "Fast to apply",
     "No — minutes",
     "Yes",
     "Yes"
    ],
    [
     "Surgical",
     "No",
     "No",
     "Yes"
    ],
    [
     "Complete",
     "Often not",
     "Yes",
     "Yes within the authorisation"
    ],
    [
     "Verifiable after the fact",
     "Weakly",
     "Weakly",
     "Yes — signed revocation entry"
    ]
   ]
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "Why token revocation hurts"
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "The nonce model"
  },
  {
   "type": "p",
   "html": "If every consequential action carries an authorisation identifier, revocation can target the authorisation rather than the credential."
  },
  {
   "type": "code",
   "text": "# At the point of effect, before the write:\n\nauth_id = action.authorization.id          # from the signed authorisation\n\nif revocations.contains(auth_id):\n    raise Halted(\"authorization revoked\", auth_id)\n\nif nonces.seen(action.nonce):              # replay protection\n    raise Halted(\"nonce already consumed\", action.nonce)\n\nnonces.consume(action.nonce)\nexecute(action)"
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "Making it actually fast"
  },
  {
   "type": "p",
   "html": "Three implementation details separate a design that stops an agent in under a second from one that stops it in a minute."
  },
  {
   "type": "ol",
   "items": [
    "<strong style=\"font-weight:600\">Check at the effect, not at the gateway.</strong> A gateway check is bypassed by anything already past the gateway: in-flight requests, queued work, retries from a local buffer.",
    "<strong style=\"font-weight:600\">Fail closed on lookup failure.</strong> If the revocation store is unreachable, halt. An agent that continues because the check failed is the failure mode the control exists to prevent.",
    "<strong style=\"font-weight:600\">Propagate by push, not poll.</strong> A 30-second poll interval is a 30-second floor on your kill time. Push invalidation with a short-TTL cache as the fallback."
   ]
  },
  {
   "type": "h2",
   "text": "Scope levels worth having"
  },
  {
   "type": "table",
   "head": [
    "Revoke",
    "Stops",
    "Use when"
   ],
   "rows": [
    [
     "One authorisation",
     "A single approved action",
     "A specific approval was obtained fraudulently"
    ],
    [
     "One delegation",
     "Everything under that delegation chain",
     "One agent instance is misbehaving"
    ],
    [
     "One principal's delegations",
     "All agents acting for that human",
     "The human's credential is suspected compromised"
    ],
    [
     "A tool or action class",
     "That action everywhere",
     "A systemic defect in a tool"
    ]
   ]
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "The evidence side"
  },
  {
   "type": "p",
   "html": "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?"
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "What this does not fix"
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "Three implementation details that decide the kill time"
  },
  {
   "type": "table",
   "caption": "Where the seconds go",
   "head": [
    "Detail",
    "If wrong"
   ],
   "rows": [
    [
     "Check at the effect, not the gateway",
     "In-flight work, queues and retries continue"
    ],
    [
     "Fail closed on lookup failure",
     "An unreachable store means the agent keeps running"
    ],
    [
     "<strong style=\"font-weight:600\">Push invalidation, not polling</strong>",
     "<strong style=\"font-weight:600\">A 30-second poll is a 30-second floor</strong>"
    ]
   ]
  },
  {
   "type": "p",
   "html": "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."
  },
  {
   "type": "h2",
   "text": "Objections and honest limits"
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“Token revocation is already instant.”</strong> 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."
  },
  {
   "type": "p",
   "html": "<strong style=\"font-weight:600\">“This does not undo what already happened.”</strong> Correct. Containment speed reduces the size of the cleanup; it does not eliminate it. Nothing at this layer does."
  }
 ],
 "faq": [
  {
   "q": "What latency is realistic?",
   "a": "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."
  },
  {
   "q": "Why not just revoke the token?",
   "a": "It works and it stops every agent sharing that identity. That collateral damage is why operators hesitate, and hesitation is what makes incidents long."
  },
  {
   "q": "Should the check fail open or closed?",
   "a": "Closed. An agent that keeps running because the revocation store was unreachable is exactly the scenario the control exists to prevent."
  },
  {
   "q": "Does this need signatures?",
   "a": "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."
  }
 ],
 "sources": [
  {
   "t": "CISA — known exploited vulnerabilities and incident reporting",
   "u": "https://www.cisa.gov/known-exploited-vulnerabilities-catalog"
  },
  {
   "t": "RFC 7009 — OAuth 2.0 Token Revocation",
   "u": "https://www.rfc-editor.org/rfc/rfc7009"
  },
  {
   "t": "Uniform Electronic Transactions Act (ULC)",
   "u": "https://www.uniformlaws.org/committees/community-home?CommunityKey=2c04b76c-2b7d-4399-977e-d5876ba7e034"
  },
  {
   "t": "Operational guidance on containment timing in incident response frameworks."
  },
  {
   "t": "Model Context Protocol specification",
   "u": "https://modelcontextprotocol.io/specification"
  },
  {
   "t": "OWASP Top 10 for Large Language Model Applications",
   "u": "https://owasp.org/www-project-top-10-for-large-language-model-applications/"
  }
 ],
 "related": [
  {
   "slug": "kill-switch-design",
   "title": "Building the kill switch",
   "category": "Developer"
  },
  {
   "slug": "intelligence-cannot-mint-permission-mcp-security",
   "title": "Intelligence cannot mint permission",
   "category": "Developer"
  },
  {
   "slug": "always-allow-most-dangerous-button-enterprise-ai",
   "title": "Always Allow is the most dangerous button",
   "category": "Developer"
  },
  {
   "slug": "replits-dropped-database-incident-postmortem-unrendered-ai-agent",
   "title": "The dropped-database postmortem",
   "category": "Developer"
  }
 ],
 "image": "https://cdn.twc.sh/images/igcache/Agent%20Kill%20Switch%20Design/1200_630/blog.jpg",
 "wordcount": 872,
 "url": "/blog/building-agent-kill-switch-actually-works-cryptographic-nonce.html",
 "reading_time": "4 min read",
 "meta_description": "The usual kill-switches are a firewall rule and a token revocation. One is slow and coarse; the other stops every agent at once.",
 "hub": {
  "slug": "topics/agent-tool-call-gating",
  "title": "Agent tool-call gating"
 },
 "pair": {
  "slug": "kill-switch-design",
  "title": "Building the kill switch",
  "mode": "DIFF"
 },
 "answer": "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.",
 "answer_q": "What makes a kill-switch actually stop an agent?",
 "glossary": [
  {
   "term": "Authorisation id",
   "def": "An identifier carried by an action, allowing revocation to target the authority rather than the credential."
  },
  {
   "term": "Blast radius",
   "def": "How much stops when you pull a control — the property that determines whether an operator is willing to pull it."
  },
  {
   "term": "Push invalidation",
   "def": "Notifying verifiers of a revocation rather than waiting for them to poll."
  }
 ],
 "checklist": {
  "title": "Building a usable kill-switch",
  "id": "killswitch",
  "desc": "Four steps.",
  "steps": [
   {
    "name": "Give every consequential action an authorisation id.",
    "text": "So revocation can target it."
   },
   {
    "name": "Check the revocation set at the point of effect.",
    "text": "Before the write, not at the gateway."
   },
   {
    "name": "Fail closed.",
    "text": "An unreachable store halts rather than permits."
   },
   {
    "name": "Push invalidation with a short-TTL fallback.",
    "text": "Polling sets the floor on your kill time."
   }
  ]
 },
 "cta": {
  "title": "Where this fits in Manav",
  "html": "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.",
  "href": "../docs.html",
  "label": "See delegation chains"
 }
}