# 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 is DNSSEC-signed and validates to the IANA root - confirm with `delv`, which re-derives the chain in your own process and prints `; fully validated`. 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, .name'
# "acef2002a323d40d4"
# "docs-example-1"

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": "{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](/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
```
```
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](/docs/access-control) decision on it directly, no JSON parsing required.

## Proof 8: `--trustless` - proving it without taking Whisper's word for it

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
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.

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