Whisper · Docs
Content

The provenance-gap cure

A Content Credential can be perfectly valid: the bytes untouched, the signature good. And your verifier will still show “unknown source.” Because whether to trust the signer has no universal public answer: the signer's cert isn't on a gate-kept Trust List, and there is no free, automated path onto one.

Whisper closes that gap by making the address be the signer: a routable IPv6 /128 derived from the key the signer already signs with, DNSSEC-anchored and DANE-pinned under a domain you control, that any verifier resolves with no list to join and you can revoke worldwide at DNS-TTL. The device_id is the C2PA signer cert serial already in every manifest. This is additive to C2PA (trust anchors are pluggable inputs to the validator), never a fork. This page walks the gap at spec level, the reframe, the exact live calls (keyless to verify, one keyed call to anchor), the who-verified analytics, and, candidly, where it stops.

The gap, at spec level

Start with what C2PA does well, because the fix rides on top of it. A Content Credential is a set of assertions (actions, hashes, metadata), a claim that hash-references those assertions, and a claim signature. The signature is a COSE_Sign1, and the signer's X.509 chain travels in-band in the COSE header (x5chain, RFC 9360): every intermediate up to the root is embedded, so a verifier builds the chain with no network call. The hard binding (c2pa.hash.data) hashes the asset bytes, so the whole manifest is tamper-evident. That part works, and Whisper doesn't touch it.

The unresolved question is not “were the bits changed?” It is “should I trust the signer?” A validator trusts a signer only if its certificate is on an explicit allowed list, or chains to a root on a trust-anchors list. And here is the honest hook: C2PA does not mandate any particular PKI. Trust lists and trust anchors are pluggable configuration inputs to the validator, which is precisely why a DNSSEC/DANE anchor is a legitimate alternative trust source, not a fork of the standard.

In practice, one list dominates. The official C2PA Trust List and Conformance Program (mid-2025) is a coalition-managed set of X.509 anchors for CAs issuing to conforming signers, gated by a Product Security Architecture submission and assurance levels. Content signed by an off-list CA displays “unknown source”, technically indistinguishable, to the reader, from a manifest signed by a recognized org. There is no free, automated, ACME-style path (no “Let's Encrypt for C2PA”) for an independent creator, a small newsroom, or an AI agent to get a recognized cert; commercial C2PA certs run on the order of ~$289/yr, and the recognized-CA set is controlled by a small coalition. The spec also permits self-signed and off-list certs, so an “anyone can sign anything” gray zone exists with no open way to make an off-list signer publicly verifiable.

The root cause has a shape: trust-list gatekeeping. The signer is real, the manifest is valid, but the verifier cannot independently resolve who the signer is without the gate-kept list. C2PA's own experimental “Web Domain Trust Anchor” tries to answer this with a self-signed cert in an HTTPS /.well-known/c2pa.json file, but it isn't DNSSEC-signed, and its authors flag domain takeover and privacy (the validator fetches from the origin) as open problems. The right mechanism is the one that proposal didn't use.

C2PA manifest COSE_Sign1 · x5chain valid · tamper-evident TODAY · off-list CA C2PA validator trust = the list C2PA Trust List gate-kept coalition “unknown source” WITH WHISPER · self-verifying C2PA validator + DANE trust anchor signer's DNSSEC zone TLSA 3 1 1 trusted signer no list to join
Same valid manifest, same validator. Today an off-list CA resolves to “unknown source.” Give the validator a DANE trust anchor and it resolves the signer's TLSA under the signer's own DNSSEC zone: trusted, with no Trust-List slot. Trust anchors are pluggable, so this is additive, not a fork.

The reframe: the address is the signer

You cannot tune your way out of a trust question with detection. A valid signature from an off-list CA is genuinely valid; the verifier's problem is that it has no independent way to resolve who that signer is. The strictly-stronger move is to change what the verifier can resolve on its own.

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 signer. Whisper derives the signer's /128 from the public key it already signs with (the SubjectPublicKeyInfo of the C2PA claim-signing end-entity cert, or a CAWG org cert, or an in-camera device signer), with the C2PA signer cert serial as the domain separator. The private key never leaves the signer's HSM or secure element; only the public SPKI is an input. The result is DNSSEC-anchored, DANE-EE pinned, RDAP-registered: a signer any validator configured with a DANE anchor resolves directly, with no gate-kept list, no coalition, and no annual CA fee.

The serial is the public index: the /128 is its cryptographic counterpart. The signer cert serial sits right there in every manifest; it is a public identifier, not a secret. But the /128 derives from the in-HSM key salted by the serial, so the serial alone yields nothing: you cannot go serial → /128 without the key, there is no enumerable directory, and RDAP / reverse-DNS return the registry object, never anything private. Because the derivation is tenant-bound, the same signer key under two organisations yields two unrelated /128s, so no outsider can link a signer across tenants.

Signer key (SPKI) C2PA claim-signer serial private key sealed in HSM only public SPKI is an input public key + serial /128 2a04:2a01:c0d::51e routable · tenant-bound DNSSEC + DANE-EE 3 1 1 A signer any verifier trusts whisper verify --trustless no Trust-List slot · no CA fee op:'revoke' → gone worldwide at DNS-TTL
The signer serial is already in the manifest: a good public name with no revocation and no open trust path. Whisper binds it to a routable, publicly verifiable /128 and gives it the off-switch C2PA's optional, cache-heavy OCSP is too slow for.

Shipped & live. Deriving a signer /128 from the signer's public key plus its C2PA signer cert serial passed as device_id is in production today. Anchor one with the control-plane call below and verify it from the DNSSEC root with tools already on your machine. A first-class typed --serial / --c2pa flag is on the roadmap; pass the serial as device_id today.

What changes

Nothing here is a new detection heuristic. Each row is a failure mode of the current trust model that stops being possible, not one you catch after the fact.

The gap today Why it closes under a DANE-anchored signer
Off-list CA → your Content Credential shows “unknown source” The signer's TLSA under its own DNSSEC zone is a trust anchor the validator resolves directly. dig the signer's name; the AAAA and TLSA agree, or the forgery is a DNSSEC/DANE inconsistency any verifier catches with stock tools.
No free/automated path onto the recognized-CA list (~$289/yr per cert; a coalition gate) The anchor is DNS you already run: no CA fee, no committee. A stringer, an independent newsroom, or an AI agent becomes publicly verifiable with one TLSA record.
A compromised signing key mints spec-valid manifests until OCSP/CRL propagates (revocation is optional and cache-heavy) One revoke tears down the /128, its PTR, and its DANE pin worldwide at DNS-TTL. Honestly: this shrinks the exposure window from “until OCSP, if ever” to minutes. It bounds the damage; it does not retroactively un-sign what the stolen key already emitted.
No cross-org trust without both signers on the same list A shared DNS root is a shared anchor. A newsroom verifies a different org's signer with no private list in common: both already trust the IANA root.

Anchor a signer identity

Anchoring is one control-plane call to POST https://graph.whisper.security/api/query with your X-API-Key header. You pass the signer's public key material (the base64 SubjectPublicKeyInfo of its C2PA claim-signing / CAWG / device key) and the C2PA signer cert serial as device_id; you get back the deterministic /128 with DANE-EE live, and a WireGuard config if you also want the signer's egress to source from that address.

The call

CALL whisper.agents({op:'connect', args:{
  tier:'wireguard',
  identity_public_key:'<base64 SPKI of the signer key>',   # public half of the C2PA claim-signing key
  device_id:'6A2F9C143B8E00D1'                             # the C2PA signer cert serial
}}) 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 signer's HSM / secure element
Q="CALL whisper.agents({op:'connect', args:{tier:'wireguard', \
   identity_public_key:'MFkwEwYHKoZIzj0…SPKI', device_id:'6A2F9C143B8E00D1'}}) \
   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, DANE-EE live at anchor time
address     2a04:2a01:c0d::51e
fqdn        signer-6a2f9c143b8e00d1.<tenant>.agents.whisper.online
ptr         signer-6a2f9c143b8e00d1.<tenant>.agents.whisper.online
state       active                     # DNSSEC + DANE-EE (3 1 1) published atomically
tlsa        3 1 1 <sha-256 of the signer leaf key>

The signer now has a name any verifier can resolve. There are two ways to point verifiers at it, and you can use both: the hosted name above, verifiable trustless from the IANA root; or bring your own domain: prove a domain you control and issue the signer under it, so a reader verifies “signed by press.example-news.org in their own words. Either way it is a DANE trust anchor a C2PA validator can be configured to consult. The derivation and the BYOD flow are covered in depth in Signer & C2PA identity.

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 signer key + serial again The same /128: idempotent, safe to retry, safe to run on every publish. No duplicate identities.
The same signer key with a different serial on your tenant 409 Conflict: a signer key binds to exactly one serial, and its identity is fixed at first registration. The /128 never silently re-pins: revoke it and re-anchor from the matching key, or omit the serial to reuse the existing identity.
A non-string device_id (a number, an array, null) 400 with an actionable detail, never an opaque 500. Send the serial as a string.
i

On the CLI: whisper create --register mints a generic signer identity, and whisper verify / whisper policy / whisper logs / whisper kill --revoke drive the rest. The typed --serial / --c2pa flag is not shipped yet. Anchor signers through the control-plane call above, which is live today, and drive them with the CLI verbs once allocated.

Revoke, worldwide

A compromised signing key, a rotated cert, a decommissioned device: one call tears down the /128, its PTR, and its DANE pin everywhere at DNS-TTL speed. It is 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:c0d::51e'}})
# …or the CLI
whisper kill --revoke 2a04:2a01:c0d::51e

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

Verify it: keyless, no account

The signer identity is public by design, so anyone (a fact-checker, a platform's trust & safety desk, an auditor, a reader) can confirm a signer 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, anchor and govern with your key.

# no key, no account: re-derive and verify the signer's identity, trustless to the IANA root
whisper verify --trustless 2a04:2a01:c0d::51e

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

# the address IS the signer: forward-confirmed reverse DNS names it
dig -x 2a04:2a01:c0d::51e +short
# signer-6a2f9c143b8e00d1.<tenant>.agents.whisper.online.

# the registry object for the /128: RDAP, typed JSON
curl -s https://whisper.online/ip/2a04:2a01:c0d::51e | jq '.handle, .parentHandle'
# "2A04:2A01:C0D::51E/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 regulator, or a rival newsroom, can verify a signer outside your tenancy. Full mechanics: Verify an agent and DANE & TLSA.

Who verified your content

C2PA verification is designed to need no network call: the signer's chain travels in-band via x5chain, so the only network events are optional OCSP/CRL and the RFC 3161 time-stamp. The consequence is that a signer is normally blind to who verified their content: it is a write-only quadrant. Every other provenance layer, including watermarking, shares that blindness.

Anchor the signer in DNS/DANE and that changes. Every verifier that resolves and validates the DANE anchor leaves DNS, TLSA, and RDAP lookups against Whisper's authoritative servers, and op:lookups returns them: who checked this signer, where, and how often.

CALL whisper.agents({op:'lookups', args:{agent:'2a04:2a01:c0d::51e', window:'24h'}})
# → 214 DANE/TLSA validations · 63 reverse-DNS · 9 RDAP
#   a verification-analytics stream, and an impersonation tripwire

Two honest notes and one payoff. It observes lookups against the public anchor, not offline validations that never touch the network: it is a strong signal, not a census. And it is an early warning: a spike of resolutions on a signer you did not publish under, or a lifted identity assertion being checked in the wild, shows up before the fake spreads, not in a post-mortem. This is the loop C2PA and SynthID structurally cannot close.

Nothing issued in the dark. Every signer mint and every revoke lands in a public, append-only Merkle log (RFC 6962 tlog-tiles with a C2SP signed-note checkpoint), each root anchored to Bitcoin via OpenTimestamps, forming an auditable, non-repudiable issuance-and-revocation trail. Honest status: it is tamper-evident, Ed25519-signed, and Bitcoin-anchored, but not yet independently witnessed (co-signing across our own servers is availability, not independence); it speaks C2SP tlog-witness, so any external witness can co-sign. Read it with curl -s https://whisper.online/ip/2a04:2a01:c0d::51e/transparency and curl -s https://whisper.online/checkpoint. Details: Transparency log.

And when the signer is an AI agent marking its own outputs, the same /128 is a control plane, not just a name: op:policy sets default-deny egress, op:firewall allows/denies by host, CIDR, or port, op:budget caps it, and op:revoke kills it worldwide in one call. Give a generative tool a signer identity and govern exactly what it may reach. See Sign agent outputs and Egress governance.

Honest scope: what this is not

Over-claiming is the credibility trap in provenance. So, candidly:

Where it fits: C2PA, CAWG, standards, SIEM

Whisper is additive. It rides on top of the manifest, the watermark, the in-camera capture, and the SIEM you already run. It replaces none of them. Whisper does not create the C2PA manifest or embed a watermark; it anchors the signer those manifests already reference, adds who-verified analytics, and gives an AI agent a signer identity it otherwise cannot get.

Standards. EU AI Act Art.50(2) requires generative-AI outputs be marked “in a machine-readable format,” effective, interoperable, and robust “as far as technically feasible”; Recital 133 enumerates “cryptographic methods for proving provenance and authenticity of content” and asks that detection be “accessible to the public.” A public DNSSEC/DANE anchor plus open verification analytics is exactly that public, interoperable bar. The honest limit: the AI-generated assertion itself rides in the C2PA manifest (Whisper anchors the signer; it does not assert “this is AI”). ISO/IEC 22144 (the C2PA architecture, at draft) pulls this into procurement language. The clause-by-clause mapping lives in EU AI Act · C2PA · ISO 22144.

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

Integrations (proposed, not vendor-endorsed). Whisper anchors the signer and domain boundary, never the manifest bytes, the watermark, or the capture pipeline:

And it is built to fail open: a Whisper outage never blocks a verify. Checks degrade to the anchors already carried in the manifest, and connectivity is preserved.

Next