NextKey

Sandbox

Build with the NextKey ID

The open API

Read-only. It holds no key, signs nothing, writes nothing and is never shown a plaintext — everything it returns is already public on chain and readable without it. Take it away and nothing stops working: the pages read the chain from your browser, and the command line reads it directly. It exists for callers who cannot reach a node.

Answering at

Ask it something, from here

The request goes from your browser, not from ours, so this is also the CORS check and the DNS check. The URL is shown before it is sent.

The routes

GET /health
GET /name/anna.nextkey.eth
GET /id/anna.nextkey.eth
GET /openapi.json

The network is a path prefix, exactly as on this site: /demo/v1 reads the hackathon deployment on Sepolia, and /v1 is reserved for mainnet. Asking /v1 today answers 501 and names the address that does work — not 404, which would read as a spelling mistake.

Two kinds of "no", and why they never share a code

404 no_published_key is a fact about the chain: this name publishes nothing, so nothing can be sealed to it. 502 upstream_unavailable is a fact about our luck: the node did not answer, the reply says in its own words that it tells you nothing about the name, and it is never cached.

And a fourth, which is the one a reader will meet most: 404 not_on_this_deployment. A name registered on production ENS is not registered here, and a text lookup answers the same empty value for a name that carries nothing and for a name that is not there at all. The two are separated by asking which resolver answers — and when that lookup itself fails, the reply says the answer is unknown rather than guessing at either.

There is a third, because the chain permits it. 422 key_not_x25519 is a name whose record holds something that is not a 32-byte key. Anyone may write any string to their own name, and inventing a NextKey ID for one would put a confident, checkable-looking identifier under a value that identifies nobody.

The records, which are the real interface

The API is a convenience. This is the protocol: five text records on an ENS name, and anything that can read and write ENS text records can take part without asking us. Nothing in the format belongs to us.

nextkey.pubkey
The X25519 key everything sealed to this name is wrapped to. Publishing it is the whole of the opt-in, and it is what a NextKey ID is derived from.
nextkey.eph
One ephemeral public key per name, written once and never replaced. Every grant on the name is addressed from this key and a recipient's.
nextkey.eph.sealed
That key wrapped to the owner, so a recipient can be added later without a signature. Useless to anybody else.
nextkey.g2.<32 hex>
A grant. The address is derived from the ECDH result, so an observer holding every public value in the system cannot compute it, cannot test a guess, and learns nothing from how many of them a name carries.
nextkey.secret
The ciphertext, padded to 256-byte blocks so its length says nothing about what it holds.

The command line

Every step the pages take has an equivalent here, and nothing about it needs a person watching. It reads the chain directly and never touches the API.

nextkey.mjs new    <label>
nextkey.mjs share  <label> <identity> <recipient>
nextkey.mjs open   <name>  <identity>
nextkey.mjs revoke <label> <recipient>
nextkey.mjs eph    <name>

NextKey in the browser

An agent driving a browser does not have to read the prose. window.NEXTKEY.state() returns what has happened so far as JSON, every result panel carries the same thing on a data-nk attribute, and no private key appears in either. A prepared state is one link: the address chooses what is being sent — /demo/passphrase or /demo/message — and ?to= fills in the recipient, ?lang= sets the language. A secret is never accepted in a URL, because a URL is written down in more places than anybody expects.

Look at the live PoC rather than believe it

The PoC live view reads five names off Sepolia on every load and shows what they carry — no cache, no replay, no screenshots. The repository carries the reasoning as well as the code, including the reversals, which is the part worth reading.

Live PoC Explorer README

About the API

A caller who uses the endpoint is telling us which name they are looking up, on top of telling the node. There is nowhere for that to be written down: no request log, no analytics, no key-value store, no database — the worker's configuration file has none of them attached, which is a stronger statement than a policy saying we choose not to. There are no API keys either, so there is nothing to correlate lookups with even in principle.

The residue we do not control is Cloudflare's own edge logging.