feat(dns): --upstream, so a supervised bridge can bootstrap - #460
Merged
Conversation
`discoverUpstreams` reads the machine's resolv.conf and drops loopback. That is
right until the machine's resolver IS this bridge: then the only nameserver on
file is the bridge itself, discovery correctly refuses to return it, and the
daemon comes up with nowhere to forward.
The result is total and self-concealing. Every clearnet name answers NXDOMAIN,
including `pit.moshcode.sh` — so the bridge never learns which endings are
Moshpit and answers for nothing either. The machine has no DNS at all and
cannot look up the reason, because the lookup goes through the bridge.
`dns enable` never hit this, which is why it went unnoticed: it runs before the
routing exists, so discovery still sees the real servers. A bridge supervised by
the unit added in v0.78.0 hits it every boot, because the resolved drop-in is a
file and is already in place when the service starts. Shipping `dns service`
therefore turned a latent bug into one that fires on every restart.
Observed on a Kubuntu desktop: the bridge up and listening, resolved correctly
routed to it, and a journal consisting entirely of
pit.moshcode.sh → NXDOMAIN
api.morgen.so → NXDOMAIN
mail-ads.google.com → NXDOMAIN
from the bridge that was supposed to answer them.
So the upstreams become sayable rather than only discoverable:
moshcode dns start --port 5354 --upstream 1.1.1.1
moshcode dns start --port 5354 --upstream 1.1.1.1,1.0.0.1
moshcode dns start --port 5354 --upstream 9.9.9.9#5353
Repeatable and comma-separated both work, `address#port` matches resolv.conf and
dnsmasq rather than inventing a third spelling, and anything that is not an
address is reported rather than dropped — silently discarding it leaves exactly
the bridge-that-forwards-nowhere this flag exists to prevent.
Named upstreams override discovery outright. Someone who says where to forward
has almost always said it because discovery got it wrong.
The half that actually fixes the reboot is `dns service`: it now resolves the
upstreams while a working resolver is still there to be asked, and bakes them
into the unit's ExecStart. At boot the daemon no longer has to guess. When
discovery comes up empty at generation time, the command says so and prints the
flag to pass instead of writing a unit that will fail silently.
`dns start` also explains the empty case now, since the symptom (every name
NXDOMAIN) looks nothing like the cause.
Verified live: a bridge started with `--upstream 1.1.1.1,1.0.0.1` logs
`forwarding non-Moshpit lookups to 1.1.1.1, 1.0.0.1 (--upstream)`, loads all
18224 endings, forwards example.com, and resolves alt.2600. Generated units
carry the flag; units with nothing to record omit it rather than writing an
empty one. Suite: 2716 tests, 0 failures.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ThnQwoieWt8VR6N7gtgnhp
ThreatCrush Security Scan4 finding(s) in the 5 file(s) this pull request changes. HIGH/CRITICAL: 1 | MEDIUM: 3
84 pre-existing finding(s) elsewhere in the repository — **HIGH/CRITICAL**: 4 | **MEDIUM**: 70 | **LOW**: 10Not introduced by this pull request. The full set is in the Security tab.
…and 64 more. Full results in the Security tab. Snippets are redacted; ThreatCrush never prints matched credential material. |
Merged
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.
Fixes the bug that v0.78.0 exposed. Diagnosed on a live Kubuntu desktop, not from the source.
The bridge could not bootstrap through itself
discoverUpstreamsreads the machine's resolv.conf and drops loopback. Correct — until the machine's resolver is this bridge. Then the only nameserver on file is the bridge, discovery rightly refuses to return it, and the daemon comes up with nowhere to forward.The failure is total and hides its own cause:
Every clearnet name NXDOMAINs — including
pit.moshcode.sh, so the bridge never learns which endings are Moshpit and answers for nothing either. The machine has no DNS at all and cannot look up why, because the lookup goes through the bridge.dns enablenever hit this, which is why it stayed hidden: it runs before the routing exists, so discovery still sees real servers. A bridge supervised by the unit added in #458 hits it every boot, because the resolved drop-in is a file and is already in place when the service starts. Shippingdns serviceturned a latent bug into one that fires on every restart.So upstreams become sayable
Repeatable and comma-separated both work.
address#portmatches resolv.conf and dnsmasq rather than inventing a third spelling. Anything that isn't an address is reported, not dropped — silently discarding it leaves exactly the forwards-nowhere bridge this flag exists to prevent. Named upstreams override discovery outright.The half that actually fixes reboots
dns servicenow resolves the upstreams while a working resolver is still there to be asked, and bakes them into the unit:ExecStart=/…/node /…/moshcode.mjs dns start --port 5354 --registry https://pit.moshcode.sh --upstream 67.207.67.3,67.207.67.2At boot the daemon no longer has to guess. When discovery is empty at generation time, the command says so and prints the flag to pass instead of writing a unit that will fail silently.
dns startexplains the empty case too, since the symptom looks nothing like the cause.Verification
Live, on a real bridge, on a spare port so the box's own resolver was never touched:
--upstream 1.1.1.1,1.0.0.1logsforwarding non-Moshpit lookups to 1.1.1.1, 1.0.0.1 (--upstream)example.com→ real A recordsalt.2600→67.205.189.229Parser exercised across single/comma/repeated/
#port/IPv6/invalid/missing-value/duplicate/absent.9 new tests. Full suite: 2716 tests, 0 failures, 334 skipped (the usual
apps/pwaguards).🤖 Generated with Claude Code
https://claude.ai/code/session_01ThnQwoieWt8VR6N7gtgnhp