feat(dns): route every lookup here, instead of naming each claimed ending - #195
Merged
Conversation
…ding
Routing endings one at a time does not survive the registry growing, and
it fails silently when it stops. `dns install` wrote all 4586 claimed
endings onto one `Domains=` line; systemd-resolved took 1090 of them
alphabetically, rejected the remaining 3496 with "Argument list too long"
one journal line at a time, and reported success. `moshcode dns status`
said "routing configured" and was satisfied.
The result is a failure with no visible cause. `moshcode dns resolve
chovy.hacker` answered, because it asks the registry over HTTPS, while
`curl chovy.hacker` could not resolve, because it asks the OS — which had
never been told about `.hacker`. Nothing in between said why, and every
ending anyone claims makes the cut deeper.
`Domains=~.` is one entry that never grows. The bill is that this bridge
now sees every lookup on the machine, so it has to be a resolver rather
than an oracle for a list:
- Anything that is not a claimed ending is relayed byte for byte,
including question types this has no opinion about. Re-encoding them
would mean implementing the whole record space correctly to avoid
corrupting answers we only need to pass along.
- `isOurs` is the gate that makes this safe. `google.com` has exactly
two labels, the same as `blue.eggs`, so parsing alone would have the
bridge answer for the clearnet. Only an ending someone has actually
claimed is ours. An unknown ending set means "not ours": failing that
way costs a Moshpit name until the registry answers again, failing
the other way costs the whole internet on that machine.
- Silent upstreams are SERVFAIL, not NXDOMAIN. "I could not find out"
is retried elsewhere; "it does not exist" gets cached and the name
stays broken after the network comes back.
- Loopback nameservers are dropped when discovering upstreams. Once
routing points here, 127.0.0.53 is the thing asking us.
Additive: with no upstreams configured the bridge behaves exactly as it
did, so the per-ending deployment keeps working and the existing config
generators are untouched.
The gate is verified by removing it — 4 tests fail, including the one
that asserts google.com is never ours.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
vu1nz Security Review0 finding(s) in PR #? No security issues found. |
ralyodio
added a commit
that referenced
this pull request
Aug 1, 2026
#195 added the catch-all config generators, upstream parsing and the forwarding path, and wired none of them in. `dns enable` still called resolvedConf(tlds) and `dns start` never passed upstreams, so v0.16.0 shipped the capability and none of the behaviour: a box that upgraded got the same 4586-ending list, the same silent truncation at the resolver's cap, and the same `curl: (6) Could not resolve host`. The release notes said routing had stopped being a list. It had not. Wiring it is the easy half. The hard half is that catch-all routing is only safe when the bridge can forward what is not ours — point every lookup at a bridge with no upstreams and the machine loses DNS entirely, which is far worse than a Moshpit name that does not resolve. So it is conditional by construction rather than by flag: - `discoverUpstreams` reads /etc/resolv.conf BEFORE routing is switched, because afterwards resolv.conf may point at us and the real servers are no longer discoverable from it - loopback entries are dropped, so the bridge cannot forward to itself - upstreams found → `Domains=~.` and the bridge forwards - none found → the per-ending list, exactly as before, which can only ever break Moshpit names - `dns start` passes the same upstreams and the claimed-ending set to the server, and says which upstreams it will use The dnsmasq backend follows the same rule, with no-resolv so it does not inherit upstreams that point back here. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.
The bug, found the hard way
moshcode dns resolve chovy.hackeranswered.curl chovy.hackersaid Could not resolve host. Both on the same machine, seconds apart.They ask different things —
resolvequeries the registry over HTTPS,curlasks the OS — and the OS genuinely had never been told about.hacker:moshpit.confTaken alphabetically until the cap, so
~hackerlanded in the dropped 76%.moshcode dns statusreported "routing configured" throughout. Every ending anyone claims makes the cut deeper.The fix
Domains=~.— one entry that never grows.The bill is that the bridge now sees every lookup on the machine, so it has to be a resolver rather than an oracle for a list:
isOursis the gate that makes this safe.google.comhas exactly two labels, same asblue.eggs. Parsing alone would have the bridge answer for the clearnet. Only an ending someone has actually claimed is ours.127.0.0.53is the thing asking us, and forwarding back is a loop.no-resolv, so it doesn't inherit upstreams from/etc/resolv.confthat may point back here.Additive, not a swap
With no upstreams configured the bridge behaves exactly as before, so the per-ending deployment keeps working and
resolvedConf/dnsmasqConfare untouched — which also keeps the drift test against@moshcoder/moshpit-dnsmeaningful.Tests
99 tests, 98 pass, 1 skipped (
dns-drift, package not installed locally).The safety gate is verified by removing it: 4 tests fail, including the one asserting
google.comis never ours. A test that can't fail wouldn't be worth having for this.Not in this PR
moshcode dns statusstill reports "routing configured" without checking what the resolver actually loaded — the silence that made this take so long to find. It should compare what it wrote againstresolvectl domainand say "wrote 4586, resolver accepted 1090". Worth doing regardless of this change.@moshcoder/moshpit-dnswill need the same change to stay in step.🤖 Generated with Claude Code