
NextKey🔑🔒️
Secrets handed to a person or an AI-agent, released by ENS rules, confirmed by a human on Ledger.
Demo
About
You place a secret — a seed phrase, an API credential, a private message. Then you decide two things: who may open it, and under what conditions. Both are enforced by the protocol, not by our servers and not by anyone's goodwill. Who may open it is answered by cryptography, addressed through ENSv2. Sharing a secret with anna.eth wraps its key to the X25519 public key Anna publishes in her own ENS records — you share with a name, not with 0x7f3a…, and Anna never registers with us. ENSv2 enforces the other half: who may write that grant, revoke it, or delegate the right to. That is state in a registry you own, not a row in our database. Three independent runs are already on Sepolia, with transaction hashes in evidence/v2-onchain.log: a grant written from a phone with no wallet; one written in a browser on a name outside our own registry and opened from a command line by a key that was never in that browser; and a recovery in which two unrelated signer implementations, MetaMask and viem, reconstructed the same 32 bytes and the chain agreed. Live at nextkey.li — the playground writes real records on Sepolia without asking you for a wallet: a disclosed demo key with roles on a single resolver signs for you. Under what conditions is answered inside a trusted execution environment. A release agent can propose but never act alone. The condition — a guardian quorum, a time lock, prolonged inactivity — is evaluated in a Chainlink CRE Confidential Workflow on AWS Nitro, and the verdict carries the hash of the on-chain request it judged, so anyone can recompute it and see that the enclave decided that request and not a modified one. evidence/cre-decision.log has a passing run; evidence/release-loop.log has one where the binding fails on purpose and the release refuses. What leaves the enclave is the verdict and a coarse reason — never how far a request stood from its threshold. Honest scope: the guardian approvals are fixtures. The request is real and on chain; the people around it are not yet. Where your own key lives is your decision alone — a file on your machine, or a Ledger. The difference is invisible to whoever shares with you, because what they read is a public key in your ENS record and nothing else. On a device, opening a secret costs a deliberate button press, so software running on your laptop cannot do it while you are away from it.
How it's made
NextKey is a static site plus a set of Node tools; there is no backend, and that is a design constraint rather than an omission. Everything a server would normally hold is either public on chain or private on your own machine. The cryptography. A secret is sealed with AES-256-GCM under a content key. That content key is wrapped to the recipient with X25519 ECDH and HKDF-SHA256 — @noble/curves and @noble/hashes in Node, the identical code path in the browser via WebCrypto for AES and the same noble primitives for the curve. A grant made in the playground opens in the command-line tool and vice versa; 26 interop tests assert exactly that, 13 of them inside Chromium. What ENSv2 does that a database cannot. Three text records carry the whole handover: nextkey.eph, nextkey.secret, and the grant itself. The recipient is addressed by name — we read the X25519 public key she publishes under her own name, so she never registers with us and never learns we exist until she is told. Who may write, revoke or delegate those records is decided by ENSv2 roles on a Permissioned Resolver, so revocation is a protocol write rather than a promise from us. Resolution goes through the hackathon Universal Resolver, with the address overridden explicitly in viem — the built-in default resolves against production and silently returns the wrong answer, which cost us an evening early on. v2, and the leak it closes. In our first version a grant lived at a record name derived from a hash of the recipient's published key. The ciphertext was never the leak — the record name was: anyone holding Anna's public key could test every name for grants to her. In v2 the wrapping key and the record name are derived from the same ECDH secret, separated by HKDF info strings and salted with ephPub ‖ recipientPub, from one ephemeral keypair per name published once at nextkey.eph. Be precise about what this buys: the records are still public and still countable by an indexer. What is no longer computable is the mapping — whether a given grant belongs to a given person, or to anyone in particular. One scalar multiplication yields both the address and the unwrapping key, which is why a Ledger is asked to sign once instead of twice. Recovery without a stored file. The ephemeral private key can be re-derived from a deterministic wallet signature, so a name can be recovered with nothing but the wallet that created it. We tested this across two unrelated signer implementations: MetaMask published the key, viem reconstructed it, same 32 bytes, verified against the chain. Determinism here is measured, not assumed. The release condition, in a Chainlink CRE Confidential Workflow. An autonomous agent can file a release request on chain under an ENS role that permits that one record on that one name and nothing else — it can propose, and it cannot act. The condition (guardian quorum, time lock, prolonged inactivity) is evaluated inside a Confidential Workflow on AWS Nitro, and the verdict carries the hash of the on-chain request it judged, so anyone can recompute that hash and confirm the enclave decided that request and not a modified one. scripts/release.mjs check performs that comparison and refuses to act when it fails — evidence/release-loop.log is a run where it fails on purpose. What leaves the enclave is a verdict and a coarse reason: quorum_not_met, never 1_of_2, because the distance to a threshold is itself useful to an attacker. Honest scope: CRE protects data during execution, not source code; the workflow is public, as it should be. And the guardian approvals in the fixture are stand-ins — the request is real and on chain, the people around it are not built yet. The hacky part we are least sorry about. The playground writes real records on Sepolia for a visitor who has no wallet, no account and no ether: a disclosed demo key holds root roles on exactly one resolver, used only by a pool of lent names, and the page says so on its face. That is what makes the on-chain step reachable from a phone in forty seconds. Where ENSv2 said no. We tried to delegate write access for grants with grantSetterRoles and could not: the role binds to the record key, but a v2 grant's key contains a tag that only comes into existence in the visitor's browser. Reproduced down to the revert selector — EACUnauthorizedAccountRoles, 0x4b27a133. It is written up as Finding 11 in FEEDBACK-ENS.md, together with the rest of our feedback on the beta deployment. Two things that are unglamorous and saved the demo. A static host caches HTML and JS separately, so a page and its bundle drift; the symptom was Cannot set properties of null, which on a page about cryptography looks like broken cryptography. Every script tag now carries a content hash, and the bundle checks on load that the page matches it. And the site runs in nine languages with a merge tool and a checker, because a hand-edited 365 KB translation file is how one language quietly ends up a sentence short.