# RDAP

**RFC 9083 registry data over HTTPS — the machine-readable who-owns-this.**

## The pain

WHOIS never had a schema. Every registry formats its `whois -h whois.example.net` reply a little differently — free text, inconsistent field labels, no content type, no pagination, and rate limits enforced by guessing at your `User-Agent`. If you've ever written a regex to scrape a registration date out of a WHOIS blob, or watched it silently break when a registry re-wrapped a line, you already know why this had to be fixed. An agent deciding in milliseconds whether to trust a peer's `/128` cannot afford to screen-scrape prose. It needs a typed object, a stable content type, and an HTTP status code it can branch on — which is exactly what RDAP is, and exactly why every one of Whisper's `2a04:2a01::/32` agent addresses is a first-class RDAP object, not an afterthought bolted onto DNS.

## What RDAP is

RDAP (the Registration Data Access Protocol) is WHOIS's IETF-standardized replacement: plain HTTPS `GET` requests answered with `application/rdap+json` bodies. The core specs:

- **RFC 7480** — RDAP runs over HTTP(S); ordinary GETs, ordinary status codes, ordinary content negotiation.
- **RFC 9082** — the query syntax: predictable, hierarchical paths like `/ip/<addr>` and `/domain/<name>`, no registry-specific query language to learn.
- **RFC 9083** — the JSON response format: a fixed object model (`objectClassName`, `entities`, `events`, `links`, `status`, `notices`) shared by every conformant RDAP server on the internet, from ARIN to RIPE to us.
- **RFC 9224** — the bootstrap mechanism a generic client uses to *find* the right RDAP server for a given resource, by walking IANA's published bootstrap registries.

Whisper runs a conformant RDAP server at `rdap.whisper.online` for the address space we hold. Point a generic RDAP client at any of our `/128`s or agent names and it just works — no Whisper-specific parsing required.

## The object model

Every response opens with `rdapConformance` (the RDAP extensions/levels in play) and an `objectClassName` that tells a generic client what shape to expect. For an agent's `/128`, that's `"ip network"`:

```json
{
  "rdapConformance": ["rdap_level_0"],
  "objectClassName": "ip network",
  "handle": "2A04:2A01:EB5A:CA74:CEF2:2A:323D:40D4/128",
  "startAddress": "2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4",
  "endAddress": "2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4",
  "ipVersion": "v6",
  "name": "WHISPER-AGENT-323D40D4",
  "type": "ASSIGNED",
  "country": "NL",
  "parentHandle": "2A04:2A01::/32",
  "status": ["active"],
  "entities": [
    {
      "objectClassName": "entity",
      "handle": "t9f3a1c2e7b4…",
      "roles": ["registrant"],
      "vcardArray": ["vcard", [
        ["version", {}, "text", "4.0"],
        ["fn", {}, "text", "Whisper agent tenant"]
      ]]
    },
    {
      "objectClassName": "entity",
      "handle": "WHISPER-ABUSE",
      "roles": ["abuse"],
      "vcardArray": ["vcard", [
        ["version", {}, "text", "4.0"],
        ["fn", {}, "text", "Whisper Security abuse desk"],
        ["email", {}, "text", "abuse@whisper.online"]
      ]]
    }
  ],
  "events": [
    { "eventAction": "registration", "eventDate": "2026-05-11T14:02:03Z" },
    { "eventAction": "last changed", "eventDate": "2026-06-30T09:11:47Z" }
  ],
  "links": [
    { "rel": "self", "href": "https://rdap.whisper.online/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4" },
    { "rel": "up",   "href": "https://rdap.db.ripe.net/ip/2a04:2a01::/32" }
  ],
  "notices": [
    { "title": "Source", "description": ["Objects returned come from the Whisper agent registry."] }
  ]
}
```

Field by field, this is the whole model:

- **`objectClassName`** — the discriminator (`"ip network"`, `"domain"`, `"entity"`). A generic client dispatches on this one field; it never needs registry-specific logic.
- **`handle`** — the registry's own opaque identifier for the object. For a registrant `entity` it's a per-tenant handle, never a raw account ID — the same opacity principle as [DANE's per-agent TLSA](/docs/dane), applied to the registry layer.
- **`entities`** — the parties attached to the object, each tagged with an IANA-registered `roles` value (`registrant`, `technical`, `abuse`, `administrative`). Names/contacts are encoded as **jCard** (RFC 7095) inside `vcardArray` — the same vCard model your phone uses, just JSON-shaped.
- **`events`** — a typed timeline (`registration`, `last changed`, `expiration`, `transfer`) instead of a WHOIS registry's ad-hoc "Creation Date:" line.
- **`links`** — RFC 8288 Web Linking. The `self` link is canonical; a `rel: "up"` link is the **referral** to the parent registrant of the covering block — this is how an RDAP client walks from a single `/128` up to the RIPE NCC allocation for `2a04:2a01::/32` (registered to **AS219419**), exactly the way a resolver walks up delegation in DNS.
- **`status`** — a controlled vocabulary (`active`, `inactive`, `pending delete`) instead of free text.
- **`notices` / `remarks`** — structured advisories (ToS pointers, source attribution) that a client can render or ignore, never regex out of a paragraph.

A `domain` object for an agent's forward name (`acef2002a323d40d4.demo.agents.whisper.online`) follows the identical shape, with `ldhName` in place of `startAddress`/`endAddress` and (when DNSSEC is configured) a `secureDNS` block carrying the delegation's DS/key data.

Errors are structured too — a miss returns HTTP 404 with an RFC 9083 error body, never an opaque 500:

```json
{ "errorCode": 404, "title": "Not Found", "description": ["No RDAP object for this target."] }
```

## The referral chain: from IANA down to a `/128`

RDAP is designed to be walked, not memorized — and you do **not** need to know Whisper's RDAP server exists to find one of our agents. A general-purpose client starts at IANA's RFC 9224 bootstrap registry, learns that `2a04:2a01::/32` is served by `rdap.db.ripe.net`, and queries RIPE — which authoritatively names the block **`WHISPER-AGENTS`** (viaGraph b.v.), with no Whisper-specific knowledge required. RIPE holds and answers for the whole `/32`; it does **not** hand the query down to us, so to reach the individual `/128` — the specific agent, its status, its registrant — you then query `rdap.whisper.online`, whose `up` link points straight back at the RIPE object. Either way the chain is unbroken and machine-walkable from the IANA root all the way to the exact agent, with no step that requires trusting our prose.

> RDAP is one of the [seven keyless verification proofs](/docs/verify) — no account, no SDK, just HTTPS. It composes with [DANE](/docs/dane) (who signs the TLS key) and the [transparency log](/docs/transparency) (when that key was issued) to answer "is this really a Whisper agent" without ever trusting our API to tell the truth.

## Ownership history

A `/128` can be released and re-allocated, so the registry keeps every holder, not just the current one. It's exposed on the IP object itself as two query parameters — `?history` for the full array of ownership intervals, `?time=<instant>` for the single holder at a moment — so it needs no special client:

```bash
# stock tools: every ownership interval, straight off the endpoint
curl -s 'https://rdap.whisper.online/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4?history' | jq '.history'

# stock tools: the holder at a past instant (RFC 3339)
curl -s 'https://rdap.whisper.online/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4?time=2026-05-01T00:00:00Z' | jq '{handle, status, entities}'

# the CLI wraps the same two parameters as flags
whisper rdap 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 --history   # every ownership interval
whisper rdap 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 --at 2026-05-01T00:00:00Z  # the holder at that instant
```

Handles on that trail are always opaque per-tenant identifiers — never a raw account ID — the same way [`dig -x`](/docs/verify) never leaks anything beyond a hostname.

## Try it — with stock tools, and with Whisper

**With stock tools** (no Whisper software — any RDAP-aware client works identically against any registry):

```bash
# discover it cold — no knowledge of Whisper's RDAP server needed. A generic client follows
# the IANA bootstrap (RFC 9224) to the RIR that holds the block; rdap.org is one public
# redirector that does the walk for you. It lands on RIPE, which names the space:
curl -sL https://rdap.org/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 | jq '{handle, name, remarks}'
# -> handle "2a04:2a01::/32", name "WHISPER-AGENTS",
#    remark "Whisper agent identity space (viaGraph b.v.) - per-/128 assignments"

# then the specific /128 — the individual agent — from Whisper's own RDAP (its `up` link
# points back at that RIPE object, so the chain is complete). RFC 9083 JSON:
curl -s -H 'Accept: application/rdap+json' \
  https://rdap.whisper.online/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 | jq '{handle, status, entities, events}'

# the domain object, for the agent's forward name
curl -s https://rdap.whisper.online/domain/acef2002a323d40d4.demo.agents.whisper.online | jq '{ldhName, status, links}'

# the legacy port-43 protocol, RFC 3912, same record
whois -h whois.whisper.online 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4

# the keyless full-chain verdict — no key, no SDK
curl -s https://rdap.whisper.online/verify-identity/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 \
  | jq '{is_whisper_agent, dane_ok, jws_ok}'
```

**With Whisper** (the CLI wraps the same endpoints, always emits the raw RDAP JSON — it's the data, not a summary):

```bash
whisper rdap 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4              # IP object
whisper rdap acef2002a323d40d4.demo.agents.whisper.online        # domain object
whisper rdap 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 --history     # ownership trail

whisper verify --trustless 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 # re-derives RDAP + DANE + the
                                                                  # transparency log itself, anchored
                                                                  # at the IANA DNSSEC root — the
                                                                  # Whisper API is never trusted
```

Both paths hit the same public server and get the same bytes back; the CLI just saves you the `curl`/`jq` boilerplate and adds `--trustless` re-derivation for when you don't want to trust us to tell you the truth about ourselves. The `whisper_rdap` tool ships in [the MCP server](/docs/cli) too, so an agent can look up a peer's RDAP record as a native tool call rather than shelling out.

## Next

- [Verify an agent](/docs/verify) — all seven keyless proofs, RDAP included
- [DANE & DNSSEC](/docs/dane) — the cryptographic pin that RDAP's referral chain complements
