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 break when a registry re-wrapped a line, you 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 /128s 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":
{
"rdapConformance": ["rdap_level_0"],
"objectClassName": "ip network",
"handle": "acef2002a323d40d4",
"startAddress": "2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4",
"endAddress": "2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4",
"ipVersion": "v6",
"name": "<name>",
"type": "Whisper agent identity",
"status": ["active"],
"country": "NL",
"entities": [
{ "objectClassName": "entity", "handle": "<tenant>", "roles": ["registrant"] }
],
"events": [
{ "eventAction": "registration", "eventDate": "2026-07-02T16:14:21.482Z" }
],
"links": [
{ "rel": "self", "href": "https://rdap.whisper.online/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4", "type": "application/rdap+json" },
{ "rel": "related", "href": "https://acef2002a323d40d4.<tenant>.agents.whisper.online", "title": "forward (AAAA)" },
{ "rel": "related", "href": "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", "title": "reverse (PTR)" },
{ "rel": "related", "href": "https://rdap.whisper.online/domain/acef2002a323d40d4.<tenant>.agents.whisper.online", "type": "application/rdap+json", "title": "domain (RDAP)" },
{ "rel": "related", "href": "https://rdap.db.ripe.net/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4", "title": "RIR (RIPE) aggregate 2a04:2a01::/32", "type": "application/rdap+json" },
{ "rel": "rdap-up", "href": "https://rdap.db.ripe.net/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4", "title": "RIR (RIPE) aggregate 2a04:2a01::/32", "type": "application/rdap+json" },
{ "rel": "related", "href": "https://rdap.whisper.online/ip/2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4", "type": "application/rdap+json", "title": "identity bundle (whisper-id1)" }
],
"notices": [
{ "title": "Whisper RDAP", "description": ["Whisper agent-identity RDAP service. Base URL: https://rdap.whisper.online/"], "links": [{ "rel": "help", "href": "https://rdap.whisper.online/" }] },
{ "title": "Whisper NS", "description": ["Served by Whisper NS 0.211.0 (ns1).", "Whisper agent-identity registry. RDAP is canonical (https://rdap.whisper.online)."] }
],
"remarks": [
{ "description": ["Whisper agent identity. The IPv6 /128 is the identity anchor: reverse PTR resolves to acef2002a323d40d4.<tenant>.agents.whisper.online. Assigned from 2a04:2a01::/32 (AS219419, viaGraph B.V.). Connectivity posture: tier1.5."] }
]
}
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. It's the same lowercase agent handle Identity derives from the address - never a raw account ID - the same opacity principle as DANE's per-agent TLSA, applied to the registry layer. There is noparentHandlefield: the parent allocation is reached throughlinks, below, not a bare pointer.entities- the parties attached to the object, each tagged with an IANA-registeredrolesvalue; a Whisper/128carries one bareregistrantentity keyed by the opaque tenant handle. NovcardArray(jCard, RFC 7095) is published for it today - there's no name/contact detail on file to encode, so the field is simply absent rather than an empty placeholder.events- a typed timeline (registration,last changed,expiration,transfer) instead of a WHOIS registry's ad-hoc "Creation Date:" line. Today's record carries the oneregistrationevent Whisper always knows.links- RFC 8288 Web Linking, and the richest part of the object:selfis canonical,relatedentries cross-link the forward AAAA, the reverse PTR, and the siblingdomainobject, andrel: "rdap-up"is the referral to the parent registrant of the covering block - this is how an RDAP client walks from a single/128up to the RIPE NCC allocation for2a04: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 (service description, source attribution, a plain-English summary of what the object is) that a client can render or ignore, never regex out of a paragraph.
A domain object for an agent's forward name (acef2002a323d40d4.t7af9242c44554e0f8e183ac8e6fbf645.agents.whisper.online) follows the identical shape, with ldhName in place of startAddress/endAddress.
Errors are structured too - a miss returns HTTP 404 with an RFC 9083 error body, never an opaque 500:
{ "rdapConformance": ["rdap_level_0"], "errorCode": 404, "title": "Not Found", "description": ["no Whisper agent identity for this address"] }
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 rdap-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 - no account, no SDK, just HTTPS. It composes with DANE (who signs the TLS key) and the transparency log (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:
# 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 never leaks anything beyond a hostname.
Point-in-time, proof-backed
The ?history and ?time= holder history above is Whisper's own record for our own address space: useful, but nothing you can verify beyond trusting our answer. For a proof-backed version of the same idea, an RDAP answer with a receipt that folds to the same signed checkpoint as every other proof on this site, and one that also works for subjects beyond our own /128s, see Point-in-time RDAP.
Try it - with stock tools, and with Whisper
With stock tools (no Whisper software - any RDAP-aware client works identically against any registry):
# 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 `rdap-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.t7af9242c44554e0f8e183ac8e6fbf645.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):
whisper rdap 2a04:2a01:eb5a:ca74:cef2:2a:323d:40d4 # IP object
whisper rdap acef2002a323d40d4.t7af9242c44554e0f8e183ac8e6fbf645.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 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 - all seven keyless proofs, RDAP included
- DANE & DNSSEC - the cryptographic pin that RDAP's referral chain complements