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.
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.
/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. |
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:
- Additive: a legitimate pluggable trust source, not official membership. Because a C2PA validator takes trust anchors as pluggable configuration, a DANE anchor is a legitimate alternative trust source. It is not official C2PA Trust-List membership, and DNSSEC/DANE is not (today) a formally recognized C2PA conformance trust anchor. Conformance currently centers on X.509 plus the curated Trust List. The formal seam is CAWG: surface the signer as a CAWG identity assertion (its
did:webissuer andcawg.web_siteURI are already DNS-native), and/or take DANE to the standard as a proposal. Position it as a complementary identity ecosystem, never “already C2PA-approved.” - Provenance is not truth. A valid signature proves who signed and that the bytes weren't changed, not that the content is true. A genuine signer can sign a staged scene or a photograph of a screen. What Whisper adds is accountability: the signer is publicly named and revocable, so the liar is on the record. It is not a veracity oracle.
- It is not a deepfake detector. Absence of a credential is not proof of fakery, and most content is unsigned. Whisper raises the value of signed-authentic; it does not flag unsigned-synthetic. Pair it with detection and watermarking: they are complementary layers.
- A screenshot or re-encode strips the manifest. C2PA manifests are embedded metadata; a screenshot or a re-compressing upload deletes them, and the standard concedes it. Signing does not prevent that. The mitigation is durable soft-binding / watermarking, which is out of scope here (Whisper anchors identity, not pixels). A public anchor plus
op:lookupshelps you detect stripping; it cannot prevent it. - Revocation bounds the window; it does not erase the past. DNS-TTL revocation shrinks a stolen key's exposure from “until OCSP, if ever” to minutes, but between theft and propagation, spec-valid fraud can exist, and revocation cannot un-sign what already shipped.
- It only helps where verifiers check. Like all provenance, the value lands where the verifier actually consults the anchor, an ecosystem-adoption dependency C2PA shares.
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:
- C2PA claim-signer. Derive the
/128from the claim-signing end-entity cert's public key; the signer serial is the socket. The publicly verifiable, DNSSEC-anchored layer on top of theCOSE_Sign1signature C2PA already carries. Complements the manifest; never replaces it. - CAWG identity assertion. A Whisper DNSSEC domain is a first-class
did:webissuer; DANE-bind thecawg.web_siteURI and thecawg.x509.coseorg cert to your domain to turn CAWG's “well-formed but unrooted” into “trusted” with no S/MIME CA. This is the formal identity seam C2PA points at. - Sign-outputs for AI agents. An agent signs its C2PA claim (
c2pa.actions/c2pa.created, IPTCdigitalSourceType: trainedAlgorithmicMedia) with a Whisper-anchored, revocable signer identity → trusted-signer status without a Trust-List slot. The one thing agent stacks otherwise cannot get. See Sign agent outputs. - Camera & device makers. Derive a per-unit signer identity from each device's existing hardware key, DNSSEC/DANE-anchored and individually revocable, so a single CA incident does not force revoking a whole model line, and independent verification survives it. Complements the in-camera signer; never reaches into the secure element.
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
- Signer & C2PA identity: how the signer
/128is derived from the key + serial, and the bring-your-own-domain path, in depth - Control plane: the full
whisper.agentsop set the anchoring call belongs to - EU AI Act · C2PA · ISO 22144: the clause-by-clause evidence mapping