feat(moshpit): TLD registry — claim .<whatever> on the network - #69
Merged
Conversation
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>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
This was referenced Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
First implementation of PRD
0001. Anyone can claim a TLD nobody holds; the operator ofthat TLD then owns everything under it.
Seed our own:
bun run scripts/seed-moshpit-tld.ts # .moshpit -> anthony@profullstack.comWho wins a contested name
The PRD leaves "if two people claim
.eggsat once, who wins?" open. Code can't.moshpit_tldsis a cache;moshpit_tld_logis the record. Allocating a unique nameis 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 orDELETEs that table: "who claimedit first" has to stay answerable afterwards, including when the answer is inconvenient.
Races are decided by the
UNIQUEconstraint, not check-then-insert — which two simultaneousclaims 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
0001filed anti-squatting as P2. The day this sells.bankor.appleit has aphishing 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
.moshpitcan't be claimed by anyone else. Assigning it tous 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.tswith no database import, so it's testablewithout Turso and reusable by a client. 11 tests / 130 assertions, covering what would
quietly corrupt a namespace:
scrambled.eggsis rejected, not silently registered as.scrambledor.eggs.EGGS→eggs→eggs)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/0003need forroot@and domain-as-nick.owner_keyis already carried through the schema so a signed record can be added without amigration.
🤖 Generated with Claude Code