Whisper · Docs
Commerce

Agent identity

Give an AI agent a routable IPv6 /128 derived from the public key it already signs with and its agent id. The address is the agent: forge-proof, tenant-private, DNSSEC-anchored, DANE-EE pinned, and revocable worldwide in one call.

This is the spine of the commerce vertical, and the core /128 path is shipped and live. Everything else (the agent-fraud cure, the A2A / AP2 / x402 integrations, the PSD2 evidence) builds on the one idea below: an agent's network identity stops being a self-asserted card, a bearer token, or a pseudonymous wallet key. It becomes a cryptographic fact only that agent's own key can stand behind, one that any merchant, facilitator, or peer agent can check with dig and openssl without trusting Whisper, a card network, or a registry.

Two tiers, per Postel's Law. With no API key anyone can verify an agent's identity from stock tools (dig, curl, RDAP) because the identity is public by design. With your key you provision and govern: mint the /128, pin a wallet to it, source-bind its egress, pull its logs, and revoke it. Verification never needs an account; the control plane does.

The address is the agent

Every agent-commerce protocol shipped in the last year authenticates a claim, never a machine, and each one leaves the identity of the acting agent unanchored. Read them in the order a request travels, top of the stack down:

Three protocols, one shared hole: a merchant, a facilitator, or a peer agent cannot cryptographically answer "which agent am I really transacting with, and can I revoke it?" The card networks conceded as much when they shipped an open framework in late 2025 explicitly to help merchants "distinguish malicious bots from legitimate AI agents." Each protocol's identifier is stable and already flows through every deployment: the A2A agent id, the AP2 mandate signer, the x402 wallet. Whisper takes that identifier as the device_id and turns it into an address the whole internet can check.

How the derivation works

The /128 is not assigned from a pool and written into a database. It is computed, the same way on every node, from inputs the agent already has. Two things go in (a third is optional):

Input What it is Where it lives
Agent public key the SubjectPublicKeyInfo (SPKI) of the key the agent already signs with: the key behind its A2A AgentCardSignature, its AP2 mandate signature, or a key it controls the public half is submitted; the private key never leaves the agent
Agent id (device_id) the agent's own stable identifier (the A2A agent id, the AP2 mandate-signer reference, or the x402 wallet address) used as the domain separator submitted with the request; the public index
Sub-agent id (optional) a per-sub-agent separator, so one principal can hold many addressable identities (a shopper, a negotiator, a settlement agent) optional; omit it for a single per-agent address

Those inputs are combined by a one-way derivation, scoped to your tenant, into a stable, unguessable interface identifier:

# inputs -> a stable, forge-proof interface identifier
derive( agent public key,  agent id [, sub-agent id],  your tenant )  -->  64 uniform bits

# the /64 prefix is your tenant block; the low 64 bits are the derived id
/128 = < your tenant /64 prefix > : < derived interface id >

Four properties fall straight out of that derivation, and each one is load-bearing:

what a counterpart is handed today: unanchored what Whisper makes of it: one verifiable address A2A Agent Card /.well-known/agent-card.json JWS signature: recommended, not mandated AP2 mandate signed VC · ECDSA P-256 signer identity: deferred to FIDO x402 wallet from: 0x8f3A…c0dE (bare EOA) pseudonymous · non-revocable agent id + public key device_id = the agent id private key stays with the agent derive /128 2a04:2a01:c0ff:ee5a::2f11 DNSSEC · DANE-EE 3 1 1 · RDAP verify with dig + openssl: no account, no registry One identity under all three protocols: the same address the card advertises, the mandate signs, and the wallet pays from.
A2A, AP2 and x402 each leave the acting agent's identity unanchored. Whisper takes the agent id and the agent's own public key and derives one routable, DANE-pinned /128 that any counterpart verifies against the IANA DNS root: no platform login, no network membership, no registry.

The DANE binding: a decentralized alternative to a registry

The moment the address is derived it is published as a full identity, atomically: an AAAA, a forward-confirmed PTR, and a DANE-EE TLSA 3 1 1 record that pins the agent's own leaf key. All of it is DNSSEC-signed to the IANA root and registered in RDAP. That TLSA pin is what turns "the address is derived from a key" into "the address is provable against that key by anyone with dig."

# the TLSA pin binds the agent's signing key to its name: DANE-EE, SHA-256
_443._tcp.a9e71c3d8b402f11.acme.agents.whisper.online. IN TLSA (
  3 1 1 9f2b7c04e1a63d58af0c2b19e4d7188c5a63b0f2e9d41c7a8b3e60f5d2c19a4b )

This is the decentralized answer to a question the A2A community is debating in the open (see A2A issue #1672): should an Agent Card be verified against a centralized registry of agent keys, or against a decentralized anchor? A registry means every counterparty must trust, and stay reachable to, one directory operator. DANE means the proof hangs off the DNS root itself: the same globally-distributed, cryptographically-chained infrastructure that already secures the web's names. A verifier resolves the agent's /128, reads the DNSSEC-signed TLSA, and confirms this card's signing key belongs to this agent, a stronger statement than a CA's "belongs to this domain," and one no single registry can revoke out from under you.

The same identity is also published as a did:web document, so AP2's "DIDs as agent roots-of-trust" model and its allowlist-registry check can consume it directly: the agent's did:web resolves to a document whose verification method is the very key the TLSA pins. One derivation, three shapes: an address, a DANE pin, and a DID, all naming the same key. See DANE & TLSA for the byte-for-byte record and DNSSEC for the chain it hangs from.

The private key never moves. The agent submits only its public SPKI, the same public half of the key it already signs A2A cards or AP2 mandates with. The server derives a public address from public inputs plus a server-side secret; it never sees, holds, or derives the agent's private key. The agent proves ownership later by presenting its own key against the DANE pin: an A2A MutualTls security scheme anchored in DNS, not a private CA.

Agent key device_id = agent id key stays with the agent /128 2a04:2a01:c0ff:ee5a::2f11 Verifiable name DNSSEC · DANE 3 1 1 dig + openssl, no account Pin a wallet x402 from → identity known, revocable payer Revoke gone worldwide at DNS-TTL one key, one address, one lifecycle: derive it, prove it, pin a wallet to it, revoke it
The lifecycle in one line: the agent's own key (named by its agent id) derives the /128, DNSSEC + DANE make it verifiable, a wallet is pinned to the verifiable identity, and one call revokes the whole thing worldwide.

Provision an agent identity

Provisioning is one control-plane call: whisper.agents with op:'connect', tier:'wireguard', the agent's public SPKI, and its agent id as device_id. It returns the deterministic /128 and a ready WireGuard configuration so the agent's traffic sources from its own identity. The endpoint is POST https://graph.whisper.security/api/query, authed with an X-API-Key header; no key ever travels in the body.

CALL whisper.agents({op:'connect', args:{
  tier:                'wireguard',
  identity_public_key: '<base64 SubjectPublicKeyInfo of the agent signing key>',
  device_id:           'acme-shopper-8a3f'   // the A2A agent id / mandate signer / wallet ref
  // sub_agent_id: 'settlement'   // optional: a distinct /128 per sub-agent
}}) YIELD op, ok, status, result, error
   RETURN op, ok, status, result, error

With stock tools: just curl, no Whisper software. A quoted heredoc keeps the Cypher single-quotes intact so it pastes and runs as-is:

curl -s https://graph.whisper.security/api/query \
  -H "X-API-Key: whisper_live_xxx" \
  -H "content-type: application/json" \
  -d @- <<'JSON' | jq .
{"query":"CALL whisper.agents({op:'connect', args:{tier:'wireguard', identity_public_key:'MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...<SPKI>...', device_id:'acme-shopper-8a3f'}}) YIELD op, ok, status, result, error RETURN op, ok, status, result, error"}
JSON

The response is the standard envelope; result carries the derived address, its verifiable name, and the transport. Because the agent holds its own key, no private key is ever returned, only the public identity and the config that binds egress to it:

{
  "op": "connect", "ok": true, "status": 200,
  "result": {
    "tier":               "wireguard",
    "address":            "2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11",   // the deterministic /128
    "fqdn":               "a9e71c3d8b402f11.acme.agents.whisper.online",
    "tlsa":               "3 1 1 9f2b7c04e1a63d58af0c2b19e4d7188c…",
    "server_public_key":  "…",
    "endpoint":           "…:51820",
    "dns":                "2a04:2a01:0:53::53",
    "wireguard_config":   "[Interface]\nAddress = 2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11/128\n…"
  },
  "error": null
}

Drop the returned wireguard_config onto the host running the agent (or feed it to wireproxy for a no-root, userspace tunnel) and every packet it sends now sources from its own /128. The backend authorizes on that address: a forge-proof, attributable, revocable network identity instead of a bearer token anyone could carry. For the full transport mechanics and the SOCKS5 / AnyIP alternatives, see Connect & egress; for every other op on this same endpoint, the Control plane reference.

The whisper CLI ships create --register, verify --trustless, policy, logs, and kill --revoke. A first-class typed --agent-id / --wallet flag is on the roadmap, not shipped. Provision agents today via the control-plane call above, passing your agent id as device_id, which is live. When the flag lands, it will be a thin wrapper over exactly this call.

Idempotent, with honest errors

Because the address is derived, provisioning is naturally idempotent, and the failure modes are clear rather than surprising. Postel all the way down:

You send You get
the same key + agent id again (same tenant) the same /128 (a re-derivation, not a new allocation)
the same key with a different agent id (same tenant) 409: the reused identity is never silently re-pinned to a mismatched address
a non-string device_id (or sub_agent_id) 400 with a helpful detail, never an opaque 500
device_id without identity_public_key 400: an agent derives its address from its own key

Verify: keyless, no account

The identity half is public on purpose: a merchant allowlisting a sanctioned shopper, a facilitator screening a payer, or a peer agent about to accept a task can all prove an agent's /128 without a Whisper account and without trusting Whisper's word. Four independent checks, all from tools already on the machine:

# 1. Forward-confirmed reverse DNS: the address names the agent, the name resolves back
dig -x 2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11 +short
a9e71c3d8b402f11.acme.agents.whisper.online.

# 2. The keyless verdict endpoint (takes an address or an FQDN; ?ip=<target> also accepted)
curl -s https://whisper.online/verify-identity/2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11 | jq .
{
  "is_whisper_agent": true,
  "dane_ok": true,
  "jws_ok": true,
  "evidence": { "aaaa": "...", "ptr": "...", "tlsa": "3 1 1 9f2b7c04e1a6…2c19a4b" }
}

# 3. The registry record: RDAP, IP-anchored to the /128
curl -s https://whisper.online/ip/2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11 | jq '.handle, .parentHandle'

# 4. The full chain re-derived on YOUR machine, against the IANA root: Whisper NOT in the trust path
whisper verify --trustless a9e71c3d8b402f11.acme.agents.whisper.online

A target that isn't a Whisper identity gets a clean 200 {"is_whisper_agent": false}. A negative verdict is a successful answer, not an error; only genuinely malformed input draws a 400, never a 500. --trustless is the strong form: it validates DNSSEC from the root in-process, on your resolver, so the proof holds even for a merchant or facilitator who won't take Whisper's word for anything. This is the check that turns "is this a real agent?" from a guess into a lookup. The full walk lives in Verify an agent, and the pre-transaction A2A handshake in Agent-to-agent trust.

Pin a wallet to the identity

x402 proves an agent controls funds; it says nothing about who the agent is. Pin-a-wallet closes exactly that gap: the wallet↔identity binding AP2 explicitly defers to FIDO. The agent signs a short statement, "this from wallet is operated by the agent at </128>," using the very key its /128 is derived from, and publishes that signature alongside its identity. A resource server or facilitator resolves the agent's /128, verifies the signature against the agent's DANE pin already in DNS, and only then honors the 402: no change to the x402 flow, no on-chain write, no settlement by Whisper.

What's shipped, and what you assemble. The /128, its DANE-EE pin, keyless verify, and the per-agent CA leaf that underpins signed agent outputs are shipped and live. A turnkey sign-outputs receipt workflow (whisper sign) is on the roadmap. Pin-a-wallet is a signed binding you assemble from those shipped parts: a signature the agent makes and anyone verifies keyless against the pin already in DNS, using stock openssl, not a single new command. A turnkey facilitator middleware and a first-class typed --wallet / pin argument are on the roadmap; the derivation never accepts a caller-supplied pin (the DANE-EE pin is always computed server-side).

# On the agent: bind the x402 `from` wallet (an EIP-3009 EOA) to its /128 by signing with
# the identity key. The private key never leaves the agent; only the signature is published.
echo -n '{"wallet":"0x8f3A9c4e2b1d7a06f5c8e39b4d2a1f70c6e8c0dE",
          "agent":"2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11","chain":"eip155:8453"}' \
  > binding.json
openssl dgst -sha256 -sign identity-key.pem -out wallet-binding.sig binding.json
# On the facilitator, before /settle: 1) prove the /128 is a live, non-revoked agent:
whisper verify --trustless 2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11   # DANE-EE 3 1 1 → key is the agent's

# 2) check the binding's signature against that same DANE-pinned agent key, the `from`
#    wallet in the x402 PAYMENT-SIGNATURE header you're about to settle.
openssl dgst -sha256 -verify agent-identity.pub -signature wallet-binding.sig binding.json
Verified OK   # from-wallet 0x8f3A9c4e… ⇄ verifiable agent 2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11

The effect: a pseudonymous x402 payer becomes a known, accountable, revocable agent. Know-Your-Agent over x402 without changing x402. Revoke the /128 and the binding no longer resolves to a live identity, so the wallet stops being an accountable agent worldwide at DNS-TTL, even though the EOA key itself can never be revoked on-chain. The full recipe, including AP2 PaymentMandate instruments, is in Pin a wallet.

Honest scope. Pinning says which wallet an agent should hold and lets a counterpart verify and revoke that binding. It does not stop a thief who has stolen the wallet's private key (that needs key custody / HSM / mTLS), and Whisper never moves, settles, or tokenizes the funds. It is the missing "who owns this wallet" anchor, not a payment rail.

Revoke: worldwide, in one call

The gap every agent protocol shares is not latency. It is a missing revocation primitive. A2A, AP2, x402 and MCP have no agent revocation at all; Visa and Mastercard can revoke, but only inside their own network. When one domain revokes a compromised agent, connected domains have no standard way to hear about it, so a "revoked" agent keeps working elsewhere: a dormant but persistent threat. A compromised integration once spread across 700+ trust domains in ~10 days on stolen tokens, and revocation didn't propagate.

Whisper's revocation is global because it lives in the DNS root everyone already resolves. One revoke tears down the /128, its PTR, and its DANE pin across both authoritative servers, and the change propagates at DNS-TTL speed:

CALL whisper.agents({op:'revoke', args:{agent:'2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11'}})

# prove it: zero Whisper software, the same stock tools that proved it existed:
dig -x 2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11 +short           # -> nothing
curl -s https://whisper.online/verify-identity/2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11
# -> {"is_whisper_agent": false, ...}

Revocation isn't a database flag you have to trust; it's provable the same way the identity was. The reverse lookup goes empty and the keyless verdict flips to false for everyone, everywhere, at once. Every merchant, facilitator, and peer agent that verifies against the DNS root sees the agent go dark in the same lookup they already run. Contrast a shared bearer token, where a rotation reaches only the callers you can still reach.

See who checked it, and govern what it reaches

An identity you can prove is also one you can watch and bound. Because every agent's name resolves through Whisper's own authoritative DNS and RDAP, and its traffic sources from its /128, the owner gets a reconnaissance tripwire and a per-agent control plane the self-asserted card never offered.

Who checked this agent: a query

op:lookups returns who resolved or RDAP-queried an agent's identity: the counterparties verifying it before they transact, and the ones enumerating your fleet before they attack. It is the owner-facing companion to op:logs (the agent's own outbound): early warning that someone is probing, plus a verification-analytics stream, which merchants checked my agent, how often, from where.

curl -s https://whisper.online/ip/2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11/lookups | jq .
{ "resolved_by": 214, "rdap_queries": 37, "first_seen": "…", "top_asns": ["…"] }

Govern what the agent may reach

The same address is a policy handle. Default-deny an agent's egress and allow only the endpoints it needs; cap how much traffic its /128 can move on the network; cut it off:

# default-deny, allow only the merchant + the x402 facilitator this agent may pay
whisper policy set --agent 2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11 \
  --default deny --allow shop.example.com,facilitator.x402.org
# per-agent firewall by host / cidr / port: {action, match, value} rules + a default verdict
CALL whisper.agents({op:'firewall', args:{agent:'2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11',
  rules:[{action:'allow', match:'host', value:'shop.example.com'},
         {action:'allow', match:'host', value:'facilitator.x402.org'}], default:'deny'}})
# budget = a network egress circuit-breaker (bytes/requests), NOT a payment spend cap.
# Egress past the cap is denied; add kill:true, or one revoke, to cut it off entirely.
CALL whisper.agents({op:'budget', args:{agent:'2a04:2a01:c0ff:ee5a:a9e7:1c3d:8b40:2f11',
  bytes_down_cap:52428800, requests_cap:5000}})

These make every other control enforceable and auditable: a mandate scope, a spend cap, an injection-defense layer all attach to a durable, attributable subject instead of a bearer string. You can also sign the agent's outputs, a receipt, a mandate acknowledgement, a settlement record, under the same identity, so a downstream party trusts the artifact came from the real agent. Full surface in Egress governance.

Nothing issued in the dark

Every identity mint and every revoke lands in a public, append-only transparency log: an RFC 6962 tlog-tiles Merkle tree with Ed25519-signed C2SP signed-note checkpoints, each root anchored to Bitcoin via OpenTimestamps. So an issuance or a revocation can be independently checked and timestamped, a non-repudiable audit trail for the PSPs and card networks who need to show which agent was authorized, and when it was cut off.

Honest status. The log is tamper-evident today: append-only, Ed25519-signed, and Bitcoin-anchored, and it speaks the C2SP tlog-witness protocol so any external party can co-sign it. It is not yet independently witnessed: our two nodes co-signing is availability, not independence. We state that plainly; independent witnessing is the next step, not a shipped claim.

Attribution: name whoever already spoofed you

Identity stops the next forgery. To name whoever already shadow-cloned your Agent Card or ran a spoofed agent across rotating clouds and residential proxies, the same API key opens the read-only attribution graph on the same endpoint. whisper.identify takes an address and returns the operator behind it, stitched across hops that a raw last-IP loses:

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(\"203.0.113.45\")"}' | jq .

The read-only Cypher surface (identify, origins, walk, variants, history) runs over the same POST https://graph.whisper.security/api/query with your key, and returns a reproducible, replayable evidence chain you can hand a chargeback team or a regulator. There is no whisper identify CLI subcommand; this is the API call, and it is live. Full verbs and shapes in Graph & cognition.

Honest: Whisper is not the only DNS-anchored agent identity

As of 2026 there is a small, healthy movement to anchor agent identity in DNS, and Whisper did not invent the category. It deserves to be named:

These are complementary, and we cite them respectfully. What Whisper adds on top of a name or discovery record is a working identity layer: (1) a routable IPv6 /128, the address is the identity, which fuses identity, reachability, and egress governance in one primitive (every other approach lives at the app handshake: a card, a header, a TXT/SVCB label); (2) pin-a-wallet, binding an arbitrary x402 wallet to the verifiable identity; (3) per-agent revocation at DNS-TTL, one call, verifiable by anyone; and (4) a cross-platform attribution graph in public infrastructure. A discovery label tells you where an agent claims to be; a routable, revocable, wallet-pinned /128 is the identity you can verify, govern, and shut off.

Honest scope: necessary, not sufficient

Verifiable identity closes the "which agent / for whom / prove it / attribute it / revoke it" gaps the payment protocols left open, and it makes every other control enforceable and auditable. It is not a silver bullet, and we say so plainly. It does not:

Where this fits (and where it doesn't)

Whisper anchors the agent's identity at the agent↔counterparty IP/transport boundary: the network endpoint a merchant, facilitator, or peer agent authorizes. It is additive: it complements the protocols and directories you already run and does not try to replace them, and it never redefines the interop protocol or moves the money.

For mapping these identities to PSD2 / PSD3-PSR and the agent-payment protocols, and for SOC export, see PSD2 · agent-payment protocols. The Splunk, Microsoft Sentinel and OpenCTI connectors are shipped; STIX 2.1 / TAXII export is on the roadmap and labelled as such there. No specific company is named, endorsed, or implicated as a breach victim anywhere in these docs; the abuse patterns cited are the public, cross-industry ones.

Next