# 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](/docs/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

```bash
# 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.

```bash
# 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

```bash
# 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](https://www.rfc-editor.org/rfc/rfc6698), strict profile [RFC 7671](https://www.rfc-editor.org/rfc/rfc7671)): the DNS record pins the exact key, no CA consulted. Byte layout and why usage `3` beats a CA chain: [DANE & TLSA](/docs/dane).

```bash
# 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

```bash
# stock tools
curl -s https://rdap.whisper.online/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 | jq '.handle, .parentHandle'
# "2A04:2A01:EB5A:CA74:CEF2:2A:323D:40D4/128"
# "2A04:2A01::/32"

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

RDAP ([RFC 9083](https://www.rfc-editor.org/rfc/rfc9083)) 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](https://www.rfc-editor.org/rfc/rfc3912)) gives the same fact as legacy text on port 43. Full object model: [RDAP](/docs/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:

```bash
# stock tools — curl only, no API key
curl -s https://rdap.whisper.online/verify-identity/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 | jq .
```
```json
{
  "is_whisper_agent": true,
  "fqdn": "acef2002a323d40d4.<tenant>.agents.whisper.online",
  "operator": "t9f3a1c2e7b4…",
  "dane_ok": true,
  "jws_ok": true,
  "evidence": {
    "ptr": "acef2002a323d40d4.<tenant>.agents.whisper.online",
    "dane_tlsa_sha256": "b653a4ef...fcb82d1d",
    "dane": {"usage": 3, "selector": 1, "matching": 1, "served_leaf_matches": true},
    "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](/docs/did-web).

```bash
# with Whisper — the CLI wraps the same call and grades it into a table
whisper verify acef2002a323d40d4.<tenant>.agents.whisper.online
```
```
agent            yes
fqdn             acef2002a323d40d4.<tenant>.agents.whisper.online
operator         t9f3a1c2e7b4…
dane_ok          yes
jws_ok           yes
dane_tlsa_sha256 b653a4ef...fcb82d1d
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](/docs/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:

```bash
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
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
dane           pass    DNSSEC-root   served leaf SPKI-SHA256 == TLSA pin
transparency   pass    DNSSEC-root   root kid ... (DNSSEC-anchored _whisper-identity TXT)
identity_doc   pass    DNSSEC-root   claims match; signature verifies

whisper: acef2002a323d40d4.<tenant>.agents.whisper.online is CRYPTOGRAPHICALLY PROVEN
  — trust anchor: DNSSEC root (IANA) + DANE-EE + DNSSEC-anchored transparency 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.

```bash
# --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

- [DANE & TLSA](/docs/dane) — the TLSA record byte-for-byte, and the DNSSEC chain proofs 4 and 8 build on
- [Access control](/docs/access-control) — gate a connection or a firewall rule on this verdict
