Create a passphrase
A fresh twelve-word phrase.
Choose your grant
An ENS name that publishes a key — that record is the whole of the opt-in, and there is no registration with NextKey. Nobody in mind? A demo grant made here works just as well for trying it out.
Seal and grant it
One AES-256-GCM key for the content, then wrapped to the recipient with X25519 and HKDF-SHA256. What is sealed here opens there.
Put it on the chain
So far this was arithmetic inside a tab. Writing it to ENS is what lets the recipient find it from another machine, and lets you take it back.
Without a wallet
We lend you one of our names and pay the gas. The key that signs this is published in this page — it owns nothing and may write only to these names, which is why we can hand it to strangers. Works on a phone.
Or write it to a name you hold yourself
With your own wallet, to your own name
The real product: your name, your wallet, your gas. Nothing of ours is involved except the arithmetic.
No name on this deployment? Use the lane above — it needs nothing from you. Getting a name of your own is four transactions, a sixty-second commit–reveal wait and mock USDC, which is the registrar's design and not something to do out of curiosity. scripts/register-name.mjs in the repository walks through it and prints each call.
The inbox
The recipient is told nothing — no address, no notification. They read one public value off each name they know and work out for themselves whether anything there is addressed to them.
You granted it to a name on chain, and their private key is on their machine, not in this browser. So this page cannot open it — and that is the property the whole thing rests on. They would run: nextkey.mjs open <label> <their identity>
Grant to another
The same secret, a second recipient. Nothing is re-encrypted and no record is replaced — one more grant appears at an address only that recipient can compute.
Take it back
A second transaction empties the grant record and leaves the ciphertext untouched. Then check the inbox again.
For AI-agents — the same loop without a browser
Every step above has a command-line equivalent, and the page reports what it did in a form a program can read. Nothing here needs a person watching.
nextkey.mjs new <label> nextkey.mjs share <label> <identity> <recipient> nextkey.mjs open <name> <identity> nextkey.mjs revoke <label> <recipient>
A prepared state is one link. The address chooses what is being sent — /demo/passphrase or /demo/message — and two parameters do the rest: ?to=anna.nextkey.eth fills in the recipient and reads their key, ?lang=de sets the language. A secret is never accepted in a URL — a URL is written down in more places than anybody expects.
In the page, window.NEXTKEY.state() returns what has happened so far as JSON, and every result panel carries the same thing on a data-nk attribute. Private keys are not in either.
What just happened, and what it proves
The ciphertext and the grant were public from the moment they existed. Anyone could read them; nobody could use them. What separated the recipient from everyone else was a private key that was never published, never uploaded and never held by us.
The stranger's failure is the part worth dwelling on. It was not a permission check that a different build could skip: a different key derives a different wrapping key, and AES-GCM refuses. Access control that is arithmetic cannot be configured away.