Containment: on-host response and the off-host kill-switch
Response happens in two layers. A bounded, opt-in tier acts on the host. The lethal lever lives off it, on our own infrastructure, so it holds even when the host is fully owned.
A host-only sensor runs its brain and its response inside the machine it watches. When an attacker or a rogue agent owns the host, the off-switch you were counting on is now under their control. Whisper Endpoint keeps a near lever for fast, reversible action when the host is still yours, and a far lever the host can never reach. The far lever is the one that matters at incident time.
In the seven-layer architecture this page is the top two layers: policy, the everyday governance expressed against the /128 from off the host, and response, the containment that fires when governance is not enough. They share one design: the identity is the handle, and the enforcement point is our network, not the host's software.
Two layers, and why
The default posture is observe-only: the sensor watches and reports, and it does not sit in the critical path. Response is something you add, deliberately, in two independent places.
The far side: the off-host kill-switch
Every host and agent carries a routable IPv6 /128 out of 2a04:2a01::/32 (announced by AS219419), named in DNSSEC-anchored reverse DNS and pinned in DANE. That identity is the handle. Revoking it stops two things at once: its resolution (it can no longer look names up through Whisper) and its egress (traffic sourced from its /128 is cut). The change propagates worldwide at DNS-TTL.
You trigger it from the CLI or the control plane, against the identity, not the host:
# the simple form
$ whisper kill --revoke 2a04:2a01:8dae:4521:45b8:b241:4f91:56e2
whisper: 2a04:2a01:8dae:4521:45b8:b241:4f91:56e2 - released
# the same act on the control plane (API key redacted; use a throwaway, never a live host)
$ curl -s -X POST https://graph.whisper.online/api/query \
-H 'content-type: application/json' \
-d '{"query":"CALL whisper.agents({op:\"revoke\", address:\"2a04:2a01:8dae:4521:45b8:b241:4f91:56e2\"})","apiKey":"whisper_live_...redacted"}'
You verify the teardown the same way you verified the identity: with stock tools, keyless, against the IANA root. A revoked or never-held /128 does not return an empty answer, it returns authenticated non-existence, DNSSEC-signed denial you can check for yourself:
$ dig +dnssec -x 2a04:2a01:dead:beef::1 @9.9.9.9
;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 26376
;; flags: qr rd ra ad; QUERY: 1, ANSWER: 0, AUTHORITY: 8 # ad is the resolver's claim about the denial; delv re-derives it
;; AUTHORITY SECTION:
1.0.a.2.4.0.a.2.ip6.arpa. 300 IN SOA ns1.whisper.online. hostmaster.whisper.online. 1787608004 ...
1.0.a.2.4.0.a.2.ip6.arpa. 300 IN RRSIG SOA 13 10 300 20260826210000 20260824200000 58485 ...
d8pbc0d1ermlg13osn18rbvi7e9la6i1.1.0.a.2.4.0.a.2.ip6.arpa. 300 IN NSEC3 1 0 0 - D8PJBQUAPIJCKDFBND55H0F75GLA64HG
Why it holds when the host is owned. The revoke does not ask the host to do anything. It changes what the network and the authoritative DNS will do for that identity. A fully-owned host cannot un-revoke itself, because the decision was never on the host. This is the move a host-only EDR structurally cannot make.
Containment is scoped, reversible, and leaves a durable audit trail: every contain action is written to the transparency ledger and exposed as a keyless third-party proof at /api/contain/action/<ledgerId>, so an auditor can confirm a containment happened without being handed any trust. Reversal is a scoped, deliberate act of its own. See Compliance evidence.
Agent trees: cut the parent, cut the branch
Agents nest. A machine that is a Whisper agent runs a browser agent that egresses through it, which spawns a scraper of its own, and each of those gets its own governed /128, to a depth of 32. The tree is measured, not claimed: the egress plane observes a child's connections physically arriving from its parent's /128 and records that edge, so "who is nested behind this identity right now" is ground truth, never a self-declared field an attacker could point at a victim. The details of the identity model are in The /128 identity; what matters here is what it does to containment.
Policy composes down the measured chain, most-restrictive-wins: a descendant's effective egress is its own rule-set intersected with every live ancestor's, so a deny at any layer binds everything transiting below it, and a parent can never widen what a child may do. Which yields the containment property this section is named for: kill the parent and every descendant behind it is cut on its next connection. You never chase the children individually.
Two levers cut a subtree, in escalating finality:
- Kill-switch cascade (instant, reversible). Flip the kill on the parent's budget:
whisper.agents({op:'budget', address:{parent}, kill:true}). The parent's live tunnels are cut immediately, and on the next connection every descendant transiting it is denied, because the per-connect policy walk finds a killed ancestor on the measured chain. Lifting the kill restores the branch. - Hard revoke (identity teardown).
op:revokeon the parent tears down its/128: authenticated NXDOMAIN, an RDAP record showing no assignment, DANE gone, and the transit path every child egressed through with it. The children's own identities are then revoked separately if they should not return elsewhere.
Why the cascade cannot be escaped. The enforcement walk follows only the measured chain, never a caller-declared parent pointer, so a hostile agent can neither hide behind a fake parent nor name a victim's identity as its parent to read that victim's policy state. Past the 32-hop depth bound the walk fails closed, and the refusal is uniform on every path, so probing the cascade leaks nothing about the tree.
Before the kill-switch: policy on the same handle
Revocation is the last resort. The everyday layer below it is a set of policy shapes, all expressed through the same one control verb (whisper.agents), all keyed by the agent's /128, and all enforced off the host, in our resolver and egress plane, so a compromised agent cannot disable its own rules the way it could flush its own iptables:
| Shape | What it governs | Enforced | Status |
|---|---|---|---|
| Egress firewall | Per-identity allow/deny rules on destination ip, cidr, host (exact or any-subdomain) or port; ordered, first match wins, then a default action. Every rule-set write lands on the same audit trail as a revoke. | egress plane, once per connect | live |
| Process-trust graded egress | Maps the graph's per-process trust tiers T0..T4 onto the freedom ladder none < dns-only < mediated < direct < unrestricted; the endpoint's worst observed process tier decides. Only a confirmed graph known-bad verdict reaches T0, never a fail-open guess. | egress plane, per connect | live |
| Byte caps and budget kill | Per-identity byte, connection and request ceilings folded into the live meter, tightest cap wins; kill:true cuts live tunnels immediately. | egress plane, live meter | live |
| Per-run scope | A windowed, deny-default destination allowlist bound to one run's duration: only the listed destinations are reachable while the window is open. | egress plane | live |
| Resolution geo policy | Country allow-lists and deny-lists evaluated live per resolution against the graph's view of where a name resolves; deny wins on conflict. | the resolver | live |
| Jurisdiction-tagged allocation | The /128 itself allocated inside a country-tagged block, so the address encodes its jurisdiction, published in RDAP and an RFC 9092 geofeed. | at allocation | opt-in, off by default |
| Per-device policy | The same policy verb with a device selector routes rules to one identity instead of the tenant; a device override only ever escalates, never downgrades a graph-critical block. A revoke drops the override, so a recycled /128 never inherits one. | the resolver | live |
The trust ladder in one picture, since it is the shape that turns detection into graded restraint instead of a binary kill:
graph trust tier egress freedom (decided per connect, off-host)
T4 verified ──► unrestricted
T3 neutral ──► direct
T2 unverified ──► mediated
T1 suspicious ──► dns-only ◄─ refusal enforced live
T0 known-bad ──► none ◄─ only a CONFIRMED graph verdict lands here
Maturity, per lever. The read-back of every policy write tells you whether that namespace is enforced or merely stored: the egress levers above answer enforcement=live; console namespaces that are recorded but not yet enforced answer enforcement=stored, and a stored token is never rendered as live policy. Jurisdiction-tagged allocation is opt-in and disabled by default; until you enable it, allocations are byte-identical to untagged ones, and a jurisdiction lookup can never block an allocation.
The near side: bounded on-host response
When the host is still yours, a complementary tier can act on it directly. It is armed and opt-in, never on by default: a host that opted into observe telemetry has not opted into acting. When you arm it, the actions are drawn from a fixed allowlist, run over a poll-only channel keyed to the host's own /128, and each one is audited.
| Action | What it does | How it is kept safe |
|---|---|---|
process.stop | Stops a process | pidfd_open + pidfd_send_signal, immune to PID reuse; a safe signal, never a blind SIGKILL mid-transaction |
quarantine.file | Defangs a file in place to a quarantine store | The file is neutered to mode 0000, the sha256 recorded so the move is verifiable and reversible |
service.restart | Restarts a unit | Allowlist-only; no shell, no $PATH lookup, absolute program paths only |
package.update | Patches a package | The package manager transaction is left intact; a build guard fails compilation if a shell literal ever appears |
An opt-in local-policy exec guard can also close the loop from observe to verdict to block on the host itself. And a reversible freeze, a SIGSTOP across the offending process tree that SIGCONT lifts, ships in observe-first mode: it detects and can hold, off by default and enforcement-gated, so nothing acts on your fleet until you say so.
Maturity. The on-host response tier and the reversible freeze are real and live-fire proven, but they are a complement, not the primary line of defense, precisely because they live on the host. Process-trust graded egress refusals (the none and dns-only rungs) are enforced live in the egress plane, and the write's read-back says so per lever. Ransomware copy-on-write rollback is designed and on the roadmap, not shipped. The adversary-proof lever remains the off-host revoke.
The containment rail, in the console
In the operator console, containment is a rail with a durable audit trail: arm, sever, a keyless proof of the sever, and a reversal path. Every step is recorded, and the proof is one anyone can re-check.
The assets a host agent can never contain
Because the far lever is network-layer, it reaches things that will never run a kernel sensor:
- OT / IoT - a PLC, a camera, a printer gets a routable identity and default-deny egress; one call cuts it off, with nothing installed on the device.
- AI agents and sub-agents - each spawned agent has its own
/128, its own logs, and its own revoke, and a nested tree is cut at the parent: one kill severs the branch. This is the containment control a host-only EDR cannot give a summoned process. See The /128 identity. - Personal and contractor devices - a partner device resolves through you and carries a name you can check, and you can cut it off without pushing software onto someone else's laptop.
Calm is the composure of power held in reserve. The sensor does not cry wolf, because the lethal lever is already outside the attacker's reach.