From 502c218c42ed034dc946dc1b62abcca468be44ce Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Sat, 1 Aug 2026 13:09:57 +0000 Subject: [PATCH] =?UTF-8?q?docs(prd):=200005=20=E2=80=94=20a=20hosted=20Mo?= =?UTF-8?q?shpit=20resolver,=20for=20devices=20that=20cannot=20run=20the?= =?UTF-8?q?=20bridge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A Moshpit ending is not in the ICANN root, so nothing resolves it until the client is told something. Today there is one way to be told, and it is unavailable exactly where people meet a name: a phone cannot run a daemon, a router takes a DNS server and nothing else, and the person you sent the link to has installed nothing. The fallback for all of them is /n/, which works and is not resolution — the URL bar reads pit.moshcode.sh, so the name never becomes the visitor's address, which is the point of holding one. Written as a draft rather than a plan because the central tradeoff is not solvable, only disclosed: a hosted resolver sees every DNS query the device makes, not just Moshpit ones. The PRD says so in the goals, in the UI requirement, and in the risks, and states plainly that the honest outcome may be to not ship it. Deliberately not a replacement for the local bridge, which stays the recommended path wherever it can run. The infrastructure is already half-built — MOSHPIT_DNS_RESOLVERS and MOSHPIT_DOH_URL are read, validated and rendered on /pit/dns, and both are unset in production. Co-Authored-By: Claude Opus 5 (1M context) --- prd/0005-hosted-moshpit-resolver.md | 192 ++++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 prd/0005-hosted-moshpit-resolver.md diff --git a/prd/0005-hosted-moshpit-resolver.md b/prd/0005-hosted-moshpit-resolver.md new file mode 100644 index 0000000..474357e --- /dev/null +++ b/prd/0005-hosted-moshpit-resolver.md @@ -0,0 +1,192 @@ +--- +openprd: "0.2" +id: "0005" +title: A hosted Moshpit resolver, for the devices that cannot run the bridge +status: Draft +authors: + - anthony@chovy.com +created: 2026-08-01 +updated: 2026-08-01 +repo: https://github.com/moshcoder/moshcode +discussion: +implementation: apps/pwa/src/lib/moshpit-resolvers.mjs, apps/pwa/src/routes/moshpit.mjs, src/dns.mjs +tags: + - moshpit + - dns + - infrastructure + - privacy +supersedes: +superseded-by: +--- + +## Problem + +A Moshpit ending is not in the ICANN root, so nothing resolves it until the +client is told something. Today there is exactly one way to be told: run +`moshcode dns enable`, which installs a local bridge and points the machine's +resolver at it. + +That works on a laptop or a VPS. It is unavailable on the devices where most +people would actually meet a Moshpit name: + +- a **phone**, which cannot run a daemon or edit `resolved.conf.d` +- a **router**, where you can set a DNS server and nothing else +- **someone else's machine** — the person you sent the link to + +For all of them the only answer is `pit.moshcode.sh/n/`, which fetches the +origin server-side and hands back the page. It works, and it is not resolution: +the URL bar reads `pit.moshcode.sh`, not `chovy.hacker`. The name never becomes +the visitor's address, which is the entire point of holding one. + +The infrastructure for the missing piece is already half-built and unused. +`MOSHPIT_DNS_RESOLVERS` and `MOSHPIT_DOH_URL` are read, validated as real +addresses, and rendered on `/pit/dns` — and both are unset in production, so the +page publishes nothing. The shop window was built before the shop. + +This PRD is deliberately **not** a replacement for the local bridge. It is the +answer for clients that cannot run one, and the difference matters because the +tradeoff is real and permanent: a hosted resolver sees every DNS query the +device makes. + +## Goals + +- A Moshpit name resolves on a device that can only be handed a DNS server. +- The URL bar keeps the name. `http://chovy.hacker/` stays `chovy.hacker`. +- Non-Moshpit lookups are answered correctly and are not the product. +- The privacy cost is stated plainly, in the UI, before anyone opts in. +- An outage degrades to "Moshpit names stop working", never "the internet stops + working", for as long as that is within our control. + +## Non-Goals + +- **Replacing the local bridge.** Where a bridge can run it stays the better + answer: no third party sees the queries, no round trip, no shared outage. + `moshcode dns enable` remains the recommended path and the docs say so. +- **Being a general-purpose public resolver.** We are not competing with + 1.1.1.1. Forwarding exists so Moshpit names can resolve, not as a service. +- **Logging queries for analytics.** See Requirements — this is the one place + where the tempting feature is the one that kills the product. +- **DNSSEC-signing the Moshpit zone.** Out of scope for v1; the endings are not + in the root, so there is no chain to anchor to. + +## Users + +**Someone sent a link.** They open `http://seo.rank/` on a phone. Today: a +browser error, or a `pit.moshcode.sh` URL. With this: the page, at its own name. + +**A household or office.** One DNS setting on the router, and every device on +the network resolves Moshpit names with nothing installed on any of them. + +**A name holder demonstrating one.** The reason to hold `chovy.hacker` is that +it is an address you can give people. Today giving it away requires asking them +to install software first. + +## Requirements + +### R1 — Two protocols, both public + +`Do53` (plain UDP/TCP 53) for routers and OS settings, and **DNS-over-HTTPS** +for phones and browsers, which increasingly will not accept anything else. +iOS and Android both accept a DoH profile; neither will run our daemon. + +DoH is the one that unlocks phones, so it is not optional. + +### R2 — Anycast or nothing + +A single box is a single point of failure for the DNS of every device pointed +at it. If the deployment cannot be multi-region behind one address, this ships +as "best effort, do not set it as your only resolver" and says so in the UI. + +### R3 — Forwarding is the dangerous part + +The resolver answers Moshpit endings from the registry and forwards everything +else. That forwarding is what makes it usable as a device's only DNS server — +and what makes it a privacy problem, a latency tax, and a shared outage. + +Three requirements follow, and R3.3 is the one worth arguing about: + +- **R3.1** — the claimed-ending gate from `isOurs` applies here identically. + `google.com` has two labels exactly like `blue.eggs`; only an ending someone + has actually claimed is ours. An unknown ending set means *not* ours. +- **R3.2** — forwarded answers are relayed unmodified. No injected records, no + rewritten NXDOMAIN, no "helpful" search page. The moment a resolver edits + answers it is spyware with a nice landing page. +- **R3.3** — **no query logging beyond aggregate counters.** No per-query + storage of name, source address, or the pair. This is the requirement that + will be under pressure the first time someone asks "which endings are people + looking up?" — and the honest answer is that we cannot know without becoming + the thing the local bridge exists to avoid. Counters (queries/sec, hit rate, + error rate) are fine. The tuple is not. + +### R4 — Say the cost before the click + +`/pit/dns` publishes the addresses. It must also state, in the same visual +weight as the addresses themselves: + +> This resolver sees every DNS lookup your device makes, not just Moshpit ones. +> If you can run `moshcode dns enable`, do that instead — it keeps your lookups +> on your own machine. + +A page that lists an IP with no context gets pasted into a router by someone who +has not thought about it. That is the failure mode to design against. + +### R5 — Reuse the bridge, do not fork it + +The hosted resolver is the same code as `src/dns.mjs` with upstreams +configured, deployed. The forwarding, the ending gate, the NODATA/NXDOMAIN +distinction and the AAAA support all already exist and are tested. A second +implementation would drift from the first, which is the failure this codebase +has already had five times over with one regex. + +### R6 — Never the default + +Nothing in `moshcode dns enable`, the CLI, or the extension silently points at +the hosted resolver. It is a documented address a person chooses to use. A +resolver that installs itself as your DNS without asking is malware behaviour +regardless of intent. + +## UX Notes + +`/pit/dns` already renders `.pit-addrs` cards for published resolvers, so the +page work is mostly copy plus the DoH URL. The order should be: + +1. **`moshcode dns enable`** — recommended, keeps lookups local +2. **`pit.moshcode.sh/n/`** — works with nothing installed, URL changes +3. **the hosted resolver** — for devices that cannot do (1), with R4's warning + +Today the page has only (1) and publishes nothing for (3). + +## Success Metrics + +- A stock phone, given only a DoH profile, loads `http://chovy.hacker/` with + `chovy.hacker` in the URL bar. +- Resolver p50 latency for a forwarded (non-Moshpit) query stays within ~10ms of + the device's previous resolver, or the tax is visible enough that people turn + it off. +- Zero per-query records in storage, demonstrable from the deployment config + rather than from a policy document. + +## Risks & Open Questions + +**The privacy tradeoff is not fixable, only disclosed.** Anyone using this hands +us their full DNS history. R3.3 and R4 are mitigations, not solutions. If that +is unacceptable, the honest outcome is to not ship this and leave `/n/` as the +answer for phones. + +**Shared outage.** A device with this as its only DNS server loses the whole +internet when we go down, not just Moshpit names. R2 exists for this and may +well be the reason to delay. + +**Abuse.** An open forwarding resolver is a DDoS amplifier. Rate limiting and +response-size limits are required before anything is published, and this is not +optional or deferrable — an open resolver is found by scanners in hours. + +**Does the demand exist?** The cheapest version of this experiment is to publish +nothing and instead measure how often `/n/` is loaded from mobile user agents. +If nobody is meeting Moshpit names on phones, this is infrastructure and risk +bought for an audience that is not there. + +**Open:** does the ending gate consult the registry per query, or hold a cached +ending set refreshed on an interval? Per query is simpler and always current; +cached is faster and survives a registry outage. The local bridge faces the same +choice and has not resolved it either.