OT
A reachable socket on a flat network shouldn't be enough to command a plant you never set foot in.
An OT control plane (a SCADA head-end, an engineering workstation, a vendor's remote-access tunnel) trusts an IP and a topology, never the machine on the other end. Modbus, DNP3 and PROFINET carry no notion of who is speaking, so a PLC obeys whoever reaches it, and much of OPC UA is deployed with no authentication at all. Whisper closes that gap with one primitive: the asset's address is its identity. This page is the OT front door to the Whisper docs. The full technical library (DNSSEC, DANE, RDAP, the control-plane API) sits one click down the sidebar, shared verbatim with whisper.online/docs.
The problem: reachability is the whole exploit
This is a missing-identity problem that patching cannot close, because the insecurity is by design in the protocol and structural in the network. A device cannot prove who it is, and a controller cannot verify what it is told. A valid-looking command from anywhere with reachability is obeyed as if it came from the plant operator. An attacker discovers an exposed asset (Shodan and Censys already catalogue it), walks in on a default or absent credential, rides IT/OT convergence and remote-access sprawl across the boundary, and lands on a flat network where segmentation is by IP and VLAN, so a foothold that owns an address inherits trust. Then the protocol obeys: a plaintext, spoofable, replayable Modbus/TCP:502 register write, a forced disconnect, a setpoint. And because the session rides shared, rotating egress with no device identity behind it, the SOC can't say which controller or session issued the destructive write, nor revoke it across the vendor/integrator boundary. The root cause has a name: OWASP broken authorization / BOLA. At plant scale, the network trusts an IP and a topology, never the machine.
This is not hypothetical. 145,000+ internet-exposed ICS services sit across 175 countries (Censys 2024); a documented class of intrusions took over ≥75 internet-reachable PLCs through a factory-default password like 1111, or none at all (CISA AA23-335A); FrostyGoop (the first malware to weaponize Modbus, and the ninth ICS-specific family ever seen) wrote to Modbus/TCP:502 registers and cut heating to roughly 600 apartment buildings for about two sub-zero days, against a backdrop of ~46,000 internet-exposed Modbus devices; and OPC UA is no refuge: Bitsight TRACE found 14,220 exposed servers, more than 50% accepting unauthenticated sessions and about 80% supporting the plaintext None security mode. Across 2024 there were 1,693 ransomware attacks on industrial organisations (+87% YoY), one in four causing a full OT-site shutdown (Dragos 8th YIR). Detection will always trail a command that is genuinely valid on the wire. The strictly-stronger move is to change what the network trusts.
The cure: the address is the asset
Shipped & live. Deriving an asset or PLC /128 from the device's own key + the OPC UA ApplicationUri (or asset serial) it already carries is in production today. Provision one with the control-plane call below and verify it from the DNSSEC root with tools already on your machine.
Whisper gives each asset (each PLC, RTU, IED, HMI, gateway or historian) a routable IPv6 /128 out of 2a04:2a01::/32 (announced by AS219419). It's derived deterministically from the device's public key (its SubjectPublicKeyInfo: the public half of the OPC UA application-instance certificate, an IEEE 802.1AR IDevID, a TPM, or a secure element) with the OPC UA ApplicationUri (or an asset serial) as the domain separator. The ApplicationUri is already a globally-unique identity: OPC UA Part 6 §6.2.2 requires the application-instance certificate SAN to carry exactly one uniformResourceIdentifier equal to it, and a session fails with BadCertificateUriInvalid if the two don't match. So OPC UA already binds a key-derived name to the asset's key. The private key never leaves the device; only its public SPKI is an input. The result is DNSSEC-anchored, DANE-EE 3 1 1 pinned, and RDAP-registered, re-derivable and verifiable by anyone with dig.
/128 and gives it the cross-org off-switch a local CRL never had.Because the derivation is tenant-bound, the same asset under two operators yields two unrelated /128s: an outsider cannot link a unit across the vendor/integrator boundary. And because the domain separator is the ApplicationUri, the ApplicationUri alone yields nothing: it is a structured identifier that flows through every OPC UA deployment and is not a secret, yet you cannot go ApplicationUri → /128 without the key, there is no enumerable directory, and RDAP and reverse-DNS return the registry object, never the asset's live whereabouts.
What becomes true the moment an asset holds one:
- "Reachable = trusted" stops being true across the conduit. You cannot present an asset identity whose key you don't hold; a source with no key behind it can't establish a session across a governed boundary, and every forgery is a DNSSEC/DANE inconsistency any verifier catches.
- IP and topology trust become irrelevant. Identity is not the source IP or the VLAN: the "last IP" was never the credential, so rotating it across clouds or a remote-access pool changes nothing.
- A command from an unproven, off-segment source can't establish a session. A reachable socket with no asset key behind it authenticates to nothing at the transport boundary. It governs who may reach and speak to the asset. The last-inch, purely-internal insecure-protocol write still has to be enforced in the command path, and the honest scope below says so plainly.
- One
revokecuts a compromised asset off worldwide at DNS-TTL speed: the cross-org off-switch OPC UA never had. Its own trust is a local per-site TrustList; revocation is a CRL edit invisible to your integrator, your vendor, or a regulator.
Additive, never a replacement. Whisper complements the anchors you already ship: the OPC UA application-instance certificate and its local TrustList, the 802.1AR IDevID and BRSKI onboarding, IEC 62443 zones & conduits, TPM/HSM/secure elements, and the device's own MUD profile. It is the publicly verifiable, DNSSEC/DANE-anchored layer on top, anchoring the asset↔network boundary. You can even DANE-pin your existing OPC UA endpoint's certificate to DNSSEC and cut single-CA and self-signed trust risk. Whisper never reaches into the Modbus, DNP3 or PROFINET wire, adds no authentication to those protocols, and does not sit inside the PLC's command path; it keeps the ApplicationUri's key-derived property and adds the two things a private TrustList lacks: public verifiability and DNS-TTL revocation, without re-flashing the brownfield fleet.
And nothing is issued in the dark: every mint and every revoke lands in a public, append-only Merkle transparency log (Ed25519-signed and anchored to Bitcoin via OpenTimestamps) that you and your assessor can audit. (Honest status: tamper-evident and Bitcoin-anchored today; independent third-party witnessing is the next step, and the log already speaks the witness protocol.)
Provision an asset identity
Provisioning is one control-plane call over the public API: POST https://graph.whisper.security/api/query with your X-API-Key. Hand it the device's base64 SPKI and the ApplicationUri; it returns the deterministic /128 and a WireGuard config for source-bound egress:
CALL whisper.agents({op:'connect', args:{
tier:'wireguard',
identity_public_key:'<base64 SPKI of the asset key>',
device_id:'urn:example-plant:line2:PLC7:server' // the OPC UA ApplicationUri
}}) YIELD op, ok, status, result, error
RETURN op, ok, status, result, error
Send it with your key. The heredoc keeps the single-quoted Cypher literals intact, so this runs as-is:
curl -s https://graph.whisper.security/api/query \
-H "X-API-Key: whisper_live_xxx" \
-H 'content-type: application/json' \
--data @- <<'JSON'
{"query":"CALL whisper.agents({op:'connect', args:{tier:'wireguard', identity_public_key:'<base64 SPKI>', device_id:'urn:example-plant:line2:PLC7:server'}}) YIELD op, ok, status, result, error RETURN op, ok, status, result, error"}
JSON
# response
{ "op": "connect", "ok": true, "status": "created",
"result": {
"address": "2a04:2a01:7a2::4840",
"fqdn": "opcua-plc7.asset.<tenant>.agents.whisper.online",
"wireguard": { /* peer, keys, allowed-ips */ }
} }
The call is idempotent and liberal in what it accepts, strict in what it returns: re-running with the same key and ApplicationUri returns the same /128; a different device_id for a key already registered on your tenant is a clear 409, not a silent overwrite; a non-string device_id is a 400 that tells you exactly what was wrong, never an opaque 500. The device_id argument is generic: pass an OPC UA ApplicationUri, an 802.1AR IDevID serial, or a bare nameplate serial, whatever native identifier the asset carries. Even a dumb Modbus PLC behind a protocol gateway gets a verifiable network identity, a PTR and an RDAP object for the first time.
A dedicated --applicationuri CLI flag is on the roadmap; today, asset provisioning is the control-plane call above (which is live). The shipped CLI verbs are whisper verify --trustless, whisper create --register, whisper kill --revoke, whisper policy, and whisper logs. See CLI & one-command.
Verify it yourself: no account
Every asset identity is checkable with no key and no login, from the internet's own records. The whisper CLI does the full walk in one call:
whisper verify --trustless opcua-plc7.asset.<tenant>.agents.whisper.online
✓ DNSSEC chain valid to the IANA root
✓ DANE-EE (TLSA 3 1 1) leaf matches the asset's OPC UA cert key
✓ RDAP: registered under AS219419 · 2a04:2a01::/32
identity: VERIFIED (our own API was never trusted)
Or reach for the raw records directly. It's the same answer, from stock tools:
# the public verify endpoint: evidence chain in JSON
curl -s https://whisper.online/verify-identity/2a04:2a01:7a2::4840 | jq
{ "is_whisper_agent": true, "dane_ok": true, "jws_ok": true, "evidence": { /* … */ } }
# the address is the asset: forward-confirmed reverse DNS names it
dig -x 2a04:2a01:7a2::4840 +short
opcua-plc7.asset.<tenant>.agents.whisper.online.
# the registry object: who holds the address, and under which allocation
curl -s https://whisper.online/ip/2a04:2a01:7a2::4840 | jq
None of these calls Whisper as an authority. --trustless re-derives the proof against the public DNSSEC root, exactly as any resolver could. This is the verifiability OPC UA's own model explicitly lacks: it discourages public CAs precisely so no external party decides which apps are trusted, which is why a peer operator, an integrator or a regulator can't check your asset without joining your TrustList. See Verify an agent for the full keyless check and DANE & TLSA for the pin, byte for byte.
Revoke, worldwide
A compromised PLC, a module swap, a change of integrator, a decommission: one call tears down the /128, its PTR, and its DANE pin everywhere at DNS-TTL speed:
CALL whisper.agents({op:'revoke', args:{agent:'2a04:2a01:7a2::4840'}})
# after the TTL: dig -x returns nothing, verify returns false
whisper kill --revoke 2a04:2a01:7a2::4840
Compromise one asset and you've compromised that asset, not the plant. The flat-network blast radius is structurally contained. Revocation is the kill-switch; the same control plane also governs what a live asset may reach in between, which the MUD section below covers in full.
Attribution: name whoever already scraped you
Identity stops the next forgery; the graph names the operator behind the sessions already in your logs: attribution that survives IP rotation because it fingerprints the operator and the tooling, not the ephemeral egress IP. Behavioral OT monitoring is excellent inside the plant, but it stops at the Purdue boundary and the firewall. Run this as read-only Cypher over the same public API with your key (there is no CLI subcommand for this; it is the graph API directly):
curl -s https://graph.whisper.security/api/query \
-H "X-API-Key: whisper_live_xxx" \
-H 'content-type: application/json' \
-d '{"query":"CALL whisper.identify(\"45.83.x.x\")"}'
# operator fingerprinted across AWS / GCP / Azure; remote-access pool collapsed by JA4
The read-only verbs (identify, origins, walk, variants, history) each return a reproducible, replayable JSON evidence chain your OT SOC, your PSIRT, and a regulator can replay. Two levers, kept honestly separate: infrastructure genealogy clusters cloud rotation by shared ASN, hosting and certificate lineage; a JA4/JA3 client fingerprint collapses a rotating remote-access or residential-proxy pool to one operator, invisible to the proxy because it lives in the TLS handshake. More in Graph & cognition.
Lookups: see who's enumerating your plant
An identity you can prove is also an identity you can watch. Because every asset's name resolves through Whisper's own authoritative DNS and RDAP, the owner can ask who looked. It's a reconnaissance tripwire the OPC UA local, out-of-band TrustList never gave you. op:lookups returns who resolved or RDAP-queried an asset's identity, so you see someone enumerating your plant before the write lands, not in the post-mortem afterward:
CALL whisper.agents({op:'lookups', args:{agent:'2a04:2a01:7a2::4840', window:'24h'}})
# the same reverse-observability view, keyless, per address
curl -s https://whisper.online/ip/2a04:2a01:7a2::4840/lookups | jq
# → who resolved this asset's PTR/AAAA/TLSA and hit its RDAP object, and when
Paired with op:logs (the asset's own outbound activity) and /ip/<addr>/transparency (its ordered lifecycle), you have both halves of the picture: what an asset reaches out to, and who is reaching in to look at it.
MUD egress governance: make the device's declaration enforceable
This is the OT differentiator. Under MUD (RFC 8520), an industrial device already declares exactly what it should communicate with: a manufacturer-signed manifest of from-device and to-device ACLs, emitted over DHCP (options 161/112), an LLDP vendor TLV, or the id-pe-mud-url X.509 extension co-located with an 802.1AR IDevID. MUD's fatal weakness: the manifest is fetched from the manufacturer's domain and enforced by a local MUD manager at the nearest switch. The rules are only a suggestion, and a compromised device still reaches anything the local admin didn't think to block. Whisper binds that declaration to the asset's verifiable /128 and enforces it as default-deny egress governance where traffic actually leaves, cross-org and checkable by anyone.
/128 where traffic actually egresses, the manufacturer's declaration of intent finally becomes a cryptographic, cross-org control, not a suggestion at the nearest hop.Egress is source-bound to the asset's /128, so policy is enforced by name and by address, a conduit at asset granularity, not a VLAN:
# default-deny: this PLC may reach ONLY its historian, controller and vendor OTA endpoint
whisper policy set --default deny --allow historian.example-plant.com,controller.line2.local,ota.vendor.com
# per-asset firewall (allow/deny by host, cidr or port) and a traffic budget + kill-switch
CALL whisper.agents({op:'firewall', args:{agent:'2a04:2a01:7a2::4840', deny:['0.0.0.0/0'], allow:['historian.example-plant.com:4840']}})
CALL whisper.agents({op:'budget', args:{agent:'2a04:2a01:7a2::4840', max_mb_per_day:50}})
This directly implements the CISA CPG 2.0 pattern (an IPv6-addressable asset register plus logical segmentation permitting only required communications) and the IEC 62443-3-3 conduit, but keyed to a cryptographically verifiable identity rather than a spoofable MUD URL or a VLAN tag. And because each historian feed and telemetry stream can be bound to, and signed under, the asset's forge-proof /128, the integrator, the auditor and downstream settlement can trust the numbers came from the real asset, not a spoofed source on the flat network. See Egress governance for the full policy surface.
The honest edge. Egress governance changes who may reach and speak to an asset, and constrains what a governed asset may reach: it contains C2, exfil and lateral movement across the convergence bridge and the remote-access surface. It does not add authentication to Modbus, DNP3 or PROFINET on the wire, and it does not stop a purely-internal manipulation by an attacker who already holds an OT-segment foothold: closing that last inch needs identity enforced in the command path, at the PLC, a protocol-aware broker-gateway, or the EWS. We say so before your assessor does.
What ships today, and what's on the roadmap
We label these honestly so you can plan against them.
| Shipped & live | On the roadmap |
|---|---|
Asset/PLC /128 from the device key + ApplicationUri (or serial): DNSSEC + DANE-EE + RDAP |
A dedicated --applicationuri / --mud CLI flag (provision via the control-plane call today) |
Control-plane provision, verify, revoke; MUD egress governance (policy/firewall/budget); op:lookups; the Merkle transparency log; the attribution graph over the public API |
STIX 2.1 over TAXII export |
| The Splunk, Microsoft Sentinel and OpenCTI connectors (signed, replayable JSON → CEF / ECS fields) | sector-ISAC (E-ISAC / WaterISAC) machine-readable JSON export |
The integration guides below describe proposed integrations at the asset and IP boundary, designed to complement the stack you already run, not endorsed by any vendor, and never named against a specific operator as a breach victim.
The five OT guides
The OT story, in depth: each page is self-contained and copy-paste runnable.
/128 from the OPC UA ApplicationUri (or an 802.1AR IDevID / serial) an asset already holds. Deterministic, tenant-bound, DNSSEC + DANE-EE pinned, the device-identity spine.
OT-exposure cure →Why a reachable socket on a flat network is the whole exploit, and how a forge-proof address ends "trusted because it owns an IP": the missing-identity root cause, cured at the identity layer.
OPC UA · MUD · 62443 →Proposed integrations at the asset/IP boundary: OPC UA GDS & ApplicationUri, MUD (RFC 8520), 802.1AR / BRSKI, IEC 62443 zones & conduits, OT-visibility feeds. Complements, never replaces.
IEC 62443 · EU CRA · TSA →Map identity, egress and attribution evidence to EU CRA Annex I, IEC 62443-4-2 CR 1.2 & 3-3 zones-and-conduits, CISA CPG 2.0, NIST SP 800-82r3 and the TSA SDs, as a network primitive, not a binder.
MUD egress · verify · attribute →Runnable recipes: enforce a device's MUD manifest as default-deny egress at its /128, verify an asset trustless, back-trace a suspicious controller on the graph.
The full technical library
OT rides on the same address-is-identity platform as every other agent on the network, so the whole shared library applies here unchanged, and every page has a clean Markdown twin at the same path + .md. Start with these; the rest is in the sidebar.
dig, curl, and openssl.
DANE & TLSA →The 3 1 1 pin that makes an address forge-proof, byte for byte, no CA in the path.
Control plane →The full whisper.agents API (provision, connect, policy, logs, revoke) over the public endpoint.