Whisper · Docs
Energy

Energy

A stolen aggregator token shouldn't be able to command a gigawatt of DER.

A DER control plane (a vendor cloud, a DERMS head-end, an aggregator API) authenticates a claim: an OAuth bearer, a JWT, an API key. It never authenticates the machine on the other end. Whisper closes that gap with one primitive: the DER's address is its identity. This page is the energy 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: fleet takeover, BOLA at grid scale

An attacker mints or reuses a bearer token against the cloud API that fronts a fleet. Hit an unauthenticated /oauth2/token, exploit a broken-authorization endpoint, or lift a hard-coded key, and it now holds the fleet authority. From there it iterates account and org IDs and the EndDevice LFDIs behind them, then dispatches legitimate-looking control: active-power setpoints, forced disconnect/reconnect, coordinated on/off, or malicious firmware. Your IP allowlists pass it because the token is valid and the egress is ordinary; rate-limit it and a fresh IP appears, hopping across clouds and residential proxies, so all your SOC ever logs is a meaningless last IP. The root cause has a name: OWASP broken object-level authorization / BOLA, here at fleet scale. The token authorizes a claim against thousands of devices, never the machine.

One token → a whole fleet, and every last IP is disposable Stolen token = fleet authority OAuth · JWT · key valid Aggregator cloud API DERMS head-end · VTN one control plane claim ✓ · machine ✗ enumerate EndDevice LFDIs: the whole fleet dispatch: active-power setpoints · disconnect / reconnect · on/off …phoned home through disposable egress: AWS GCP Azure residential swarm SOC logs a meaningless last IP
The whole kill chain leans on one thing: the token authorizes a claim against thousands of EndDevice records, never the machine. The egress is disposable too, so the last IP the SOC records was never the attacker.

This is not hypothetical. Research has found ~35,000 internet-exposed solar devices across 42 vendors on Shodan; a single cloud-platform pair was shown to coordinate ~195 GW of solar where a stealable token took over any account; and ~800 PV-monitoring devices were hijacked into a Mirai botnet through one unauthenticated command-injection flaw. That was the first publicly confirmed cyberattack on PV generation. Detection will always trail a credential that is genuinely valid. The strictly-stronger move is to change what the backend trusts.

The cure: the address is the DER

Shipped & live. Deriving a DER or inverter /128 from the device's own key + the LFDI it already carries is in production today. Provision one with the control-plane call below, then verify it from the DNSSEC root with tools already on your machine.

Whisper gives each inverter (or each DER, battery gateway, or EVSE) a routable IPv6 /128 out of 2a04:2a01::/32 (announced by AS219419), derived deterministically from the device's public key: its SubjectPublicKeyInfo, the public half of the IEEE 2030.5 EndDevice certificate, a SunSpec/Kyrio device cert, a TPM, or a secure element, with the LFDI (or a DER serial) as the domain separator. The IEEE 2030.5 LFDI is itself the leftmost 160 bits of a SHA-256 over that certificate: a genuinely good key-derived name the standard already computes. 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.

the address is the DER Device key + LFDI IEEE 2030.5 EndDevice cert SunSpec-Kyrio · TPM · SE private key stays on-device public SPKI + LFDI /128 2a04:2a01:5e0::50c routable · tenant-bound DNSSEC · DANE 3 1 1 · RDAP A name anyone verifies whisper verify --trustless our API not in the trust path op:revoke → gone worldwide at DNS-TTL
The IEEE 2030.5 LFDI is already a SHA-256 of the device's certificate: a good key-derived name trapped in a private, non-revocable root. Whisper binds it to a routable, publicly verifiable /128 and gives it the off-switch its own PKI explicitly lacks.

Because the derivation is tenant-bound, the same device under two aggregators yields two unrelated /128s: an outsider cannot link a unit across operators. And because the domain separator is the LFDI, the LFDI alone yields nothing: it flows through every CSIP deployment and is not a secret, yet you cannot go LFDI → /128 without the key, there is no enumerable directory, and RDAP and reverse-DNS return the registry object, never the device's whereabouts.

What becomes true the moment a DER holds one:

Additive, never a replacement. Whisper complements the anchors you already ship: the IEEE 2030.5 / SunSpec / Kyrio EndDevice certificate, the private SERCA→MCA→MICA root, IEC 62351 on the substation bus, ISO 15118 Plug & Charge, TPM/HSM/secure elements. It is the publicly verifiable, DNSSEC/DANE-anchored layer on top, anchoring the device↔cloud boundary. You can even DANE-pin your existing 2030.5 endpoint's certificate to DNSSEC and cut single-CA trust risk. Whisper never reaches into the IEC 61850 GOOSE/MMS bus, the DNP3 outstation link, or the ISO 15118 charging handshake; it keeps the LFDI's key-derived property and adds the two things that private root lacks: public verifiability and DNS-TTL revocation, without re-keying the 25M+ inverters already in the field.

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 regulator 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 a DER 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 LFDI; 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 device key>',
  device_id:'3F2504E04F8911D39A0C0305E82C33018B2E44F9'   // the IEEE 2030.5 LFDI
}}) 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:'3F2504E04F8911D39A0C0305E82C33018B2E44F9'}}) YIELD op, ok, status, result, error RETURN op, ok, status, result, error"}
JSON
# response
{ "op": "connect", "ok": true, "status": "created",
  "result": {
    "address": "2a04:2a01:5e0::50c",
    "fqdn":    "lfdi-3f2504e0.der.<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 LFDI returns the same /128; a different LFDI 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 the LFDI, an SFDI, or a bare DER serial: whatever native identifier the unit carries.

i

A dedicated --lfdi CLI flag is on the roadmap; today, DER 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 DER 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 lfdi-3f2504e0.der.<tenant>.agents.whisper.online

 DNSSEC chain valid to the IANA root
 DANE-EE (TLSA 3 1 1) leaf matches the identity's key
 RDAP: registered under AS219419 · 2a04:2a01::/32
identity: VERIFIED, and our own API was never trusted

Or reach for the raw records directly. Same answer, from stock tools:

# the public verify endpoint: evidence chain in JSON
curl -s https://whisper.online/verify-identity/2a04:2a01:5e0::50c | jq
{ "is_whisper_agent": true, "dane_ok": true, "jws_ok": true, "evidence": { /* … */ } }

# the address is the inverter: forward-confirmed reverse DNS names it
dig -x 2a04:2a01:5e0::50c +short
lfdi-3f2504e0.der.<tenant>.agents.whisper.online.

# the registry object: who holds the address, and under which allocation
curl -s https://whisper.online/ip/2a04:2a01:5e0::50c | jq

None of these calls Whisper as an authority: --trustless re-derives the proof against the public DNSSEC root, exactly as any resolver could. See Verify an agent for the full keyless check and DANE & TLSA for the pin, byte for byte.

Revoke worldwide, govern in between

A compromised inverter, a module swap, a change of aggregator, 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:5e0::50c'}})

# after the TTL: dig -x returns nothing, verify returns false
whisper kill --revoke 2a04:2a01:5e0::50c

Compromise one inverter and you've compromised that inverter, not the fleet. The fleet-takeover failure mode is structurally removed. Revocation is the kill-switch, but the same control plane also governs what a live DER may reach in between. Egress is source-bound to the device's /128, so policy is enforced by name and by address:

# default-deny: this inverter may reach ONLY its DERMS head-end and OTA endpoint
whisper policy set --default deny --allow derms.example-vpp.com,ota.example-vpp.com

# per-device firewall (allow/deny by host, cidr or port) and a traffic budget + kill-switch
CALL whisper.agents({op:'firewall', args:{agent:'2a04:2a01:5e0::50c', deny:['0.0.0.0/0'], allow:['derms.example-vpp.com:443']}})
CALL whisper.agents({op:'budget',   args:{agent:'2a04:2a01:5e0::50c', max_mb_per_day:50}})

And because each dispatch and telemetry stream can be bound to (and signed under) the DER's forge-proof /128, the ISO, the utility, and your own market settlement can trust the numbers came from the real device, not a spoofed feed. See Egress governance for the full policy surface.

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. Run it 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(\"34.90.x.x\")"}'
# operator fingerprinted across AWS / GCP / Azure; residential swarm 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. More in Graph & cognition.

Lookups: see who's enumerating your fleet

An identity you can prove is also an identity you can watch. Because every DER's name resolves through Whisper's own authoritative DNS and RDAP, the owner can ask who looked: a reconnaissance tripwire the LFDI's private, out-of-band registry never gave you. op:lookups returns who resolved or RDAP-queried a device's identity, so you see someone enumerating your fleet before the command lands, not in the post-mortem afterward:

CALL whisper.agents({op:'lookups', args:{agent:'2a04:2a01:5e0::50c', window:'24h'}})

# the same reverse-observability view, keyless, per address
curl -s https://whisper.online/ip/2a04:2a01:5e0::50c/lookups | jq
# → who resolved this DER's PTR/AAAA/TLSA and hit its RDAP object, and when

Paired with op:logs (the device's own outbound activity) and /ip/<addr>/transparency (its ordered lifecycle), you have both halves of the picture: what a DER reaches out to, and who is reaching in to look at it.

What ships today, and what's on the roadmap

We label these honestly so you can plan against them.

Shipped & liveOn the roadmap
DER/inverter /128 from the device key + LFDI: DNSSEC + DANE-EE + RDAP A dedicated --lfdi CLI flag (provision via the control-plane call today)
Control-plane provision, verify, revoke; 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) E-ISAC machine-readable JSON export

The integration guides below describe proposed integrations at the cloud and IP boundary, designed to complement the stack you already run. They're not endorsed by any vendor, and never named against a specific operator as a breach victim.

The five Energy guides

The energy story, in depth: each page is self-contained and copy-paste runnable.

The full technical library

Energy rides on the same address-is-identity platform as every other agent on the network, so the whole shared library applies here unchanged. Every page has a clean Markdown twin at the same path + .md. Start with these; the rest is in the sidebar.