Skip to content

feat(dns): enable supervises the proxy, disable takes it away - #470

Merged
ralyodio merged 1 commit into
mainfrom
feat/enable-does-everything
Aug 30, 2026
Merged

feat(dns): enable supervises the proxy, disable takes it away#470
ralyodio merged 1 commit into
mainfrom
feat/enable-does-everything

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

Two commands were meant to be the whole of it — install/upgrade, then dns enable. Getting a desktop working today took eleven manual steps.

The largest missing piece: moshpit-proxy ships no unit of its own, so on every machine that installed it, it sat there installed, trusted and never started. That's indistinguishable from absent — nothing on 443, no certificate, and dns enable correctly reporting no proxy on a box that had one.

enable

Writes and starts that unit before it goes looking for a proxy, and waits for 443 to actually be held rather than trusting Type=simple's idea of "active".

The unit runs the wrapper with a PATH containing the interpreter running this code — the mistake that has now bitten three separate times in this codebase, made deliberately once here — drops to the operator's account so the local root in ~/.moshpit is reachable, and is granted CAP_NET_BIND_SERVICE rather than running as root.

Validated against the hand-written unit on the one machine where this has worked for months; it independently reached the same shape (User=, absolute mise node path, MOSHPIT_PROXY_TLDS, Restart=always).

Non-fatal in every direction. A machine without a working proxy resolves Moshpit names and can't verify them. A machine whose DNS was refused because an optional component wouldn't start is worse.

disable

Stops and removes it — gated on the restore point, not on what's in /etc. A proxy unit this tool never installed is somebody else's, and stopping it for sharing a filename is exactly the guessing the manifest exists to prevent.

captureRestorePoint gains extraPaths, so both units are recorded like everything else: prior content, or null for "was not here". null is the load-bearing value — it removes a unit this run created, and preserves one that was already there.

The trust anchor needed nothing

applyTrust has always installed the local root into /usr/local/share/ca-certificates and applyUntrust removes it. That step simply never ran, because every enable died before reaching it. That's why curl failed with unable to get local issuer certificate while Chrome (NSS, set up by moshpit-trust) would have worked.

Verification

systemd-analyze verify passes on the generated unit. 7 new tests. Suite: 2741 tests, 0 failures.

Caveat worth stating: dns enable cannot be end-to-end tested on the dev box — it would take its DNS down, and it's an origin with nginx on 443 so the proxy can't run there. Every piece here is unit-tested and the proxy step is non-fatal by construction, but the full path needs one real desktop run.

🤖 Generated with Claude Code

https://claude.ai/code/session_01ThnQwoieWt8VR6N7gtgnhp

Two commands were supposed to be the whole of it — install/upgrade, then
`dns enable` — and getting a desktop working took eleven manual steps instead.
The largest missing piece: moshpit-proxy ships no unit of its own, so on every
machine that installed it, it sat there installed, trusted and never started.
Indistinguishable from absent — nothing on 443, no certificate, and `dns enable`
correctly reporting no proxy on a box that had one.

`dns enable` now writes and starts that unit before it goes looking for a
proxy, and waits for 443 to actually be held rather than trusting Type=simple's
idea of "active". The unit runs the wrapper with a PATH containing the
interpreter running this code — the mistake that has now bitten three times in
this codebase, made deliberately once here — drops to the operator's account so
the local root in ~/.moshpit is reachable, and is granted CAP_NET_BIND_SERVICE
rather than running as root. Validated against the hand-written unit on the one
machine where this has been working for months, which reached the same shape.

Non-fatal in every direction. A machine without a working proxy resolves
Moshpit names and cannot verify them; a machine whose DNS was refused because
an optional component would not start is worse.

`dns disable` stops and removes it, gated on the restore point rather than on
what happens to be in /etc — a proxy unit this tool never installed is somebody
else's, and stopping it for sharing a filename is exactly the guessing the
manifest exists to prevent. `captureRestorePoint` gains `extraPaths` so both
units are recorded the same way as everything else: prior content, or null for
"was not here". Null is the load-bearing value — it is what removes a unit this
run created and what preserves one that was already there.

The trust anchor needed nothing: `applyTrust` has always installed the local
root into /usr/local/share/ca-certificates and `applyUntrust` removes it. That
step simply never ran, because every enable died before reaching it.

Suite: 2741 tests, 0 failures.

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

Claude-Session: https://claude.ai/code/session_01ThnQwoieWt8VR6N7gtgnhp
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

1 finding(s) in the 3 file(s) this pull request changes.

HIGH/CRITICAL: 1

Severity Rule Location
HIGH tls-verification-disabled src/dns.mjs:766
90 pre-existing finding(s) elsewhere in the repository — **HIGH/CRITICAL**: 7 | **MEDIUM**: 73 | **LOW**: 10

Not introduced by this pull request. The full set is in the Security tab.

Severity Rule Location
HIGH js-ssrf-outbound-request apps/pwa/public/sw.js:45
HIGH tls-verification-disabled apps/pwa/src/lib/moshpit-gateway.mjs:299
HIGH sh-remote-script-execution install.sh:86
HIGH sh-remote-script-execution install.sh:90
HIGH sh-remote-script-execution install.sh:202
HIGH sh-remote-script-execution install.sh:213
HIGH sh-remote-script-execution install.sh:219
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:139
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:153
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:179
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:373
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:377
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:422
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:671
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:867
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:869
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:928
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:974
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1044
MEDIUM sql-template-interpolation apps/pwa/src/moshpit.mjs:1147

…and 70 more. Full results in the Security tab.

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit ae7e309 into main Aug 30, 2026
6 checks passed
@ralyodio ralyodio mentioned this pull request Aug 30, 2026
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