Skip to content

feat(moshpit): TLD registry — claim .<whatever> on the network - #69

Merged
ralyodio merged 1 commit into
masterfrom
feat/moshpit-tld-registry
Jul 31, 2026
Merged

feat(moshpit): TLD registry — claim .<whatever> on the network#69
ralyodio merged 1 commit into
masterfrom
feat/moshpit-tld-registry

Conversation

@ralyodio

Copy link
Copy Markdown
Collaborator

First implementation of PRD 0001. Anyone can claim a TLD nobody holds; the operator of
that TLD then owns everything under it.

GET  /api/moshpit/tlds        public registry (?mine=1 for yours)
GET  /api/moshpit/tlds/:tld   availability + owner, no auth
POST /api/moshpit/tlds        claim one

Seed our own:

bun run scripts/seed-moshpit-tld.ts     # .moshpit -> anthony@profullstack.com

Who wins a contested name

The PRD leaves "if two people claim .eggs at once, who wins?" open. Code can't.

moshpit_tlds is a cache; moshpit_tld_log is the record. Allocating a unique name
is an ordering problem, so ordering goes into an append-only log that can be published and
audited — which is what lets the directory be mirrored by anyone without a mirror being
able to forge or seize a name. Nothing ever UPDATEs or DELETEs that table: "who claimed
it first" has to stay answerable afterwards, including when the answer is inconvenient.

Races are decided by the UNIQUE constraint, not check-then-insert — which two simultaneous
claims would both pass. The insert is the check, and a violation reads as "someone got
there first" → 409, so clients can distinguish it from a malformed request.

Reserved names ship enabled

PRD 0001 filed anti-squatting as P2. The day this sells .bank or .apple it has a
phishing problem and a trademark problem, and neither unwinds cheaply. A static list is
blunt and it works on day one.

Our own names are on it, so .moshpit can't be claimed by anyone else. Assigning it to
us is the one case that bypasses the list, and that path isn't reachable from the public API.

Tests

Name policy lives in lib/moshpit-name.ts with no database import, so it's testable
without Turso and reusable by a client. 11 tests / 130 assertions, covering what would
quietly corrupt a namespace:

  • scrambled.eggs is rejected, not silently registered as .scrambled or .eggs
  • normalisation is idempotent (.EGGSeggseggs)
  • the reserved list is stored normalised, so a lookup can't miss on case
  • all-numeric labels rejected (ambiguous against IPv4 in a hostname)

Two unrelated suites fail in my environment on Cannot find module '@libsql/client'
missing node_modules, not this change. CI has deps and will run them properly.

Not in this PR

Payment, and the identity keypair 0002/0003 need for root@ and domain-as-nick.
owner_key is already carried through the schema so a signed record can be added without a
migration.

🤖 Generated with Claude Code

First implementation of PRD 0001: anyone can claim a TLD nobody holds, and the
operator of that TLD owns everything under it.

  GET  /api/moshpit/tlds        the public registry (?mine=1 for yours)
  GET  /api/moshpit/tlds/:tld   availability + owner, no auth
  POST /api/moshpit/tlds        claim one

On authority. The PRD leaves "if two people claim .eggs at once, who wins?"
open, and it cannot stay open in code. The moshpit_tlds row is a cache;
moshpit_tld_log is the record. Allocating a unique name is an ordering problem,
so ordering is written to an append-only log that can be published and audited
-- which is what lets the directory be mirrored by anyone without a mirror
being able to forge or seize a name. Nothing ever UPDATEs or DELETEs that
table: "who claimed it first" has to stay answerable afterwards, including when
the answer is inconvenient.

Races are decided by the UNIQUE constraint rather than a check-then-insert,
which two simultaneous claims would both pass. The insert is the check, and a
constraint violation reads as "someone got there first" -- a 409, so a client
can tell it apart from a malformed request.

Reserved names ship enabled. PRD 0001 filed anti-squatting as P2, but the day
this sells .bank or .apple it has a phishing and a trademark problem, and
neither unwinds cheaply. A static list is blunt and it works on day one.
Our own names are on it too, so .moshpit cannot be claimed by anyone else --
assigning it to us is the single case that bypasses the list, and that path is
not reachable from the public API.

Name policy lives in lib/moshpit-name.ts with no database import, so it is
testable without a Turso connection and reusable by a client. 11 tests cover
the parts that would quietly corrupt a namespace: that "scrambled.eggs" is
rejected rather than silently registering someone the wrong name, that
normalisation is idempotent, and that the reserved list is stored normalised so
a lookup cannot miss on case.

Seed the operator's own TLD with:

  bun run scripts/seed-moshpit-tld.ts

Not covered: payment, and the identity keypair that PRD 0002/0003 need for
root@ and domain-as-nick. owner_key is carried through the schema so a signed
record can be added without a migration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

vu1nz Security Review

0 finding(s) in PR #?

No security issues found.

@ralyodio
ralyodio merged commit a2408aa into master Jul 31, 2026
2 checks passed
@ralyodio
ralyodio deleted the feat/moshpit-tld-registry branch July 31, 2026 01:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant