WhisperDocs
Verify & authorize

Verify an agent

A Whisper agent carries a verifiable address, not a bearer token - so anyone with dig and curl can check who it is in a second, with no account and without taking Whisper's word for it.

Seven independent, standards-based proofs converge on the same /128. A keyless endpoint runs them for you; an eighth mode re-derives the chain from the IANA DNSSEC root, trusting not even that endpoint. Run every command below against the public demo agent - address 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4, name acef2002a323d40d4.<tenant>.agents.whisper.online - or swap in any address. The mechanism is the same.

Why seven proofs and not one

A single fact is a single point of failure. If "verified" meant only "the forward DNS resolves," whoever compromised one zone could forge an identity outright.

Whisper spreads the claim across separately-populated DNS zones, an X.509 key pin, two registry protocols, and a signed document. An impostor has to win all of them at once - and any mismatch (a PTR whose forward AAAA doesn't point back, a TLSA pin the served cert fails) is visible instantly with stock tools. Identity is the derivation that ties the name to the address; this page is the checklist that proves it held.

Proofs 1–3: reverse, forward, and the friendly alias

# stock tools - no Whisper software anywhere in this block
dig -x 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 +short
# acef2002a323d40d4.<tenant>.agents.whisper.online.        <- 1. PTR (RFC 3596 / RFC 1035 §3.3.12)

dig +short AAAA acef2002a323d40d4.<tenant>.agents.whisper.online
# 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4                    <- 2. forward AAAA (RFC 3596)

dig +short CNAME my-agent.<tenant>.agents.whisper.online
# acef2002a323d40d4.<tenant>.agents.whisper.online.        <- 3. friendly alias (RFC 1035 §3.3.1)

Every answer carries AD=YES under DNSSEC on any validating resolver you already run - confirm with dig +dnssec or kdig +dnssec. The load-bearing property: the PTR zone (ip6.arpa) and the forward zone (agents.whisper.online) are two separate delegations, populated from opposite ends of Whisper's authoritative data. Forge one without the other and you get an inconsistency - dig -x names an agent whose AAAA doesn't point back - not a convincing fake.

# with Whisper - one call re-derives and cross-checks 1–3 for you
whisper verify 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4

Proof 4: TLSA / DANE - the key pin

# stock tools
dig +dnssec TLSA _443._tcp.acef2002a323d40d4.<tenant>.agents.whisper.online
# ... 300 IN TLSA 3 1 1 b653a4ef...fcb82d1d

openssl s_client -connect acef2002a323d40d4.<tenant>.agents.whisper.online:443 \
  -servername acef2002a323d40d4.<tenant>.agents.whisper.online </dev/null 2>/dev/null \
  | openssl x509 -pubkey -noout | openssl pkey -pubin -outform der | openssl dgst -sha256
# (stdin)= b653a4ef...fcb82d1d   <- must equal the TLSA pin

3 1 1 is DANE-EE / SPKI / SHA-256 (RFC 6698, strict profile RFC 7671): the DNS record pins the exact key, no CA consulted. Byte layout and why usage 3 beats a CA chain: DANE & TLSA.

# with Whisper - the same comparison, done for you
whisper verify --trustless acef2002a323d40d4.<tenant>.agents.whisper.online

Proofs 5 & 6: RDAP and WHOIS - the registry record

# stock tools
curl -s https://rdap.whisper.online/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 | jq '.handle, .name'
# "acef2002a323d40d4"
# "make-review-demo"

whois -h whois.whisper.online 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4

RDAP (RFC 9083) is the typed application/rdap+json object: startAddress/endAddress pinned to this one /128, and a links entry that refers up to the RIPE allocation covering 2a04:2a01::/32. WHOIS (RFC 3912) gives the same fact as legacy text on port 43. Full object model: RDAP.

Proof 7: the keyless full-chain verdict

Stitching proofs 1–6 together by hand is exactly the friction Postel's Law says to remove. Whisper runs the whole chain server-side and hands back one JSON answer - still keyless:

# stock tools - curl only, no API key
curl -s https://rdap.whisper.online/verify-identity/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 | jq .
{
  "is_whisper_agent": true,
  "fqdn": "acef2002a323d40d4.<tenant>.agents.whisper.online",
  "operator": "<tenant>",
  "tenant": "<tenant>",
  "dane_ok": true,
  "jws_ok": true,
  "verified_at": 1788063020831,
  "evidence": {
    "address": "2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4",
    "ptr": "4.d.0.4.d.3.2.3.a.2.0.0.2.f.e.c.4.7.a.c.a.5.b.e.1.0.a.2.4.0.a.2.ip6.arpa",
    "forward_aaaa": "2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4",
    "agent": "acef2002a323d40d4",
    "allocated_at": 1783008861482,
    "posture": "tier1.5",
    "dane_tlsa_sha256": "b653a4ef...fcb82d1d",
    "dane": {"usage": 3, "selector": 1, "matching": 1, "strong_pin": true, "served_leaf_matches": true},
    "rdap": "https://rdap.whisper.online/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4",
    "identity_doc": "https://acef2002a323d40d4.<tenant>.agents.whisper.online/.well-known/whisper-identity"
  }
}

The endpoint takes an address or an FQDN, at /verify-identity/<target> or ?ip=<target>, and is liberal in what it accepts, conservative in what it emits. A target that isn't a Whisper agent 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 with a helpful detail. Never a 500.

jws_ok reflects the signed identity document at /.well-known/whisper-identity: a compact ES256 JWS binding the address, FQDN, tenant, and issue time to the same per-agent key DANE pins. A peer with a JWT library and nothing else can validate it independently of this endpoint - the same shape as did:web VC-JWT.

# with Whisper - the CLI wraps the same call and grades it into a table
whisper verify acef2002a323d40d4.<tenant>.agents.whisper.online
FIELD                VALUE
agent                yes
fqdn                 acef2002a323d40d4.<tenant>.agents.whisper.online
operator             <tenant>
dane_ok              yes
jws_ok               yes
dane_tlsa_sha256     b653a4ef...fcb82d1d
served_leaf_matches  yes
whisper: acef2002a323d40d4.<tenant>.agents.whisper.online is a verified Whisper agent (DANE-anchored)

The exit code is the verdict - 0 for a fully-verified agent, non-zero otherwise - so a script can gate a firewall rule or an access-control decision on it directly, no JSON parsing required.

Proof 8: --trustless - proving it without trusting Whisper at all

Proof 7 is honest about what it is: Whisper's own server running the chain and reporting the result. That's a convenience, not a proof to someone who won't take Whisper's word for anything. --trustless removes Whisper from the trust path - it validates DNSSEC from the IANA root in-process, on your machine, with your own resolver:

whisper verify --trustless acef2002a323d40d4.<tenant>.agents.whisper.online
FIELD        VALUE
address      2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4
fqdn         acef2002a323d40d4.<tenant>.agents.whisper.online
agent        acef2002a323d40d4
tenant       <tenant>
tlsa_sha256  b653a4ef...fcb82d1d
served_spki  b653a4ef...fcb82d1d

CHECK         RESULT  TRUST        DETAIL
dnssec        pass    DNSSEC-root  AAAA, PTR and TLSA(3 1 1) all DNSSEC-validated to the IANA root; address and name are consistent
dane          pass    DNSSEC-root  served leaf SPKI-SHA256 == a published TLSA pin; issuer "Whisper Agent Identity Issuing CA"
transparency  pass    DNSSEC-root  root signature verified; root_hash bound
identity_doc  pass    DNSSEC-root  JWS verified against the DNSSEC-anchored key; claims match

whisper: acef2002a323d40d4.<tenant>.agents.whisper.online is CRYPTOGRAPHICALLY PROVEN
  - trust anchor: DNSSEC root (IANA anchor) + DANE-EE + DNSSEC-anchored transparency/ledger keys -- Whisper API NOT trusted

Each row carries its own trust level, so the verdict is auditable rather than a black box:

Trust level Meaning
DNSSEC-root fully trustless - chains to the IANA DNSSEC root anchor
pin+DNSSEC the signing key is served over HTTPS, but the claims it signs are cross-checked against DNSSEC-validated DNS facts
trust-on-pin verified, but the key came from a WebPKI-served JWKS, not from DNS

The chain, in order: resolve and DNSSEC-validate AAAA, PTR, and TLSA, checking that address and name agree - a mismatch is a fail, a fraud signal, not a skip. Open the TLS connection and confirm the served leaf's SPKI hash equals the DNSSEC-proven TLSA pin (RFC 6698/7671, no CA). Then verify the transparency entry and the identity-document JWS against keys published in _whisper-identity/_whisper-ledger TXT records under the same DNSSEC chain.

When those validate, the HTTPS-served JWKS is demoted to a cross-check and the whole verdict anchors at the root. It reads true iff both load-bearing legs (dnssec, dane) pass and nothing reports a fail; the exit code mirrors it, and --json prints the structured report for a pipeline.

# --resolver points the in-process validator at a resolver you trust
whisper verify --trustless --resolver 9.9.9.9 acef2002a323d40d4.<tenant>.agents.whisper.online

There's no stock-tools column for proof 8 - being the stock-tools re-derivation, done once and correctly, is the point. By hand it's every command in proofs 1–6 plus a manual DNSSEC chain walk and an ES256 signature check, which is exactly what the flag automates so you don't have to trust that you did it right either.

Next