# How the sensor works

The whole pipeline on one page: seven layers and one thread. A routable /128
identity at the anchor, a forward-first sensor above it, a calm brain with two
schedulers, on-host detection engines, an internet-scale graph verdict, policy
expressed against the identity, and two response levers. Running through all
seven: everything is checkable from outside. This is the map. Each layer links
to its own guide.

## The shape of it: seven layers, one thread

Most EDR runs its brain and its response on the very box it is trying to protect.
Whisper is built the other way around, and the reason it can be is unusual:
Whisper is also the DNS and the network the endpoint lives on. The host sensor
collects and does only cheap, local, observe-only deciding; the expensive
analytic work happens off-host against the Whisper graph, which has usually
already scored the destination because it served the DNS query; and the control
that matters at incident time, ending a compromised host, lives on our
infrastructure rather than on the host. That split is why the sensor runs at
about 2.6% of one core, and why containment survives a fully owned box.

```
  1 · IDENTITY        routable /128 · DNSSEC · DANE · RDAP          off-host  |
  2 · SENSOR          exec · file · conn · dns · forward-first      on-host   | V
  3 · CALM            two schedulers · 2.6% of one core · 16 MB     host+fleet| E
  4 · ON-HOST BRAIN   12 engines + Sigma + YARA-X · observe-only    on-host   | R
  5 · THE GRAPH       process -> DNS -> verdict · 7.52B nodes        off-host  | I
  6 · POLICY          firewall · egress · geo · device · nesting    off-host  | F
  7 · RESPONSE        bounded on-host · off-host kill-switch        both      | Y
```

Layers 2, 3 and 4 run on the host, small and observe-only. Layers 1, 5 and 6
live on Whisper's infrastructure, keyed by the /128. Layer 7 has a lever in both
places, and the one that survives a compromised box is the off-host one. The
thread on the right is the part nobody else has: each layer's claim can be
checked from outside, keyless.

## Layer 1: identity, the anchor

Every host and every agent is issued a real, routable IPv6 /128 from our own
space (2a04:2a01::/32, announced by AS219419), with DNSSEC-anchored reverse DNS,
a DANE-EE key pin and a public RDAP object. It is not an opaque vendor sensor
ID; it is an internet identity a stranger can check with dig, delv and openssl,
with Whisper's API nowhere in the trust decision.

That identity is what the other six layers hang off. Attribution keys to it
(reverse DNS answers "which agent made this connection"), egress sources from
it, policy is written against it, and containment revokes it. The sensor itself
is optional: a device that will never run a kernel agent (a PLC, a printer, a
contractor laptop, an ephemeral AI agent) still enrolls a /128, still verifies
keyless, and can still be contained from the network. The seven-proof keyless
verify chain, persistence across upgrades, and the connectivity tiers are in
[The /128 identity](/docs/wire-identity).

## Layer 2: the sensor, forward-first by construction

The default build is pure Go and GPL-free: an exec collector on the kernel
NETLINK process-connector, a file collector on fanotify, a both-directions
socket collector on NETLINK_SOCK_DIAG, and a DNS-question tap on AF_PACKET. A
lower-latency eBPF exec and per-connect tap is opt-in behind a `-tags ebpf`
build. Everything the sensor sees rides one telemetry stream in four
vocabularies: exec, file, conn, dns, plus a fifth `detection` kind that findings
ride back on.

The architectural rule that shapes the whole layer: the hot path always forwards
telemetry downstream before any detector looks at it. Detectors attach as taps
that fold a judgement on the side, or as observers that side-consume copies; by
construction, detection can never delay, drop or alter telemetry. The sensor is
also kernel-graceful and fail-open: a tap that cannot attach on a given kernel
is dropped and the rest keep running. The package behind this carries 2,112 test
functions and 93 fuzz targets across 216 test files. Mechanism by mechanism, with
each tap's blind spot, in [Telemetry collectors](/docs/collectors) and
[The Linux sensor](/docs/linux-sensor).

## Layer 3: calm, two schedulers and a measured 1%

Scheduling is the key to any telemetry system, and Whisper runs two of them.

The on-host governor is an autonomous, cgroup-bounded scheduler whose only job
is to be invisible: the hot path (the taps and the local verdict) never rides it
at all; only deferrable work does. When the host is idle it leans in; when the
network is also demonstrably quiet it opens a burst window of up to 10 parallel
workers; when the host is busy it collapses to urgent-only. A self-meter reads
the sensor's own cgroup so calm is a measured property, a graded circuit breaker
sheds work before the host ever feels it, a coalescer folds churn storms (a
package manager touching thousands of files) into one submission, and every
heavy worker runs at nice 19 with idle I/O priority. A starvation floor
guarantees past-deadline work still lands, so calm never becomes blind.

The fleet planner is the second scheduler, computed off-host where compute is
free: per-/128 advisory hints derived from the graph's behavioral history. A
clean history deepens deferral; a suspicious one holds bursts; a critical
finding requests immediate attention. It is advisory by construction: the host
pulls hints, a missing or dead planner leaves the host on its standalone
baseline, and no hint can touch the hot path or exceed the cgroup ceiling.
Strike the entire backend column and the host is still calm.

Above the levers sits a disposition system: the sensor's temperament is a point
on a two-axis plane (how hard it hunts, how much it tells you), with ten named
presets from "barely there" to the silent-and-relentless SOC preset, and one
move on that plane deterministically re-projects all 18 tuning levers. A safety
floor no preset or override can cross keeps CPU at or under the 25% cgroup
quota, caps memory, and pins autonomous response to observe.

25% of a core is the ceiling we promise never to need; a live capture today,
root-privileged in a cgroup scope matching the production SLA, settled at 2.61%
of one core and 16 MB, inside a 150 MB soft budget and a 250 MB hard cap. The
full scheduler, the enforced floor and the persona plane are in
[Calm footprint & SLA](/docs/footprint).

## Layer 4: the on-host brain, observe-only

On top of the stream sits the local verdict layer: twelve behavioral engines
(ransomware behavior, process injection, the write-exec-connect event chain, a
critical-config integrity watch, hash verdicts, the per-process trust tier)
plus two real, industry-standard rule engines running natively on the host: a
Sigma evaluator built on the SigmaHQ ecosystem's rule format, matching all four
event vocabularies, and a YARA-X byte-signature scanner (VirusTotal's engine)
over settled executables and anomalous memory. Together they name 91 on-host
ATT&CK techniques with 79 bundled Sigma rules; the default build ships zero YARA
rules, since that engine is a build-time opt-in. Both engines accept
operator-supplied rules.

Every engine is observe-only: it emits a detection event onto the stream, it
never blocks, kills or quarantines on its own. And every engine deliberately
tops out below certainty, leaving the final word to the graph layer beneath it:
a local confidence of 0.90 with a graph verdict on the destination beats a local
1.0 with no context. Engine by engine, with real rule names and
false-positive screens, in [Detection engines](/docs/detections).

## Layer 5: the graph, three graphs and one join key

The reason detection is better here, not just different, is that the brain runs
against an internet-scale graph the host could never hold, and the join into
that graph exists because Whisper serves the DNS. Three graphs are in play.

Graph one is on the host. The sensor folds its stream into a bounded
per-endpoint graph: processes, files, sockets, domains, executables, with edges
for spawn, exec, connect and resolve. Its rarest edge is process to the DNS name
it looked up, drawn by attributing the port-53 question to the querying PID,
with a blind spot: DoT and DoH bypass the tap and are simply not
observed.

Graph two is the internet. The Whisper security graph, 7.52B nodes
and 38.6B relationships, scores every destination for ownership, ASN,
prevalence and threat band, off the host.

Graph three is the join. Both sides key the same canonical, lowercased host
name, so the on-host `evil.example` node and the internet-graph `evil.example`
node are the same string by design, and the verdict pins straight onto the local
picture.

```
  GRAPH 1 · ON-HOST                        GRAPH 2 · THE INTERNET
  PROCESS curl                             HOSTNAME evil.example
    | RESOLVED                               | RESOLVES_TO -> IPV4 -> ORIGIN_AS
  DOMAIN evil.example  <---- one key ---->  owner · ASN · prevalence · band
    | CONNECTED_TO · srcPort 51244           band: CRITICAL · prevalence: rare
  SOCKET 203.0.113.7:443
  bounded · fail-open · per /128            7.52B nodes · 38.6B relationships

  GRAPH 3 · THE JOIN: verbatim overlay + srcPort fusion · fail-open, never a gate
```

The join runs in both directions. At resolve time, the resolver scores the name
against the graph and the verdict band rides back with the DNS answer. At read
time, the per-endpoint view collects the host's destination names and pins the
graph's band / owner / country / asn / prevalence columns onto them verbatim, in
one batched, absolutely fail-open call. And in the exfiltration correlator, a
connect is joined to the nearest preceding resolve across different processes,
strongest key first: name equality, then the shared source-port flow key (the
same ephemeral port on the resolve and the connect, even when a shared system
resolver did the lookup), then same-process timing. The fused chain is scored 0
to 200: temporal proximity plus graph-verdict severity, so a chain to a
newly-registered, rarely-seen destination outranks a chain to a popular benign
host at the same lag. No graph signal simply means a pure temporal score, never
a claim of malice.

The verdict itself is a re-runnable Cypher with a provenance receipt, and it is
keyless to check:

```
# a keyless graph verdict, no account, no API key
curl -s https://graph.whisper.online/api/query \
    -H 'content-type: application/json' \
    -d '{"query":"CALL whisper.assess(\"google.com\")"}'
{"columns":["host","label","band","sub_labels","signals","coverage","evidence",
  "verdictScore","isThreat","threatSources"],
 "rows":[{"host":"google.com","label":"benign-allowlisted","band":"NONE",
   "sub_labels":[],"signals":[],"coverage":"known-clean",
   "evidence":["coverage:known-clean","band:NONE","host-class:unknown","popularity-rank:1"],
   "verdictScore":0.0,"isThreat":false,"threatSources":0}]}
```

Graph-first means the graph gets the first opinion; where it has none,
resolution falls through to real DNS. Fail-open is the load-bearing promise: if
the graph is slow or unreachable, the sensor and the resolver keep serving and
lose visibility, never availability. No graph call sits in a blocking hot path,
so a bad day at the backend can reduce what you see but cannot take the host
down. That is the opposite trade from an EDR whose brain is a kernel driver on
the box.

## Layer 6: policy, written against the identity

Because the identity is an address we allocated and the egress is a plane we
run, governance is expressed against the /128 and enforced off the agent's
host. A compromised agent cannot disable its own firewall, because the firewall
is not on its box. Five policy shapes, all riding one control verb
(`whisper.agents`) with the caller's API key:

- **Firewall.** A per-/128 allow/deny rule-set the egress plane consults once
  per connection: destination IP, CIDR, host (exact or any-subdomain) or port,
  first match wins, up to 1,000 rules. Every write lands in the durable
  containment audit trail. Live.
- **Graded egress.** Every process is bucketed into a trust tier, T0 known-bad
  up to T4 verified, and each tier maps to an egress freedom on an ordered
  ladder: none < dns-only < mediated < direct < unrestricted. The endpoint's
  worst observed process tier drives the decision, so a confirmed-bad process
  drags its host's egress down to DNS-only, decided in the network. Tier
  refusals, per-/128 byte caps, budget caps with an immediate kill, and windowed
  per-run allowlists are all live-enforced; the policy read-back labels every
  namespace as enforcement=live or enforcement=stored, and a stored
  token is never presented as live policy.
- **Geography.** Two planes. Resolution-time geo bundles (geo:deny / geo:allow,
  deny wins on conflict) are evaluated live per resolution against the graph.
  Jurisdiction-tagged allocation, where the address itself encodes its country
  and publishes it via RDAP and a geofeed, is opt-in and disabled by default;
  until a tenant enables it, allocation is untagged and byte-identical to
  before.
- **Per-device policy.** The same policy verb takes an optional device selector,
  so one call governs a whole tenant or a single /128. Per-device overrides can
  only escalate, never downgrade a graph-critical block, and a revoked address
  drops its override so a recycled /128 inherits nothing. Live.
- **Nested identities.** Agents nest: a parent agent spawns sub-agents, each
  getting its own governed /128, and the fleet topology reports the tree.
  Enforcement composes down the measured transit chain (who is actually
  egressing through whom, not who claims to be): a child's effective firewall is
  its own rules intersected with every live ancestor's, most restrictive wins,
  and a kill-switched ancestor cuts every descendant behind it. Revoking the
  parent severs the subtree's transit in one move. Depth is bounded and the walk
  fails closed past the bound. Live.

```
  parent /128 [kill] ---measured---> child A /128      DENY at next connect
        \--measured--> child B /128 --measured--> grandchild /128   DENY
  the walk follows the measured transit chain, most-restrictive-wins
  cut the parent, cut the branch
```

The calm personas of layer 3 are themselves a policy shape: the same per-device
selector that scopes a firewall can scope a disposition, clamped quieter-only
against the tenant floor.

## Layer 7: two levers, near and far

Response is two layers, and you want both.

The near lever is an on-host respond tier: armed and opt-in, it can stop a
process, quarantine a file, restart a service or patch a package over a
poll-only channel, with per-action audit, an allowlist and no shell. It defaults
off. A host that opted into observe telemetry has not opted into action; you arm
it deliberately. Where it acts it is reversible where reversal makes sense (the
process freeze is a reversible SIGSTOP).

The far lever is the off-host kill-switch. One control-plane call revokes the
/128: reverse DNS and forward resolution go to authenticated NXDOMAIN, the RDAP
record shows no assignment, the DANE pin drops, and traffic that sources from
that /128 stops.
Because the lever lives on our infrastructure, not on the host, it holds even
when the host is fully compromised and the local sensor is dead: a host-only
agent's off-switch lives on the machine it has just lost. The two levers, the
safety posture, and the reproducible revoke walkthrough are in
[Containment](/docs/kill-switch).

The boundary: on-host action is opt-in, not the default. The C2 anomaly
auto-contain ladder is gated and observe-first today; we say held, not blocked,
while a decision runs in observe. The adversary-proof lever is the off-host
revoke, and it is live.

## The thread: verify it yourself

Running through all seven layers is the property that makes the rest
believable: the claims are checkable from outside, keyless. The identity chain
(PTR, DNSSEC, DANE, RDAP) verifies with stock tools. The graph verdict re-runs
as the Cypher above. Revocation verifies as an authenticated NXDOMAIN a
stranger can fetch. And the evidence trail is anchored in a signed, append-only
log with an independent witness cosignature and a Bitcoin timestamp. We call
that trail tamper-evident, deliberately not "publicly verifiable", while the
witness set is still small; the receipts and their limits are in
[Compliance evidence](/docs/evidence).

## Where each piece is documented

- [Telemetry collectors](/docs/collectors) - layer 2 in full: every shipped
  collector, its kernel mechanism, its blind spot, and the forward-first
  stream they all feed.
- [Detection engines](/docs/detections) - layer 4 in full: the twelve behavioral
  engines and the two real rule engines (Sigma + YARA-X), with named rules
  and FP screens.
- [The /128 identity](/docs/wire-identity) - layer 1 in full: DNSSEC reverse,
  DANE, RDAP, did:web, nested sub-agents, and the keyless verify chain.
- [Calm footprint & SLA](/docs/footprint) - layer 3 in full: the two schedulers,
  the persona plane and its 18 levers, the enforced floor, and the measured 1%
  footprint.
- [Containment](/docs/kill-switch) - layer 7 in full: the opt-in on-host respond
  tier and the off-host kill-switch that survives a fully owned box.
- [ATT&CK coverage](/docs/attack-coverage) - what the pipeline captures across
  the matrix, measured live, with telemetry kept strictly separate from the
  verdict.
