Kill Switches, Circuit Breakers, and Canary Tokens
A kill switch is a boolean held outside the agent's edit surface, such as a Redis key, a feature flag, or a signed config, and it disables the agent entirely. A circuit breaker is finer-grained: it trips on a specific pattern (five identical tool calls in a row), pauses that path, and escalates to a human. A canary token borrows from classical deception: a fake credential or honeypot record the agent has no legitimate reason to touch, so any access raises an alert. eBPF-based datapaths like Cilium can rewrite a quarantined pod's egress to a forensic honeypot at the kernel layer. Published Cilium benchmarks report sub-millisecond P99 datapath latency under load, but your propagation budget depends on how a policy update reaches the node, not on the datapath. Statistical detectors (EWMA, CUSUM) adapt to a moving baseline and will quietly accept drift, so layer them with hard constitutional limits that do not bend.
Learning Objectives
- Describe the three tripwires (kill switch, circuit breaker, canary token) and the failure class each one covers.
- Apply the properties of a correct kill switch to decide where the off flag must live and when it is checked.
- Trace a trajectory through `main.py` and predict which detector fires, on which turn, and why the canary stays silent.
- Explain why statistical detectors such as EWMA and CUSUM need hard constitutional limits layered beside them.
- Name the attack classes no detector here catches, and where canaries must live to stay trustworthy.
The Problem
Cost governors (Lesson 13) bound what the agent can spend. They say nothing about what the agent can do inside the budget. An agent with a $50 velocity limit can still exfiltrate a secret, publish the wrong post, or delete a resource, and the damaging action is often the cheap one in tokens.
Three detectors sit next to the cost layer:
- Kill switch: a boolean off-button held outside the agent's reach.
- Circuit breaker: an action-pattern detector that pauses a specific path.
- Canary token: bait the agent has no legitimate reason to touch, so touching it gives the agent away.
None of this is new. Classical deception, rate-limit breakers, and feature-flag kills all predate autonomous agents. What changed is the attack surface. Agents read untrusted content (Lesson 11), edit their own memory, and can compose many safe-looking actions into an unsafe one. These detectors hold up because they do not trust the agent's self-report.
The Concept
Kill switches
A kill switch is a boolean the agent reads but cannot write. You have a few ways to build one:
- Feature flag in a managed service. LaunchDarkly, Statsig, Unleash. Updates propagate in seconds.
- Redis key the agent polls. Simple, but the agent process has to check it on each turn.
- Signed config in object storage. The agent verifies a signature on boot and rejects unsigned states.
- OS-level signal or container-lifecycle kill. Docker
kill, Kuberneteskubectl delete pod, systemd stop.
A correct kill switch has these properties:
- The agent cannot set it to
off, because it lives in a system the agent's credentials do not write. - It is checked before each consequential action, not just at startup.
- When it is off, the agent does nothing externally observable, including logging to systems the agent can reach.
- Re-enabling it is an explicit human action, not an automatic timeout.
Circuit breakers
A circuit breaker pauses a specific pattern instead of the whole agent. The classical shape comes from the 2007 Nygard book and still applies:
- Closed: the action is allowed.
- Open: the action is blocked.
- Half-open: after a cool-down, 1-3 probe attempts are allowed (default 1). Success closes the breaker, and any remaining failure re-opens it.
Triggers that matter for agents:
- Five identical tool calls in a row (a repetitive loop).
- Five consecutive failures on the same tool with different inputs (a systemic failure).
- A tool-call rate above a threshold (the Lesson 13 velocity idea).
- A specific tool such as
file.deleteinvoked after an out-of-trust content read (Lesson 11).
Canary tokens
Canary tokens, also called honeytokens, are inputs the agent should not touch. Access is the alarm.
Some examples for agents:
- A fake
AWS_SECRET_ACCESS_KEYin the environment with no actual privilege. Exfiltration attempts become easy to attribute. - A fake database record carrying a known sentinel value. Any read or update of it fires an alert.
- A fake file in the workspace. Reads or edits of it are suspicious.
- A fake memory entry injected at session start. If it shows up in a later output, memory has leaked.
Canary design depends on the workflow. A canary the agent has a legitimate reason to touch is just noise.
Why layer statistical and hard limits
Statistical detectors (EWMA, CUSUM, z-score on tool-call rates) adapt to a moving baseline. That helps with legitimate drift and hurts with adversarial drift: a patient attacker can move the baseline slowly enough that no alarm fires.
Hard constitutional limits (Lesson 17) do not bend. A rule like "do not email secrets@" is simpler to audit and can't be gamed by slow drift. Layer them: the statistical detectors catch most of the noise, and the hard limits catch the attack that walked past the detector.
Quarantine via eBPF datapath redirect
Cilium's eBPF-based network policy can rewrite a quarantined pod's egress to a forensic honeypot at the kernel layer. Cilium's public benchmarks report sub-millisecond P99 datapath latency on TCP_RR. The wall-clock budget for triggering a quarantine in your cluster is a different number, and it is dominated by how a policy update propagates from your control plane to the node. A few hundred milliseconds is realistic on Kubernetes with the operator pre-loaded, and tail latency depends on cluster size and reconcile cadence.
You get two things from this. The agent does not exfiltrate anything real, and its behavior inside the honeypot is fully observed. It is the canary idea at a different layer: the agent reveals intent by what it tries to do while contained.
What no detector catches
- Semantic attacks that compose from allowed primitives. Each action passes each detector, and the composition is the problem. This is the Auto Mode composite failure from Lesson 10.
- Attacks that carry the detector in the payload. Say the agent reads a page that tells it to disable the canary before acting, and the agent has the capability. Now the detector is compromised. Put canary tokens in systems the agent cannot modify.
Use It
code/main.py simulates a short agent trajectory through three detectors: a kill switch held in an external dict, a circuit breaker that trips on five identical tool calls, and a canary file whose read triggers an alert. The synthetic trajectory mixes legitimate actions, a repetitive loop, and a canary probe. The script runs it twice, once with the kill switch off and once with it on, so you can watch the agent's actions get halted.
Ship It
outputs/skill-tripwire-design.md reviews a proposed detector stack for an agent deployment and flags gaps: a missing kill switch, a missing canary, a circuit breaker threshold that is too loose.
I'd run main.py and read its output before trusting the description above. The script pushes one nine-action trajectory through the detectors twice, first with the kill switch off and then with it on. In the first run the breaker opens on turn 7, the fifth identical read:logs/app.log, and the loop breaks there. The canary read on turn 9 is never reached, so both runs end with canary_hits=0. Put the ~/.env.canary read ahead of the log burst and you'll see the alert fire. I left the trajectory alone because it shows how detector order and an early exit decide which tripwire gets to speak.
Two other gaps between the sketch and a real stack. CircuitBreaker declares a half_open state in a comment, but record() only moves from closed to open. There is no cool-down and no probe, so a tripped breaker stays tripped. And EXTERNAL_KILL_SWITCH is a dict in the same process as the loop, so it is "outside the agent" only by convention. In a deployment I'd want it in a system the agent's credentials cannot write, which is the whole point of the first property in the list above.
If I were adding one thing, it would be the hard limit from exercise 2. The three detectors here are cheap to read and cheap to reason about, and a rule that does not adapt is the one I'd trust against slow drift.
Exercises
-
Run
code/main.py. Confirm the circuit breaker fires on turn 7, the fifth identical log read. Then check the summary line:canary_hitsis 0, because the loop stops at the breaker before reaching the fake-key read on turn 9. Reorder the trajectory so the canary fires. -
Add a statistical detector: EWMA z-score on tool-call rate. Feed in a trajectory that drifts slowly and show the detector does not fire. Now add a hard limit (no more than 50 tool calls in 10 minutes) and show the hard limit fires on the same trajectory.
-
Design a canary token set for a browser agent (Lesson 11). List at least three canaries and what each would detect.
-
Read the Cilium network-policy docs. Describe an egress-redirect quarantine flow concretely: which policy selector, which pod, which egress rewrite, which alert. What governs the wall-clock latency from "decide to quarantine" to "first redirected packet"?
-
Define a re-enable procedure for a kill-switched agent. Who can re-enable? What must be documented? What must change about the agent before re-enable?
Key Terms
| Term | What people say | What it actually means |
|---|---|---|
| Kill switch | "Off button" | Boolean outside the agent's edit surface; checked on every consequential action |
| Circuit breaker | "Pattern pause" | Action-specific trip on repetition, failure rate, or rate-limit |
| Canary token | "Honeytoken" | Bait the agent has no legitimate reason to touch; access fires an alert |
| Honeypot | "Forensic sandbox" | Redirected traffic / workspace where a quarantined agent is observed |
| EWMA | "Moving average" | Exponentially weighted; adapts to drift (feature + bug) |
| CUSUM | "Cumulative sum" | Detects sustained shift from baseline |
| Hard limit | "Constitutional rule" | Does not adapt; constant regardless of history |
| Constitutional limit | "Always-true rule" | Tied to Lesson 17's constitution; cannot be edited by the agent |
Further Reading
- Anthropic: Measuring agent autonomy in practice: kill-switch and circuit-breaker framing for autonomous agents.
- Microsoft Agent Framework: HITL and oversight: production governance patterns.
- OWASP LLM / Agentic Top 10: detection-and-response requirements.
- Cilium: Network policy and eBPF: pod-level egress redirect and forensic honeypot patterns.
- Anthropic: Claude's Constitution (January 2026): hardcoded prohibitions as "constitutional limits".
Code
- main.py (python, 146 lines)