# 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](https://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](https://owasp.org/API-Security/editions/2023/en/0xa1-broken-object-level-authorization/), 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         Aggregator cloud API     EndDevice LFDIs: the whole fleet
  = fleet authority ─▶ DERMS head-end · VTN ─┈▶ ▢ ▢ ▢ ▢ ▢ ▢ ▢ ▢ ▢
  OAuth · JWT · key    claim ✓ · machine ✗      ▢ ▢ ▢ ▢ ▢ ▢ ▢ ▢ ▢
     (valid)              (one control plane)   ┌───────────────────────────────┐
                                                │ dispatch: 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](/docs/dnssec)-anchored, [DANE-EE `3 1 1`](/docs/dane) pinned, and [RDAP](/docs/rdap)-registered: re-derivable and verifiable by anyone with `dig`.

```
device public key (SPKI)      ──public SPKI + LFDI──▶   /128                   ──DNSSEC · DANE 3 1 1 · RDAP──▶   a name anyone verifies
IEEE 2030.5 EndDevice cert /                            2a04:2a01:5e0::50c                                     whisper verify --trustless
SunSpec-Kyrio / TPM / SE                                routable, tenant-bound                                 (our API not in the trust path)
(private key stays on-device)                                                                                 └─▶ 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 `/128`s: 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:

- **"One token → a whole fleet" becomes physically impossible.** You cannot present thousands of DER identities whose keys you don't hold; every forgery is a DNSSEC/DANE inconsistency any verifier catches.
- **IP rotation becomes irrelevant.** Identity is not the source IP. The "last IP" was never the credential, so rotating it across clouds or residential proxies changes nothing.
- **Stolen tokens fail.** A minted or reused bearer with no device key behind it authenticates to nothing; the dispatch path checks the inverter, not the bearer.
- **One `revoke` kills a compromised inverter worldwide** at DNS-TTL speed: the off-switch IEEE 2030.5 never had. Its own MPKI is explicit that *"no certificates … can be revoked … CAs are not required to issue CRLs nor provide OCSP"*: life-long certs that cannot be updated or revoked.

**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](/docs/dane) 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](/docs/transparency), 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:

```sh
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
```

```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.

> 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](/docs/cli).

## 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:

```sh
# 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](/docs/verify) for the full keyless check and [DANE & TLSA](/docs/dane) 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](/docs/sign-outputs)) 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](/docs/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):

```sh
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](/docs/graph-api).

## 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 & live | On 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.

- **[DER & inverter identity](/docs/industries/energy/der-identity).** Derive a routable `/128` from the IEEE 2030.5 `LFDI` a DER already carries. Deterministic, tenant-bound, DNSSEC + DANE-EE pinned: the device-identity spine.
- **[Grid-API-abuse cure](/docs/industries/energy/grid-api-abuse-cure).** Why a stolen aggregator token dispatches your whole fleet, and how a forge-proof address ends it. The BOLA root cause, cured at the identity layer.
- **[Platform integrations](/docs/industries/energy/energy-integrations).** Proposed integrations at the cloud/IP boundary: IEEE 2030.5 / CSIP head-ends, DERMS & VPP, OpenADR VTN, SunSpec, AWS IoT. Complements, never replaces.
- **[NERC CIP · IEC 62351 · Rule 21](/docs/industries/energy/energy-compliance).** Map identity and attribution evidence to NERC CIP-013/005, IEC 62351, CA Rule 21, EU NIS2/NCCS, and NISTIR 7628, as a network primitive, not a binder.
- **[Telemetry · egress · attribution](/docs/industries/energy/energy-recipes).** Runnable recipes: bind non-repudiable telemetry to a DER's `/128`, default-deny a compromised inverter's egress, back-trace a suspicious controller on the graph.

## 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.

- **[Quickstart](/docs/quickstart).** Install, register your first identity, connect it, confirm it, all in one terminal, start to finish.
- **[Verify an agent](/docs/verify).** The full keyless identity check: every proof you run with `dig`, `curl`, and `openssl`.
- **[DANE & TLSA](/docs/dane).** The `3 1 1` pin that makes an address forge-proof, byte for byte, no CA in the path.
- **[Control plane](/docs/control-plane).** The full `whisper.agents` API (provision, connect, policy, logs, revoke) over the public endpoint.

---

← [For grid security](https://energy.whisper.online/for-grid-security) · [DER & inverter identity →](/docs/industries/energy/der-identity)
