Standalone registration site for the @frens space: free, sovereign Bitcoin
handles (alice@frens) with instant NIP-05
nostr verification (alice@frens.earth), anchored on-chain in batches via the
Spaces protocol. A project of the
Pac's Arcade non-profit.
git clone https://github.com/PacsArcade/frens.earth frens.hip
cd frens.hip
npm install
npm run devThat's the whole install. First run writes your .env.local (with a fresh
session secret, so sign-in just works) and opens a clean local registry. Then:
- open http://localhost:3000 and claim a tag — forge keys right in the browser if you don't have any;
- hit PLAY — Bitcoin Buddy is already wired;
- start building — the operator console lives at
/a(setOPERATOR_NPUBSin.env.localto your npub to open it), tickets at/support, the manuals inrtfm/.
- PICK YOUR TAG — live availability check against the registry.
- GET YOUR KEYS — NIP-07 extension, paste an
npub, or forge a fresh keypair in the browser (guided key ceremony; the secret key never touches the server — it's copied, then revealed for a double-check, never stored). - LOCK IT IN — tag + public key enter the claim queue. NIP-05 verification works immediately; the next batch ceremony commits a Merkle root of all queued tags to Bitcoin in one transaction.
- Next.js 16 (App Router, Turbopack) + Tailwind 4, deployed on Vercel
- Registry storage: Vercel Blob in production — one immutable blob per
claim (
registry/<space>/<handle>.json);allowOverwrite: falsemakes the pathname itself the atomic uniqueness check. Local dev uses a JSON file andpredevclears it, so test claims can never ride into a real batch. nostr-toolsfor key generation/encoding (the only crypto dependency)
npm install
npm run dev # local file registry, cleared on every startProduction needs two secrets: BLOB_READ_WRITE_TOKEN (a Vercel Blob store
connected to the project — the registry) and SEAT_SECRET (any long random
string; it signs the sign-in cookie — without it, returning login, tag
release, and the admin side all 500). Set OPERATOR_NPUBS too if you want
the operator console, and GITHUB_TOKEN (fine-grained PAT: contents +
pull-requests write) if the SCAR merge queue should execute merges. No
database, no user accounts — see .env.example for the
full list.
Two deploy lessons, learned the hard way: env vars only apply to builds
made after they exist (set one → redeploy), and this project deploys by
CLI push (npx vercel deploy --prod), not on git merge. Operators
configure their node links (spaced, MUD) from the /a console — stored
config wins, env is the bootstrap fallback.
This repo is a template: one deployment = one space, and the space is pure
configuration. Your .env.local already exists (first npm run dev wrote it);
change two values in it — or set the same vars in your Vercel project:
NEXT_PUBLIC_SPACE_NAME=yourspace # tag = name@yourspace
NEXT_PUBLIC_NIP05_DOMAIN=yourspace.example # domain serving this site
Then:
- Create a Vercel project + Blob store for it
(
vercel blob create-store <name> --access public --yes), and setBLOB_READ_WRITE_TOKEN+SEAT_SECRET(andOPERATOR_NPUBSfor/admin). - Point your domain at the project. NIP-05 requires the domain in
NEXT_PUBLIC_NIP05_DOMAINto be the one serving/.well-known/nostr.json. - Reserved names live in
src/lib/registry.ts(RESERVED) — review them for your community. - Rebrand. The registry / claim / NIP-05 core is config-driven, but the
visible copy is not yet fully themeable (that migration is tracked in
docs/themeable-signin-plugin.md). Until it lands, hand-edit the Pac's Arcade branding in the hero + cards (src/components/RegistrationPage.tsx), the claim ceremony (src/components/TagClaim.tsx), header nav + footer (ArcadeHeader.tsx,EarthFooter.tsx), the profile (FrenProfile.tsx), page metadata (src/app/layout.tsx,page.tsx), and the brand theme (src/lib/brand/themes/). If you own only one space, you can also trim the multi-space host map insrc/lib/identity-config.tsto your own domain.
Each claim is stored as JSON: { handle, npub, status: "queued" | "committed", batchId, requestedAt }. A claimed tag is queued and verifies over NIP-05
immediately.
Status: the Spaces-protocol batch ceremony — computing the Merkle root, committing it to Bitcoin with the space-owner wallet, and flipping entries to
committedwith an inclusion proof — is not yet built. Tags stayqueued(and fully usable on nostr) until that tooling lands; thecommitted/batchIdfields are reserved for it.