Skip to content

feat(dns): resolvers for the Moshpit namespace, and the rest of the internet - #77

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

feat(dns): resolvers for the Moshpit namespace, and the rest of the internet#77
ralyodio merged 1 commit into
masterfrom
feat/moshpit-dns

Conversation

@ralyodio

Copy link
Copy Markdown
Collaborator

Public DNS resolvers for *.pit.moshcode.sh, so .moshpit and every other Moshpit TLD resolve without the TronBrowser extension — and .com, .org and the rest of the internet keep working from the same resolver, forwarded to 8.8.8.8 and 1.1.1.1.

One setting, nothing to install. PRD 0004 R1 from the resolver side instead of asking people to type scrambled.eggs.pit.moshcode.sh.

What it answers

Query Answer
scrambled.eggs (registered TLD) the gateway's address, so the site loads
www.scrambled.eggs the same — the gateway routes by Host
example.com whatever upstream says, relayed byte for byte
nobody.zzzz NXDOMAIN, exactly as clearnet said
an aliased TLD a real CNAME to where it points, then the address

Contested names — profullstack.ai exists in both namespaces — are a mode, defaulting to clearnet wins, registry backfills. A public resolver that silently redirected a domain which resolves perfectly well would be indistinguishable from a hijack. MOSHPIT_RESOLVE_MODE=moshpit is the override. Same policy as the extension's moshpit-resolve.ts, so moving between them does not change which site you land on.

Shape

  • lib/dns/wire.ts — the DNS codec, written out rather than pulled from npm: this is the one process strangers can send arbitrary bytes to on UDP/53. Total decoder, guards on compression loops and short reads.
  • lib/dns/policy.ts — which namespace a query belongs to. Pure, no I/O.
  • lib/dns/registry.ts — reads /api/moshpit/resolve over HTTP, cached and coalesced. Non-authoritative by construction (0004 R2), which is also what makes it self-hostable (R8).
  • lib/dns/upstream.ts — staggered failover across upstreams, not a race: racing doubles the traffic and the number of parties who see it to save a few ms.
  • lib/dns/answers.ts, gateway.ts, ratelimit.ts, server.ts, doh.ts; entry scripts/moshpit-dns.ts.
  • Zero dependencies — node built-ins and fetch.

Safety

Open resolvers are reflection amplifiers, and scanners find them within hours. On by default: per-client token bucket that drops rather than replies, ANY refused, non-recursive clearnet queries refused, responses arriving at the listening socket ignored.

Failure is one-directional on purpose: a registry outage costs Moshpit names and leaves the web alone; a gateway with no known address answers nothing rather than caching a name as address-less; a dead upstream is SERVFAIL, not a hung client.

Verified

116 tests pass (44 new), tsc --noEmit clean, and it was run against the live registry and gateway:

dig +short @127.0.0.1 -p 5354 fuck.yeah           -> 69.46.46.24   (gateway)
dig +short @127.0.0.1 -p 5354 example.com         -> real answers
dig +short @127.0.0.1 -p 5354 TXT anything.moshpit -> v=moshpit1 name=... gateway=pit.moshcode.sh
dig +tcp / DoH GET /dns-query                      -> same answers
dig ANY example.com                                -> REFUSED

The gateway half is not done, and this says so

Resolution is half of reaching a site. The browser then connects to the gateway with Host: scrambled.eggs, and today nothing accepts that Host — curl -H 'Host: fuck.yeah' http://<pit address>/ gets Railway's {"status":"error","code":404,"message":"Application not found"}, and no Moshpit name can be registered against that service, because the premise is that anyone can invent one.

deploy/Caddyfile.gateway is the any-Host ingress to run alongside a resolver, with MOSHPIT_GATEWAY_A pointed at it; it serves the clearnet preview page (0004 R11) until the hosting grid exists to proxy into. The resolver does not change either way — that is why the gateway address is a setting.

Also not fixed, and documented: HTTPS on a Moshpit name (no public CA will issue for scrambled.eggs), and DNSSEC on synthesized answers (no chain from a root the namespace does not descend from). Forwarded clearnet answers keep their signatures.

One assumption to confirm

The deployed registry returns fields no branch in this repo has yet (target, name_registered, prefer). The client reads target and honours it when DNS can express it — an address becomes A/AAAA, a hostname becomes a CNAME, a URL falls back to the gateway. It deliberately ignores the owner-supplied prefer: on a public resolver, letting a name owner declare that their lookalike outranks the real domain is the hijack the clearnet-first default exists to prevent. Say the word if prefer was meant to bind resolvers.

Deploying

docs/moshpit-dns.md has the records to publish (dns1.pit / dns2.pit as explicit A records — an exact name beats the *.pit wildcard that serves the gateway), client setup per OS, Docker, systemd, and the operating notes.

Not Railway: DNS is UDP. A small VPS with a static address is the entire requirement.

🤖 Generated with Claude Code

…nternet

A Moshpit name only resolved for people running the extension or a node.
These are public resolvers anyone can point a laptop, phone or router at:
`.moshpit`, `.eggs`, `.yeah` come from the registry, and `.com` and everything
else is forwarded to 8.8.8.8 and 1.1.1.1, relayed byte for byte so DNSSEC and
EDNS survive the trip. One setting, no install — PRD 0004 R1 approached from
the resolver side rather than asking people to type a gateway URL.

Which namespace owns a contested name (`profullstack.ai` exists in both) is a
mode, defaulting to clearnet-wins with the registry as a backfill: a public
resolver that silently redirected a domain resolving perfectly well would be
indistinguishable from a hijack. `MOSHPIT_RESOLVE_MODE=moshpit` is the
override. This is the extension's policy, so switching between them does not
change which site you land on.

The wire codec is written out rather than pulled from npm: this is the one
process strangers can send arbitrary bytes to on UDP/53, and a parser bug there
is remotely reachable by anyone. Nothing is installed at all — node built-ins
and fetch — which is also why the container needs no dependency step.

Open resolvers are reflection amplifiers, so the defences are on by default: a
per-client token bucket that drops rather than replies, ANY refused,
non-recursive clearnet queries refused, and responses arriving at the listening
socket ignored.

Failure is one-directional on purpose. A registry outage costs Moshpit names
and leaves the web alone; a gateway with no known address answers nothing
rather than caching a name as address-less; an upstream that dies is SERVFAIL
rather than a hung client.

Also: DoH on the same policy for locked-down machines where system DNS is not
yours to change, docs/moshpit-dns.md for the records to publish and how to run
one, and deploy/ for Docker and systemd.

The gateway half is not done and the docs say so: Railway's edge answers
"Application not found" for a Moshpit Host, and no Moshpit name can be
registered against it. deploy/Caddyfile.gateway is the any-Host ingress to
point MOSHPIT_GATEWAY_A at until the hosting grid lands.

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 marked this pull request as ready for review July 31, 2026 03:21
@ralyodio
ralyodio merged commit 9244623 into master Jul 31, 2026
2 checks passed
@ralyodio
ralyodio deleted the feat/moshpit-dns branch July 31, 2026 03:21
ralyodio added a commit to moshcoder/moshcode that referenced this pull request Jul 31, 2026
The pit could sell you `scrambled.eggs` and then leave you with no way to open
it. Resolvers now exist (moshcoder/moshcoding#77) that answer Moshpit names
from this registry and forward everything else to the ordinary internet — this
is the page that tells a person how to use one.

A tab rather than a link in a paragraph: `/pit` is the namespace and `/pit/dns`
is how you reach it, two halves of the same thing, and nobody finds the second
one buried in prose.

The addresses come from MOSHPIT_DNS_RESOLVERS rather than the source, because
they are operational facts that change when a box moves, and a page that keeps
telling people to use an address that moved is worse than one that says
nothing. Unset — which is today — it says the resolvers are not published yet
and explains how to run one. It never invents an address for a stranger to
paste into their network settings, and entries that are not addresses are
dropped rather than rendered.

Also states what does not work: HTTPS on a Moshpit name warns, because no
public CA will issue for a namespace outside the ICANN root, and clearnet
lookups are forwarded to Google and Cloudflare. Both are reasons to run your
own, which the page then tells them how to do.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ralyodio added a commit to moshcoder/moshcode that referenced this pull request Jul 31, 2026
)

* feat(pit): a DNS tab, so the namespace is reachable from a machine

The pit could sell you `scrambled.eggs` and then leave you with no way to open
it. Resolvers now exist (moshcoder/moshcoding#77) that answer Moshpit names
from this registry and forward everything else to the ordinary internet — this
is the page that tells a person how to use one.

A tab rather than a link in a paragraph: `/pit` is the namespace and `/pit/dns`
is how you reach it, two halves of the same thing, and nobody finds the second
one buried in prose.

The addresses come from MOSHPIT_DNS_RESOLVERS rather than the source, because
they are operational facts that change when a box moves, and a page that keeps
telling people to use an address that moved is worse than one that says
nothing. Unset — which is today — it says the resolvers are not published yet
and explains how to run one. It never invents an address for a stranger to
paste into their network settings, and entries that are not addresses are
dropped rather than rendered.

Also states what does not work: HTTPS on a Moshpit name warns, because no
public CA will issue for a namespace outside the ICANN root, and clearnet
lookups are forwarded to Google and Cloudflare. Both are reasons to run your
own, which the page then tells them how to do.

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

* feat(pit): land a typed-in name on the shortest path to holding it

`/pit?name=mosh.whatever` is where a resolver or the gateway sends someone
whose name did not resolve to a site. They have just demonstrated demand for a
name, so the page opens with what they can do about it rather than a 404.

Four honest answers, because what can be offered depends on who holds the
ending, and today only a TLD's owner may mint names under it:

  nobody holds `.whatever`  -> claim the ending, prefilled; the name and every
                               other one under it comes with it
  you hold it               -> one form, prefilled, registers `mosh.whatever`
  you already minted it     -> it is yours, it is in your list
  someone else holds it     -> say so. `registerName` refuses anyone but the
                               owner and there is no way for them to sell it
                               through the pit yet, so an offer button would be
                               an invitation into a flow that does not exist

The decision lives in lib/moshpit-landing.mjs with no database and no request,
so the wording is testable and cannot drift from the rule it describes.

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

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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