Whisper · Docs
Health · Recipes

Verify · attribute · govern

Six copy-paste recipes put a forge-proof, revocable network identity under a medical device and a FHIR endpoint. Bind it to the Endpoint.identifier it already carries, prove it keyless, DANE-pin your UDAP endpoint certificate to public DNSSEC, back-trace whoever is driving a suspicious API session, govern exactly what a device may reach, and see who is enumerating your fleet. Each runs today against shipped primitives.

They share one primitive: a routable IPv6 /128 that a device or endpoint derives from a key it already holds, DNSSEC-signed and DANE-pinned so any relying party can verify it and nobody can forge it. Everything below is built from shipped parts: the device /128, keyless verify, the attribution graph, per-device egress governance, the reverse-observability op:lookups, the Merkle transparency log, and one-call revoke. Where a step is a pattern you assemble rather than a single command, or a connector still on the roadmap, it says so in plain words.

What's shipped, stated honestly. The device/endpoint /128 derivation, keyless verify / RDAP, the attribution graph (whisper.identify), per-device egress governance (policy · firewall · budget · revoke), the reverse-observability op:lookups, and the Merkle transparency log are all live. Two caveats these recipes are written around: there is no first-class --fhir-endpoint or --udi CLI flag yet, so provisioning is shown via the control-plane API (which is live) with the Endpoint.identifier or FDA UDI passed as the generic device_id; and the SIEM/threat-intel export connectors named at the end are roadmap beyond Splunk, which ships. Whisper anchors the IP / DNS / transport boundary: it is not on the clinical bus (HL7v2, unauthenticated DICOM, Modbus), not inside a device's own key store, and it does not fix a FHIR BOLA in the application's authorization. It complements segmentation, device patching, and protocol-level auth; it never replaces them. Each recipe names exactly where it stops.

The shared primitive: an identity a device derives, not one you hand it

A FHIR endpoint, an EHR-facing gateway, or a connected medical device already carries a key: the key behind its UDAP server certificate, a secure-element or TPM key, or, for an ISO/IEEE 11073 point-of-care device, a hardware key named by an EUI-64. Whisper takes only its public SubjectPublicKeyInfo (SPKI) and, together with the Endpoint.identifier (or the FDA UDI device identifier, or a DICOM AE Title) as a domain separator, deterministically derives a /128 under 2a04:2a01::/32 (AS219419). The private key never leaves the device; the server only ever computes a public address.

The Endpoint.address in FHIR is already a URL (DNS-addressable by construction), but the FHIR Endpoint resource carries no key, signature, or certificate: its own trust model is "None", delegated to TLS and out-of-band UDAP. The derivation below turns that name into an identity with four properties the resource itself lacks:

Endpoint / device key UDAP cert · TPM · 11073 EUI-64 public SPKI only private key stays on device SPKI + identifier /128 2a04:2a01:7d2::b19 routable identity DNSSEC + DANE-EE A name anyone can verify whisper verify --trustless our API not in the trust path op:revoke → gone worldwide at DNS-TTL
The device's own key, named by the identifier it already carries, becomes a routable, publicly verifiable /128, and the off-switch a private community trust anchor can't give a counterparty outside it.

Because the address is bound to the device's key and its identifier, the identifier alone yields nothing. You cannot go Endpoint.identifier → /128 without the key, and RDAP / reverse-DNS return the registry object, never a directory of your fleet. One honest limit up front: the identity is only as forge-proof as the device's key custody (on an end-of-life device with no TPM or secure element, protect the key accordingly). Every check in the recipes below that doesn't provision is keyless: no account, just DNS and TLS any counterparty already has.

Recipe 1: Bind a device or endpoint to its FHIR Endpoint.identifier

Give a FHIR endpoint (or a connected medical device) the network identity it should have been born with, derived from the key it already holds and named by the Endpoint.identifier that already flows through every TEFCA / RCE directory entry. One call; the reply is the address, and a stock dig confirms the name.

Boundary. This complements the FHIR Endpoint resource, the UDAP server certificate, and FDA UDI/GUDID. It does not replace them or touch the clinical bus (HL7v2, DICOM, Modbus). It takes the public half of the device's existing key and makes it addressable, publicly verifiable, and revocable. The /128 is only as forge-proof as the key store it derives from.

Pass the Endpoint.identifier (or a device's FDA UDI device identifier) as the generic device_id. Re-running with the same device key + identifier returns the same /128, which makes it idempotent and safe to run from a provisioning loop:

# Bind a FHIR endpoint to the Endpoint.identifier it already carries. device_id = the identifier.
# Re-running with the same device key + identifier returns the SAME /128 (idempotent).
curl -s https://graph.whisper.security/api/query \
  -H "X-API-Key: whisper_live_xxx" \
  -H "content-type: application/json" \
  -d '{"query":"CALL whisper.agents({op:'"'"'connect'"'"', args:{tier:'"'"'wireguard'"'"', identity_public_key:'"'"'<base64 SPKI of the UDAP server-cert key>'"'"', device_id:'"'"'urn:oid:2.16.840.1.113883.3.7204:endpoint:ehr-prod'"'"'}}) YIELD op, ok, status, result, error RETURN op, ok, status, result, error"}' | jq .
{ "op":"connect", "ok":true, "status":200,
  "result":{ "address":"2a04:2a01:7d2::b19",
             "name":"ep-3f2504e0.fhir.example-hie.whisper.online",
             "dane":"3 1 1", "dnssec":"signed", "wireguard":"<peer config>" } }

# For a physical device, pass its FDA UDI device identifier (the GUDID DI) instead:
#   args:{tier:'wireguard', identity_public_key:'…', device_id:'00889842179897'}   # UDI DI
# or a DICOM node's AE Title, or an ISO/IEEE 11073 EUI-64 (any native identifier).

Confirm the /128 and its name from the other side, keyless. The reverse DNS is forward-confirmed (PTR ⇄ AAAA), so nothing but dig is required:

dig +short -x 2a04:2a01:7d2::b19
ep-3f2504e0.fhir.example-hie.whisper.online.

# …and the forward AAAA resolves straight back, the round trip that makes it forge-proof:
dig +short AAAA ep-3f2504e0.fhir.example-hie.whisper.online
2a04:2a01:7d2::b19

The mint is not issued in the dark. Every /128 minted and every one revoked lands in a public, append-only Merkle transparency log, so you and an auditor can replay the full issuance history of your fleet. That's the kind of asset-identity trail a HIPAA network map and an FDA §524B postmarket program both need:

# The identity's ordered lifecycle (mint, any rotations, revoke), keyless:
curl -s https://whisper.online/ip/2a04:2a01:7d2::b19/transparency
i

A supplied identifier is checked against a reused identity: the same device key with a different device_id on your tenant is a 409, and a non-string device_id is a clear 400. That's liberal in what it accepts, strict and unambiguous in what it commits. Honest status of the ledger: it is tamper-evident, Ed25519-signed, and Bitcoin-anchored via OpenTimestamps today; independent third-party witnessing is the next step. It already speaks the C2SP tlog-witness protocol so any external witness can co-sign. A first-class typed --fhir-endpoint / --udi argument is on the roadmap; today the identifier rides the generic device_id, which is shipped.

Recipe 2: Prove a device's identity, keyless

Once bound, any counterparty (a QHIN before it queries, an EHR before it accepts a report, a device before it trusts the server it is reporting to) proves the endpoint or device is real without an account, without a shared CA list, and without trusting Whisper as an authority. The proof re-derives against the IANA DNSSEC root.

Boundary: the honest last inch. This authenticates the endpoint's network identity at the IP/transport boundary. It kills a stolen static credential: a lifted UDAP/SMART token or a hard-coded key buys nothing, because the identity is a network fact bound to a key, not a bearer string that replays from another host. It does not authenticate the caller (that stays with SMART / OAuth), does not fix a FHIR BOLA in the application's own authorization, and does not stop an HL7v2 injection or an unauthenticated DICOM C-STORE between two nodes on the same segment. Those are segmentation and protocol-auth problems that still need in-path enforcement. Whisper shrinks who can reach the endpoint and attributes them; it does not patch the protocol.

The trustless check walks the full chain (DNSSEC to the IANA root, the DANE-EE pin, and the transparency-log entry) and prints exactly what it trusted:

# Prove any device/endpoint identity, trustless: re-derived against the IANA DNSSEC root,
# with no Whisper API trusted as an authority anywhere in the chain.
whisper verify --trustless 2a04:2a01:7d2::b19
dnssec   pass   DNSSEC-root   AAAA, PTR and TLSA(3 1 1) all DNSSEC-validated to the IANA root
dane     pass   DNSSEC-root   served leaf SPKI-SHA256 == TLSA pin
ledger   pass   DNSSEC-root   transparency-log entry present, signature verifies
CRYPTOGRAPHICALLY PROVEN · trust anchor: DNSSEC root (IANA) + DANE-EE · Whisper API NOT trusted

The same fact is reachable with tools already in every ops toolbox. One keyless HTTPS call, or plain dig, gets you there:

# Is this address a real, DANE-anchored device identity? One keyless call, no install:
curl -s https://whisper.online/verify-identity/2a04:2a01:7d2::b19
{"is_whisper_agent":true,"dane_ok":true,"jws_ok":true,
 "evidence":{"address":"2a04:2a01:7d2::b19",
   "ptr":"ep-3f2504e0.fhir.example-hie.whisper.online.",
   "forward_aaaa":"2a04:2a01:7d2::b19"}}

# A rogue endpoint or cloned device, with no registered /128 for its key or DANE pin to present:
curl -s https://whisper.online/verify-identity/2a04:2a01::1
{"is_whisper_agent":false,"detail":"no Whisper agent identity anchors this address"}

Two counterparties in a TEFCA exchange can now confirm each other with nothing but DNS and TLS: the layer where the network answers "who is this, provably," which is exactly where Endpoint.address lives and where the FHIR resource itself carries no proof.

Recipe 3: DANE-pin your UDAP / FHIR endpoint cert to public DNSSEC

UDAP already proves your FHIR endpoint's identity to your community: the base URL SHALL equal the server's {baseURL} and SHALL match a uniformResourceIdentifier entry in the certificate's SubjectAltName, and /.well-known/udap returns signed_metadata, a JWS signed by the server key, with its chain in the x5c header. The gap: that proof is rooted in a private, community-scoped trust anchor. A relying party can only verify it if it was pre-provisioned with your community's CA: a TEFCA anchor, a DirectTrust anchor, a state-HIE anchor. Publish a DNSSEC-signed DANE TLSA record that pins the same certificate, and any relying party (even one outside your community, with no anchor pre-provisioned) verifies the base-URL↔cert binding against the IANA DNS root. That closes the base-URL↔cert spoofing gap UDAP can only assert privately.

Boundary. This complements UDAP. It does not replace its Dynamic Client Registration, tiered OAuth, JWT client auth, or your community trust anchor, and it authenticates the endpoint host, not the caller. It publishes, in open DNS, the same cert-to-URL binding UDAP asserts inside the community, and makes it revocable at DNS-TTL: a second, orthogonal, publicly rooted proof beside the community CA. It does not touch SMART app authorization or a FHIR BOLA.

the base-URL ↔ cert binding UDAP asserts privately, anchored publicly UDAP · /.well-known/udap base URL == {baseURL} SAN uri == base URL signed_metadata JWS (x5c) private community CA verifiable only inside the community server cert C leaf SPKI Whisper · DNSSEC + DANE TLSA 3 1 1 pins leaf C DNSSEC → IANA root any relying party verifies no pre-provisioned anchor needed signs with C pin == C
UDAP binds the base URL to certificate C inside a private community anchor; a DNSSEC-signed TLSA 3 1 1 pins the same C in open DNS, so a relying party outside the community verifies the binding against the IANA root.

The endpoint host's /128 (provisioned exactly as in Recipe 1, but with identity_public_key set to the SPKI of your UDAP server certificate's key) is DANE-pinned to that certificate automatically. From then on, the pin equals the SPKI of the very cert UDAP signs its metadata with, and any party can cross-check the three facts line up:

# 1) What UDAP presents to the community (the signed_metadata leaf is cert C):
curl -s https://fhir.example-hie.org/r4/.well-known/udap | jq -r .signed_metadata
eyJ4NWMiOlsiTUlJ…"]}   # its x5c[0] leaf is cert C; iss == the FHIR base URL

# 2) What Whisper publishes to everyone (the DNSSEC-signed DANE pin of the SAME leaf C):
dig +short TLSA _443._tcp.ep-3f2504e0.fhir.example-hie.whisper.online
3 1 1 b653a4ef…fcb82d1d

# 3) The live TLS leaf, which must equal BOTH the TLSA pin and the UDAP x5c leaf:
openssl s_client -connect fhir.example-hie.org:443 </dev/null 2>/dev/null \
  | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der \
  | openssl dgst -sha256        # == the TLSA pin above == the UDAP signed_metadata leaf SPKI

A rogue server answering on that name, or a certificate mis-issued under some community anchor, fails immediately. It cannot present a leaf whose SPKI matches the DNSSEC-signed TLSA pin. And if the key is burned, one revoke drops the pin worldwide at DNS-TTL, a kill-switch that reaches every relying party at once, not just the ones inside your CRL/OCSP scope:

whisper verify --trustless ep-3f2504e0.fhir.example-hie.whisper.online
dane   pass   DNSSEC-root   served leaf SPKI-SHA256 == TLSA pin (== UDAP x5c leaf)
CRYPTOGRAPHICALLY PROVEN · base-URL ↔ cert binding verified without your community anchor

Recipe 4: Back-trace a suspicious API session

Detection tools tell you that a FHIR or UDAP client is hammering your endpoint oddly. When the source rotates across clouds and residential proxies (the pattern behind third-party-vendor compromise, the origin of 41% of 2024 healthcare breaches), they lose who at the firewall. The attribution graph doesn't: it fingerprints the operator, not the disposable last IP. This is the keyed half, and it is exactly the "verify who is asking" that TEFCA itself names as an open problem. Pass the suspicious egress address to whisper.identify over the public graph API.

Boundary. This is an enrichment query over Whisper's internet-infrastructure graph (BGP, DNS, WHOIS, TLS, hosting, and threat-intel), not a tap on your clinical network. It complements your IoMT sensor and SIEM; the egress IP is the one thing it never relies on. It attributes the operator; it does not, by itself, block them.

what your SOC sees: a rotating, meaningless “last IP” Stolen UDAP/SMART token = endpoint authority AWS us-east 3.68.x.x GCP us-central 34.90.x.x Azure eastus 20.61.x.x residential-proxy swarm 71.x · Comcast 82.x · Spectrum 99.x · AT&T JA4-identical tooling ASN · hosting · cert genealogy JA4 fingerprint One operator infra genealogy + JA4 / JA3 fingerprint evidence chain → your SIEM what the graph sees: one operator
Attribution survives rotation because it tracks the infrastructure and the tooling, not the ephemeral egress. Infrastructure genealogy collapses the cloud hops; a JA4 client fingerprint collapses the residential swarm: the cross-organization view a single-hospital sensor structurally can't see.

The call itself is the graph API, since there is no whisper identify CLI verb. Passing the address to whisper.identify over POST https://graph.whisper.security/api/query is the interface:

# Who really operates the client behind a suspicious FHIR/UDAP session? The graph API.
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(\"34.90.x.x\")"}' | jq .
  operator:  <fingerprinted> · seen across AWS / GCP / Azure
  residential swarm collapsed by JA4: same tooling, 41 exit IPs → 1 operator
  evidence:  reproducible, replayable JSON for your SOC and an auditor

Cluster the rotation into one genealogy, then express the exchange-scale question (one source touching N distinct endpoint identities in a window) as read-only Cypher rather than a ticket:

# Cluster the rotating egress into one infrastructure genealogy, and timeline it:
curl -s https://graph.whisper.security/api/query -H "X-API-Key: whisper_live_xxx" \
  -H "content-type: application/json" \
  -d '{"query":"CALL whisper.origins(\"34.90.0.0/16\") YIELD asn, prefix, first_seen RETURN asn, prefix, first_seen"}' | jq .
# whisper.walk(node, depth) expands the genealogy; whisper.history() gives the timeline.

Recipe 5: Govern a device's egress

An identity you can prove is also one you can fence. An infusion pump, an imaging modality, or an EHR gateway should talk to its FHIR server, its QHIN, and its vendor's update host, and nothing else. So make that the policy, cap what it can move, and keep the one-call kill-switch ready. Because egress source-binds to the device's own /128, these controls apply to the device, not to a fragile IP allowlist, and they work even on an agentless or end-of-life device that could never take a NAC client.

Boundary. This governs the device's internet-side egress via the graph-first resolver and source-bound egress. It is an L3 allow-list: additive and availability-safe, adding no inline chokepoint on the clinical bus. It maps directly to the HIPAA Security Rule NPRM's network-segmentation ask, the HHS CPG segmentation goal, and access control at §164.312(a). What it does not do is stop a same-segment, unauthenticated clinical-protocol exchange between two nodes on the ward LAN; that remains a segmentation + protocol-auth problem. It shrinks who a device can reach and cuts its C2/exfil path; it does not become the ward firewall.

Device /128 2a04:2a01:7d2::a03 egress source-bound policy default-deny firewall · budget by host / cidr / port FHIR server fhir.example-hie.org ✓ allow QHIN endpoint qhin.example-network.org ✓ allow everything else C2 · exfil · unknown ✗ block op:revoke → /128 + PTR + DANE gone worldwide at DNS-TTL
Default-deny per device: allow the FHIR server and the QHIN by name, block the rest, cap the throughput, and keep one revoke that cuts a compromised device off worldwide, an L3 segment even a device that can't run an agent can be given.

Set the resolution policy to default-deny and allow only the names the device legitimately needs, with the CLI or over the control plane with op:policy:

# Default-deny the device's egress; allow only its FHIR server and its QHIN.
whisper policy set --agent 2a04:2a01:7d2::a03 \
  --default deny --allow fhir.example-hie.org,qhin.example-network.org

# The same, over the control plane (op:policy), allowing by name or subdomain:
curl -s https://graph.whisper.security/api/query -H "X-API-Key: whisper_live_xxx" \
  -H "content-type: application/json" \
  -d '{"query":"CALL whisper.agents({op:'"'"'policy'"'"', args:{agent:'"'"'2a04:2a01:7d2::a03'"'"', default:'"'"'deny'"'"', allow:['"'"'fhir.example-hie.org'"'"','"'"'qhin.example-network.org'"'"']}})"}'

Tighten it further with a per-device firewall by host, cidr or port, cap what the device can move with a budget, and keep the kill-switch one verb away:

# Per-device firewall (op:firewall), allowing the vendor update host on 443 and denying all else:
curl -s https://graph.whisper.security/api/query -H "X-API-Key: whisper_live_xxx" \
  -H "content-type: application/json" \
  -d '{"query":"CALL whisper.agents({op:'"'"'firewall'"'"', args:{agent:'"'"'2a04:2a01:7d2::a03'"'"', rules:[{allow:'"'"'host'"'"', host:'"'"'updates.example-vendor.com'"'"', port:443},{deny:'"'"'all'"'"'}]}})"}'

# Budget + kill-switch (op:budget), capping egress volume and tripping the switch past the cap:
curl -s https://graph.whisper.security/api/query -H "X-API-Key: whisper_live_xxx" \
  -H "content-type: application/json" \
  -d '{"query":"CALL whisper.agents({op:'"'"'budget'"'"', args:{agent:'"'"'2a04:2a01:7d2::a03'"'"', egress_mb_per_day:20, on_exceed:'"'"'kill'"'"'}})"}'

# Cut a compromised device off worldwide in one call (op:revoke), no re-imaging, no truck roll:
whisper kill --revoke 2a04:2a01:7d2::a03   # worldwide, at DNS-TTL

After the revoke, every keyless check from Recipe 2 flips to is_whisper_agent:false for that address within the DNS TTL: no CRL to distribute, no per-vendor blocklist to push, no fleet-wide firmware flash. Containment is one call, and it lands in the transparency log, the postmarket-containment evidence FDA §524B(b)(1) asks you to be able to demonstrate.

Recipe 6: See who enumerated your fleet

The kill-chain starts with reconnaissance: someone resolving and RDAP-querying your endpoint and device identities to map the fleet before the session. A stale TEFCA/RCE or NDH directory makes it easy: a listed FHIR base URL invites a walk of every endpoint behind it. Because every identity's name resolves through Whisper's own authoritative DNS and RDAP, the owner sees exactly who looked, a tripwire the community directory never gave you. This is reverse observability: not the device's own outbound (op:logs), but who has been checking it.

Boundary. op:lookups reports resolution and RDAP accesses against your identities' authoritative records: the PTR / AAAA / TLSA queries and /ip reads. It is an early-warning signal, not a claim to see traffic inside someone else's network; pair it with Recipe 4 to turn a suspicious source into an attributed operator.

recon, before the session, and now you can see it Enumerator walking your directory dig -x · TLSA RDAP /ip Whisper authoritative DNS · RDAP for your /128s ep-3f2504e0… udi-00889842… …512 distinct names 512 lookups / 60s op:lookups who checked this identity ⚠ recon tripwire early warning → your SOC
One source resolving hundreds of distinct endpoint identity names in seconds is the enumeration signature, surfaced by op:lookups as an early warning, not a post-mortem.

Ask who has been checking a single endpoint's identity (keyless, for that address) or the whole fleet with your key:

# Who resolved or RDAP-queried this endpoint's identity? A recon tripwire, keyless per-address:
curl -s https://whisper.online/ip/2a04:2a01:7d2::b19/lookups
{"address":"2a04:2a01:7d2::b19","window":"24h",
 "lookups":[{"kind":"PTR","count":6},{"kind":"TLSA","count":2},
            {"kind":"rdap","count":3}]}

# Fleet-wide, with your key (op:lookups): the single source walking your whole directory:
curl -s https://graph.whisper.security/api/query -H "X-API-Key: whisper_live_xxx" \
  -H "content-type: application/json" \
  -d '{"query":"CALL whisper.agents({op:'"'"'lookups'"'"', args:{scope:'"'"'fleet'"'"', window:'"'"'60s'"'"'}})"}' | jq .
  source 34.90.x.x: resolved 512 distinct endpoint identities in 60s   ⚠ enumeration

Feed that source straight into Recipe 4's whisper.identify and the anonymous recon becomes an attributed operator, before a single record is queried.

Where each recipe stops and what's roadmap

Honest scoping, one row per recipe. Whisper anchors the IP / DNS / transport boundary; the clinical bus, the device's own key store, and the application's authorization stay exactly where they are.

RecipeComplementsDoes not touch / replace
Bind to its identifier FHIR Endpoint resource, UDAP server cert, FDA UDI/GUDID the device's own key custody; the clinical bus (HL7v2 / DICOM / Modbus)
Prove keyless · DANE-pin UDAP the UDAP community CA, /.well-known/udap signed_metadata UDAP DCR / tiered OAuth / JWT client auth; SMART app authZ; a FHIR BOLA
Back-trace · Lookups your IoMT sensor, SIEM and threat-intel; TEFCA "verify who is asking" a tap on your clinical network; it enriches, it doesn't monitor the LAN
Govern device egress HIPAA NPRM segmentation, HHS CPG, §164.312(a), IEC 62443 FR2 an inline clinical chokepoint; a same-segment unauthenticated DICOM / HL7v2 exchange

Roadmap, clearly labelled. Streaming this evidence into a SIEM ships today for Splunk, Microsoft Sentinel and OpenCTI (signed JSON → CEF/ECS). A STIX 2.1 over TAXII feed and a Health-ISAC machine-readable export for sharing revocation and attribution evidence are proposed, not yet available. And the first-class typed --fhir-endpoint / --udi arguments are roadmap: provision via the control-plane API shown above, passing the identifier as the generic device_id, which is live. The transparency log is tamper-evident, Ed25519-signed and Bitcoin-anchored today; independent witnessing is the next step. Nothing on this list is required for the six recipes; they run on shipped primitives alone.

Next