Whisper · Docs
Commerce

The agent-fraud cure

The whole agentic-commerce stack authenticates a claim, never the machine. An A2A Agent Card is a JSON file whose signature is optional; an AP2 mandate is signed but defers whose key signed it; an x402 payment is a bare wallet that proves control of funds, not which agent holds it. So an impostor presents a look-alike card, replays a captured mandate, or pays from a wallet no one can attribute. No revocation protocol shuts any of it down.

Whisper cures it by making the address be the agent: a routable IPv6 /128 derived from the key the agent already holds, DNSSEC-anchored and DANE-pinned, that no counterparty can forge and one call can revoke worldwide. A cloned card with no agent key behind it authenticates to nothing. This page walks the abuse end-to-end, the reframe, and the exact live calls: keyless to verify, one keyed call to provision, one more to revoke. It also covers the wallet binding the rails leave open and the attribution companion that names whoever already transacted in your agent's name.

The abuse, at class level

It is not a breach. The A2A, AP2 and x402 protocols are used exactly as they were built, by an agent that was never yours, wearing an identity no counterparty can check. The mechanics repeat across every rail, and they follow the same six moves every time.

the agent-fraud kill chain (no zero-day required) 01 · DISCOVER Enumerate the agents well-known cards · JWKS 02 · IMPERSONATE Shadow-clone the Agent Card or lift a bearer 03 · ENTER Auth as “an agent” holds the secret → in 04 · TRANSACT Inject / confused deputy → pay OWASP LLM01 05 · REPLAY Reuse mandate, rotate egress no id to correlate 06 · PERSIST Survive the “revoke” no cross-domain protocol Two invariants make it invisible at the network layer: ① a legitimate agent is one verified principal to one authorized action; the abuser is one operator to thousands. ② every artifact it presents is replayable, and every egress IP is disposable, so nothing on the wire pins it down.
No exploit chain: a self-asserted card and a bearer credential, used as designed, at scale. The chain leans on exactly two invariants, and neither is an IP or a session you can block.

Discover and impersonate. Agent Cards are fetched at /.well-known/agent-card.json (a well-known URI, RFC 8615) and the map of who-can-transact is public by design. A third party publishes a shadow-cloned card at a plausible domain (A2A does not mandate how a card is verified; it delegates that to implementers), or simply lifts a bearer token, a static API key, or a leaked wallet key. Now it holds a credential the counterparty cannot tell from the real one.

Enter the flow and transact. Auth says yes to whoever holds the secret, so the impostor becomes a participant inside an A2A/AP2/x402 exchange indistinguishable from a legitimate peer. From there the payment is triggered two ways. Prompt-inject the victim's own agent: injected instructions in a page, an email, or a tool result steer it into an unauthorized purchase or leak its token. Or play confused deputy, persuading an over-permissioned agent to spend “on your behalf” and drain a wallet faster than a human can react.

The root cause is a trust vacuum the rails were built without: today an agent is authenticated by a bearer credential, not a forge-proof identity. An API key “grants access to anyone who possesses it, not a verified identity”; an OAuth token is portable; an Agent Card is self-asserted. That leaves three structural holes every counterparty inherits: no forge-proof identity (steal the token, become the agent), no cross-platform attribution (each domain validates in isolation), and no revocation (“when one domain revokes, connected domains have no standard mechanism to receive that signal”: not a latency problem, but a missing protocol).

Replay, rotate and persist. The abuser reuses a captured mandate (or exploits an SDK signature-verification bypass of the kind disclosed against x402 tooling in 2026), then rotates its egress across AWS, GCP and Azure, or a residential-proxy swarm, so with no stable identity to correlate, its sessions cannot be linked. Finally it survives the response: there is no cross-domain revocation protocol, so a compromised identity keeps working elsewhere, and a “revoked” agent often keeps its registration: a dormant but persistent threat. This is not hypothetical. A single compromised agent integration spread across 700+ trust domains in about ten days on stolen OAuth tokens, and the revocation never propagated. Analysts project non-human identities now outnumber humans ~109:1, most authenticated by a bearer secret, and the WEF estimates ~1 in 4 data breaches could stem from AI-agent exploitation by 2028.

Detection will always be a step behind a credential that is genuinely valid and a card that is well-formed. You can tune fraud models forever and the abuser still looks exactly like a legitimate agent. To your backend, it is one. The strictly-stronger move is to change what the counterparty trusts.

The reframe: the address is the agent

Whisper has one primitive: the address is the identity. A routable IPv6 /128 out of 2a04:2a01::/32 (announced by AS219419), deterministically derived from a key, DNSSEC-signed to the IANA root, DANE-EE pinned (3 1 1), and RDAP-registered: re-derivable and verifiable by anyone with dig.

Point it at the agent. Whisper derives each agent's /128 from the public key it already holds (the same key behind its A2A Agent Card signature, its AP2 mandate signer, or its x402 wallet), with the agent id as its domain-separating input, which you pass as device_id. The derivation is deterministic and tenant-bound: the same device key and agent id always yield the same /128, and the mapping is unlinkable across marketplaces to an outsider. The private key never leaves the agent; only its public SubjectPublicKeyInfo is an input.

The counterparty then authorizes on the agent's pinned identity, not a stealable artifact: a request either proves it is the agent it claims to be, before a single fraud rule runs, or it has no authority at all.

TODAY: authorize a CLAIM (self-asserted) Self-asserted id card · bearer · wallet Counterparty claim ✓ · agent ✗ any account → any agent reach any card → transact as any agent UNDER IDENTITY: authorize the AGENT Request mutual-auth to the target /128 key ✓ One agent, its pinned /128 DANE-EE 3 1 1 · the agent co-signs no key authenticates to nothing A cloned card still parses… …but the request must prove the agent's key, which the impostor never holds. Impersonation loses its leverage: reaching any card no longer reaches any agent.
Detection asks “is this caller behaving?” Identity asks “is this the agent?” That's a strictly stronger question, one a self-asserted claim cannot answer. The impersonation gap closes because the identity now has a key of its own.

The agent id is the public index. The /128 is its cryptographic counterpart. An agent id is meant to be shared: it flows through every A2A discovery and every mandate, and that is exactly what an impostor weaponizes. But the /128 derives from the in-agent key with the id as its domain-separating input, so the id alone yields nothing. You cannot go id → /128 without the key, there is no enumerable directory, and RDAP and reverse DNS return the registry object, never where the agent runs. The same key under two platforms yields two unrelated /128s, so no outsider can link an agent across marketplaces.

What changes

Nothing here is a new fraud rule. Each row is an abuse technique that stops being possible, not one you catch after the fact.

The abuse today Why it dies under identity
Present a shadow-cloned Agent Card You cannot present an agent identity whose device key you do not hold. Every forgery is a DNSSEC / DANE inconsistency any verifier catches with stock tools: dig -x names an identity whose AAAA and TLSA don't agree, before the mandate is signed.
Rotate egress across clouds / residential proxies Identity is not the source IP. The last IP was never the credential, so rotating it (across AWS, GCP, Azure, or a proxy swarm) changes nothing about whether the caller can prove the agent.
Replay a captured mandate or a stolen bearer The artifact has no agent key behind it. A state-changing request terminates mutually-authenticated to the agent's /128, and the agent co-signs, so a valid-looking mandate that can't prove the signer's identity never had authority. Reaching any account no longer reaches any agent.
Pay from an unattributable x402 wallet The wallet is pinned to the verifiable identity: a facilitator resolves the agent, checks the DANE pin, and confirms the wallet is bound to a known, non-revoked agent before honoring the 402. That's the “who” x402 deliberately left out.
Survive the “revoke” and persist elsewhere One revoke tears down the /128, its PTR, and its DANE pin worldwide at DNS-TTL speed, verifiable by anyone, no membership required. No dormant registration outlives its own revocation; compromise one agent and you have compromised that agent, not every counterparty it ever touched.

Provision an agent identity

Provisioning is one control-plane call to POST https://graph.whisper.security/api/query with your X-API-Key header. You pass the agent's public key material (the base64 SubjectPublicKeyInfo of the key behind its Agent Card / mandate signer / wallet) and its agent id as the device_id; you get back the deterministic /128 and a WireGuard config to source the agent's traffic from that address.

The call

CALL whisper.agents({op:'connect', args:{
  tier:'wireguard',
  identity_public_key:'<base64 SPKI of the agent key>',   # public half of its Agent-Card / mandate / wallet key
  device_id:'ag_9e0a9e7c14ab2e44f9'   # the agent id, carried in A2A / AP2 / x402
}}) YIELD op, ok, status, result, error
RETURN op, ok, status, result, error

Over stock tools: jq builds the JSON body so the Cypher's own quotes never fight the shell:

# the public key only: the private key never leaves the agent's process / secure element
Q="CALL whisper.agents({op:'connect', args:{tier:'wireguard', \
   identity_public_key:'MFkwEwYHKoZIzj0…SPKI', device_id:'ag_9e0a9e7c14ab2e44f9'}}) \
   YIELD op, ok, status, result, error RETURN op, ok, status, result, error"

curl -s https://graph.whisper.security/api/query \
  -H "X-API-Key: whisper_live_xxx" \
  -H "content-type: application/json" \
  --data "$(jq -nc --arg q "$Q" '{query:$q}')"

The response

# result carries the deterministic identity plus a ready-to-apply tunnel
address        2a04:2a01:9e0::a9e7
fqdn           ag-9e0a9e7.<tenant>.agents.whisper.online
ptr            ag-9e0a9e7.<tenant>.agents.whisper.online
state          active                       # DNSSEC + DANE-EE (3 1 1) live at provision time
wireguard_config   [Interface] …            # source the agent's traffic from its own /128

The agent now has a name it can prove and an address it egresses from. Reverse DNS resolves the /128 to that identity, a TLSA record pins the leaf key, and RDAP registers the object under 2a04:2a01::/32. That's the full seven-proof chain, published atomically with the allocation, with each mint recorded in the Merkle transparency log.

Idempotency and errors

The call is deterministic and honest about conflicts: conservative in what it emits, liberal in what it accepts.

You send You get
The same device key + agent id again The same /128: idempotent, safe to retry, safe to run on every start-up. No duplicate identities.
The same device key with a different agent id on your tenant 409 Conflict. A device key binds to exactly one identifier, and the error.detail tells you which id it is already bound to.
A non-string device_id (a number, an array, null) 400 with an actionable detail, never an opaque 500. Send the agent id as a string.

Shipped & live. This provisioning path (an agent /128 derived from the agent's public key + its device_id) is in production today. The device_id argument is generic: pass an A2A agent id, an AP2 signer reference, or your own agent handle. A first-class typed --agent-id flag is on the roadmap; today, provision through the control-plane call above, then drive it with whisper verify / whisper policy / whisper logs / whisper kill --revoke.

Revoke worldwide and govern in between

A compromised agent, a key rotation, an ownership change, a decommission: one call tears down the /128, its PTR, and its DANE pin everywhere at DNS-TTL speed. It's provable with the same stock tools that proved the identity existed, no Whisper software required.

# the control-plane op…
CALL whisper.agents({op:'revoke', args:{agent:'2a04:2a01:9e0::a9e7'}})
# …or the CLI
whisper kill --revoke 2a04:2a01:9e0::a9e7

# now prove it, worldwide, at DNS-TTL speed:
dig -x 2a04:2a01:9e0::a9e7 +short                       # -> nothing
curl -s https://whisper.online/verify-identity/2a04:2a01:9e0::a9e7
# -> {"is_whisper_agent": false, ...}

Revocation is the kill-switch the protocols never defined. But the same control plane governs what a live agent may reach in between. Because egress is source-bound to the agent's /128, policy is enforced by name and by address: default-deny, allow only your PSP and your facilitator, cap the traffic and spend, and kill it in one call:

# default-deny: this agent may reach ONLY its PSP and its x402 facilitator
whisper policy set --default deny --allow api.your-psp.com,facilitator.x402.org

# per-agent firewall (allow/deny by host, cidr or port) + a budget with a kill-switch
CALL whisper.agents({op:'firewall', args:{agent:'2a04:2a01:9e0::a9e7', deny:['0.0.0.0/0','::/0'], allow:['api.your-psp.com:443']}})
CALL whisper.agents({op:'budget',   args:{agent:'2a04:2a01:9e0::a9e7', max_mb_per_day:200}})

A compromised agent can't phone an exfil host it was never allowed to reach, and a drained-wallet incident stops itself at the budget ceiling. This is the least-privilege layer identity makes enforceable: it does not stop a fully-authorized agent's bad decision, but it bounds the blast radius and makes every action attributable. Full policy surface: Egress governance.

Pin the agent's wallet: accountable x402

x402 is deliberately pseudonymous: the payment is an EIP-3009 authorization signed by the from wallet (an externally-owned account that proves control of funds, not which agent holds it, and that cannot be revoked). Pin-a-wallet converts that pseudonymous wallet into an accountable, revocable, publicly-verifiable agent, without changing a single line of the x402 flow. It is the exact wallet↔agent binding x402 omits and AP2 explicitly defers to FIDO.

The binding is a signed statement in the agent's own name, not a side database anyone has to trust. The agent issues a compact Verifiable Credential: a JWS whose issuer and subject are the agent's own did:web, signed with the same P-256 key family that backs its DANE TLSA pin. It asserts “this wallet is mine,” and publishes it next to its did.json:

pin-a-wallet: the x402 “from” bound to a verifiable, revocable agent The agent's /128 2a04:2a01:9e0::a9e7 DNSSEC · DANE-EE 3 1 1 did:web + TLSA key signs, same key AgentWallet credential (JWS) /.well-known/wallets/eip155-8453.jwt "address":"0x9F2b…c41A" · ES256 resolve + verify Facilitator, before the 402 ✓ DANE pin = the signing key ✓ VC binds this from-wallet ✓ /128 not revoked · tlog ⩽ tx No onchain change, no new registry to join. A bare EOA becomes a known, revocable agent paying, and one op:revoke ends the agent's ability to transact even though the key itself can't be revoked.
The identity underneath the wallet is what any forgery must defeat first: DNSSEC, DANE and reverse DNS all have to lie in agreement, the same bar every other Whisper proof sets. Full mechanics, including the transparency-log timestamp check: Pin a wallet.
# the agent publishes a signed wallet binding next to its did.json (issuer = subject = the agent)
# header:  {"alg":"ES256","kid":"did:web:ag-9e0a9e7.<tenant>.agents.whisper.online#key-1"}
# payload: {"vc":{"type":["VerifiableCredential","AgentWallet"],
#           "credentialSubject":{"chain":"eip155:8453","address":"0x9F2b…c41A","purpose":"x402"}}}
curl -s https://ag-9e0a9e7.<tenant>.agents.whisper.online/.well-known/wallets/eip155-8453.jwt

# a facilitator verifies the agent BEFORE honoring the 402: keyless, no account, no onchain change
whisper verify --trustless 2a04:2a01:9e0::a9e7          # establishes key-1 (DANE pin, DNSSEC to the IANA root)
# then check the JWS signature against that key with any stock JOSE library, and confirm it names the from-wallet

Honest status. Pin-a-wallet composes from primitives that ship today: did:web, DANE, the transparency log, and the keyless whisper verify --trustless check, plus one DIY step: signing the credential with a stock JOSE library. There is no single whisper pin-wallet command yet, and whisper verify today takes an address or FQDN, not a --credential file, so the credential's own signature is checked with the JOSE step above. It is the pattern you assemble from shipped parts. The full recipe: Pin-a-wallet · A2A-trust · verify.

Verify it: keyless, no account

The identity is public by design, so anyone (a merchant, a facilitator, a peer agent, a PSP, an auditor) can check an agent without your key and without taking Whisper's word for it. This is the keyless half of the two-tier surface: verify with no key, provision and govern with your key. It is also the a2a-trust step A2A and MCP lack: before two agents talk, each resolves the other's /128 and checks the DANE pin.

# no key, no account: re-derive and verify the agent's identity, trustless to the IANA root
whisper verify --trustless 2a04:2a01:9e0::a9e7

# or with only curl: the keyless full-chain verdict
curl -s https://whisper.online/verify-identity/2a04:2a01:9e0::a9e7
# { "is_whisper_agent": true, "dane_ok": true, "jws_ok": true, "evidence": { ... } }

# the address IS the agent: forward-confirmed reverse DNS names it
dig -x 2a04:2a01:9e0::a9e7 +short
# ag-9e0a9e7.<tenant>.agents.whisper.online.

# the registry object for the /128: RDAP, typed JSON
curl -s https://whisper.online/ip/2a04:2a01:9e0::a9e7 | jq '.handle, .parentHandle'
# "2A04:2A01:9E0::A9E7/128"
# "2A04:2A01::/32"

The --trustless flag is the point: nothing there calls back to Whisper's own API as an authority. The CLI re-derives the DNSSEC chain to the IANA root, on your machine, with your resolver. A merchant can verify an agent without joining Visa's directory or Mastercard's registry, and without trusting the platform that issued it. Full mechanics: Verify an agent and DANE & TLSA.

See who checked your agent

An identity you can prove is also an identity you can watch. Because every agent's name resolves through Whisper's own authoritative DNS and RDAP, the owner can ask who looked: both halves of the picture, and neither one the well-known JWKS ever gave you. A legitimate merchant vetting your agent before it transacts shows up here as verification analytics; an adversary enumerating your fleet shows up as a reconnaissance tripwire (step 01 of the kill chain), surfaced while it's happening:

# who resolved / RDAP-queried this agent's identity, and when
CALL whisper.agents({op:'lookups', args:{agent:'2a04:2a01:9e0::a9e7', window:'24h'}})

# the same reverse-observability view, keyless, per address
curl -s https://whisper.online/ip/2a04:2a01:9e0::a9e7/lookups | jq
# → one source resolved 4,000+ agent PTR/AAAA/TLSA records in 30 minutes: a fleet sweep, before any mandate is signed

Paired with op:logs (the agent's own outbound activity) and /ip/<addr>/transparency (its ordered lifecycle), you have what an agent reaches out to and who is reaching in to enumerate it: the cross-platform visibility each domain-in-isolation never had.

Name what already transacted as you

Identity stops the next impostor. It does not name the operator behind the sessions already in your logs, so the same platform back-traces them. The attribution survives the rotation because it fingerprints the operator's infrastructure and tooling, not the ephemeral egress IP.

what your fraud team sees: a rotating, meaningless “last IP” Suspect sessions from your logs cloned card AWS eu-central 3.68.x.x GCP europe-w4 34.90.x.x Azure westeu 20.61.x.x residential-proxy swarm 71.x · Comcast 82.x · KPN 99.x · Orange infra genealogy JA4 fingerprint One operator ASN + hosting 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 IP: the JA4 fingerprint lives in the TLS handshake the proxy can't rewrite. The one input never relied on is the last IP.

Take a suspect egress IP straight from your fraud logs and ask the graph who really operates it. This runs read-only over the same public graph API, with your key:

# who really operates a host, even behind a CDN or a cloud front
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) run over that one endpoint against a live internet-infrastructure graph of fused BGP, DNS, WHOIS, TLS, hosting, and threat intelligence. Cloud rotation collapses through origins and walk, which cluster shared ASN, hosting, and certificate lineage into one infrastructure genealogy. You can express agent impersonation directly as read-only Cypher, such as “one source transacting as N distinct agent identities in a window.” Every answer is reproducible, replayable JSON: the paper trail a chargeback fight or a PSD3 audit needs, not a screenshot.

These graph verbs are the API surface: you call the endpoint directly, as above. There is no whisper identify / graph / export CLI subcommand; the CLI covers the control plane (create, verify, policy, logs, kill), and the graph is the query API. See Graph & cognition.

Honest scope: what this is, and what it is not

A verifiable, revocable agent identity is necessary. It is not, by itself, sufficient, so here is the candid line. Whisper closes the “which agent / for whom / prove it / attribute it / revoke it” gaps the payment protocols left open, and makes every other control enforceable and auditable. It is deliberately not several other things.

The honest one-liner: verifiable identity is the missing anchor the rails were built without, and the thing that makes spend caps, mandate scope, injection defense and human approval actually mean something. Not a substitute for the guardrails on top; the foundation they stand on.

Where it fits: standards, SIEM, integrations

Whisper is additive. It rides on top of the protocols you already speak and the SIEM you already run. It replaces none of them, and it never settles a payment.

Compliance. The per-/128 egress logs, the op:lookups stream, and the attribution graph are ready-made evidence for exactly the questions the new rails ask. Pinned identity plus one-call revocation maps cleanly to Mastercard Agent Pay (“only registered agents can transact, governed and traceable,” authorization revocable in real time; Whisper's “revocable in one call” mirrors the registry pull), and to Visa's Trusted Agent Protocol, where an agent proves identity with RFC 9421 HTTP Message Signatures against a well-known key directory. DNSSEC/DANE is a well-known, publicly-verifiable public-key directory, network-neutral instead of one silo. It is the durable identity an OpenAI/Stripe ACP delegated-payment token or an AP2 mandate points at, and it delivers the KYA pillars of “crypto identity” and “continuous accountability” directly. For PSD2/SCA and FATF Travel-Rule attribution the mapping is evidentiary and forward-looking, never a certification. The clause-by-clause detail lives in PSD2 · agent-payment protocols.

Nothing issued in the dark. Every identity mint and every revoke lands in a public, append-only RFC 6962 Merkle transparency log: Ed25519-signed C2SP checkpoints, each root anchored to Bitcoin via OpenTimestamps. It's an auditable, non-repudiable issuance and revocation trail a dispute or a regulator can replay, and the timestamp that proves a wallet binding predates the transaction under scrutiny. Honest status: it is tamper-evident and Bitcoin-anchored today, but not yet independently witnessed (our two servers co-signing is availability, not independence); the log already speaks the C2SP witness protocol, so any external witness can co-sign.

Shipped vs roadmap. The Splunk, Microsoft Sentinel and OpenCTI connectors ship today; findings arrive as signed JSON mapped to CEF and ECS fields. Roadmap, labelled as such and not yet available: STIX 2.1 over TAXII export, a per-sector machine-readable export, and the first-class typed --agent-id CLI/API argument.

Integrations (proposed, not vendor-endorsed). Whisper anchors the identity and IP boundary, never the interop protocol itself and never the settlement of a payment:

And it is built to fail open: a Whisper outage never blocks a checkout. Checks degrade to the anchors the rails already ship, and connectivity is preserved. Anycast on AS219419, no single node in the path.

Next