Skip to content

dns: trust the registry's root, in dns enable and a new dns ca verb - #520

Merged
ralyodio merged 1 commit into
mainfrom
feat/dns-registry-ca
Sep 16, 2026
Merged

ralyodio merged 1 commit into
mainfrom
feat/dns-registry-ca

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

What

moshcode dns enable now installs the registry's root, and a new moshcode dns ca does only that. With #519 the registry signs a certificate for every name it holds, so a machine that trusts that root once trusts every Moshpit name over https: curl, Firefox, Chromium, git, all of it. The per-machine local CA and dns trust <name> stay as the fallbacks.

How

  • dns enable (unless --no-trust): fetch /api/moshpit/ca and /api/moshpit/ca.crt, check the served root is a self-signed CA:TRUE certificate whose SHA-256 fingerprint matches what the registry reports for it (two fetches that must agree, so a truncated or substituted download is refused rather than installed), keep it at ~/.moshpit/ca/registry-root.crt, install into the NSS store (no root needed) and the system store (as root), nickname Moshpit Root CA. That nickname is what TronBrowser's launcher uses, so neither imports the other's work twice. Operator home and ownership handling are the same as the local CA path.
  • dns disable (unless --keep-trust) takes it back out of both stores and removes the file.
  • dns ca installs just the root; dns ca --remove removes it. For a machine whose resolution comes from elsewhere (a router running the bridge, DoH, TronBrowser's own resolver) but whose clients still refuse the certificates.
  • trustStores() / untrustPlan() take nickname and systemFile parameters; defaults unchanged, so the local-CA path is byte-for-byte what it was.
  • A registry without a CA (enabled: false) or an unreachable one is reported and skipped; nothing about resolution changes.

The root carries no name constraints. X.509 cannot express "everything except the ICANN root" across seventeen thousand endings; what bounds it is the signer, which refuses real TLDs, signs only for the account that controls a name, and issues 30-day leaves. That is why installing it stays an explicit act (dns enable, dns ca) and never something a resolver does on its own.

Verified

  • test/trust-registry.test.mjs (11): accepts a matching self-signed CA root, refuses a fingerprint mismatch, a leaf, and garbage; store plans carry the registry nickname and file; NSS without root, system only as root, neither without certutil; apply writes the root, imports, copies, refreshes, and hands the database back to the operator; a mismatched root installs nothing; no-CA and unreachable registries are reported; remove takes both stores and the file.
  • Existing test/trust.test.mjs unchanged and green; full root suite 3282 pass, 0 fail.
  • Live on this dev box: node bin/moshcode.mjs dns ca fetched the production root (fingerprint 4A:57:66:EC:…), imported it into ~/.pki/nssdb as Moshpit Root CA C,,, and reported the system store as needing root.

🤖 Generated with Claude Code

…nable and a new dns ca verb

The registry now signs a certificate for every name it holds (#519), so a machine that trusts its root once trusts every Moshpit name over https: curl, Firefox, Chromium, git. dns enable fetches /api/moshpit/ca and /api/moshpit/ca.crt, checks the served root is a self-signed CA:TRUE certificate whose fingerprint matches what the registry reports, keeps it at ~/.moshpit/ca/registry-root.crt, and installs it into the NSS store (no root) and the system store (as root) under the nickname Moshpit Root CA, the same nickname TronBrowser's launcher uses so neither imports the other's work twice. dns disable takes it out. moshcode dns ca does only that half, for a machine whose resolution comes from elsewhere; --remove undoes it. trustStores/untrustPlan take the nickname and system file as parameters, defaults unchanged. The root carries no name constraints, since X.509 cannot say everything except the ICANN root across seventeen thousand endings; the signer is the bound, and installing it stays an explicit act.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
});

test("trustStores takes a nickname and a system file, and defaults to the local CA's", () => {
const local = trustStores({ platform: "linux", home: "/home/x", caFile: "/tmp/a.crt" });
assert.ok(local.find((s) => s.id === "nss").args.includes("Moshpit Local CA"));
assert.equal(local.find((s) => s.id === "system").copyTo, "/usr/local/share/ca-certificates/moshpit-local-ca.crt");

const registry = trustStores({ platform: "linux", home: "/home/x", caFile: "/tmp/r.crt",
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

10 finding(s) in the 4 file(s) this pull request changes.

HIGH/CRITICAL: 1 | MEDIUM: 9

Severity Rule Location
HIGH tls-verification-disabled src/dns.mjs:766
MEDIUM sql-string-concatenation src/cli-schema.mjs:161
MEDIUM sql-string-concatenation src/cli-schema.mjs:220
MEDIUM sql-string-concatenation src/cli-schema.mjs:592
MEDIUM sql-string-concatenation src/cli-schema.mjs:625
MEDIUM sql-string-concatenation src/cli-schema.mjs:732
MEDIUM sql-string-concatenation src/cli-schema.mjs:1484
MEDIUM sql-string-concatenation src/cli-schema.mjs:1502
MEDIUM insecure-temp-file test/trust-registry.test.mjs:65
MEDIUM insecure-temp-file test/trust-registry.test.mjs:69
91 pre-existing finding(s) elsewhere in the repository — **HIGH/CRITICAL**: 7 | **MEDIUM**: 73 | **LOW**: 11

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:258
HIGH sh-remote-script-execution install.sh:269
HIGH sh-remote-script-execution install.sh:275
MEDIUM sql-template-interpolation apps/pwa/src/lib/moshpit-certs.mjs:44
MEDIUM sql-template-interpolation apps/pwa/src/lib/moshpit-certs.mjs:82
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

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

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit e7a198a into main Sep 16, 2026
6 checks passed
@ralyodio
ralyodio deleted the feat/dns-registry-ca branch September 17, 2026 09:12
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.

2 participants