Skip to content

Ship the Moshpit Root CA: fetched on install and upgrade, trusted by the launcher on every start - #109

Merged
ralyodio merged 1 commit into
mainfrom
feat/moshpit-root
Sep 16, 2026
Merged

ralyodio merged 1 commit into
mainfrom
feat/moshpit-root

Conversation

@ralyodio

Copy link
Copy Markdown
Contributor

What

TronBrowser ships the Moshpit Root CA. The registry now signs a certificate for every name it holds (moshcoder/moshcode#519), so one root, installed once, makes every Moshpit name trusted over https. No per-name imports, no pins to check, no flags.

How

  • install.sh ensure_moshpit_root: on install and on every tron upgrade, fetch GET /api/moshpit/ca and GET /api/moshpit/ca.crt from the registry. Two answers that must agree: the SHA-256 fingerprint the registry reports for its root, and the certificate itself. A mismatch, or a certificate not marked CA:TRUE, is refused with a warning and nothing is installed. Unchanged root: no-op. Kept next to the launcher as moshpit-root-ca.crt. Skip with TB_NO_MOSHPIT_ROOT=1; another registry with TRONBROWSER_MOSHPIT_REGISTRY. ensure-moshpit-root subcommand for retries.
  • launcher/tronbrowser sync_moshpit_trust: the shipped root (and ~/.moshpit/ca/registry-root.crt, where moshcode dns ca keeps its copy) is imported first into every browser trust database the launcher writes, as an anchor (C,,), under the nickname Moshpit Root CA, the same one moshcode dns enable uses, so neither imports the other's work twice.
  • ensure_certutil now also runs on machines that have only the shipped root, not just ones with moshcode's certificates, since the root ships with TronBrowser itself.
  • Doc updated. The pit helper's per-name import stays for origins that still self-sign and is otherwise idle.

Verified

Against the live registry, in a fake app dir and a fake $HOME:

  • fetch installs the root; subject CN=Moshpit Root CA, O=Moshpit, fingerprint 4A:57:66:EC:8C:1F:10:F8:… (matches the generated root in the vault);
  • second run is a quiet no-op; TB_NO_MOSHPIT_ROOT=1 skips;
  • a substituted certificate is refused ("does not match the fingerprint it reports") and nothing is written;
  • the launcher's sync imports it into the NSS store as Moshpit Root CA C,,.
  • sh -n on both scripts.

Both halves must land: the tag carries the launcher; install.sh deploys from main to tronbrowser.dev and is what tron upgrade runs. Origins serving registry-signed chains is the moshpit-proxy PR that follows; until an origin renews, its self-signed leaf still goes through the per-name path.

🤖 Generated with Claude Code

…the launcher on every start

The registry now signs a certificate for every name it holds (moshcoder/moshcode#519), so one root makes every Moshpit name trusted over https. install.sh ensure_moshpit_root fetches /api/moshpit/ca and /api/moshpit/ca.crt on install and on tron upgrade, refuses a root whose fingerprint does not match what the registry reports or that is not CA:TRUE, and keeps it next to the launcher as moshpit-root-ca.crt. The launcher's sync_moshpit_trust imports it into every browser trust store it writes under the nickname Moshpit Root CA, the one moshcode dns enable uses, so neither imports the other's work twice. certutil is now installed on machines that have the shipped root, not only ones with moshcode's certificates. Verified against the live registry: fetch, no-op on rerun, skip switch, a tampered answer refused, and the launcher importing it as C,,.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

ThreatCrush Security Scan

56 finding(s)

HIGH/CRITICAL: 3 | MEDIUM: 33 | LOW: 20

Severity Rule Location
HIGH py-ssrf-outbound-request apps/desktop/launcher/tron-tor-helper:303
HIGH py-ssrf-outbound-request apps/desktop/launcher/tron-tor-helper:437
HIGH sh-remote-script-execution apps/web/public/install.sh:754
MEDIUM js-open-redirect apps/desktop/extensions/ai-sidebar/install-helper.js:156
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/media.js:34
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/media.js:57
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/newtab.js:237
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/newtab.js:266
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/newtab.js:336
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/options.js:305
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/sidepanel.js:78
MEDIUM js-unescaped-html-sink apps/desktop/extensions/ai-sidebar/sidepanel.js:166
MEDIUM sh-remote-script-execution apps/desktop/launcher/tronbrowser:156
MEDIUM sh-remote-script-execution apps/desktop/launcher/tronbrowser:479
MEDIUM js-unescaped-html-sink apps/extensions/public/store.js:77
MEDIUM js-unescaped-html-sink apps/extensions/public/store.js:227
MEDIUM js-unescaped-html-sink apps/extensions/public/store.js:569
MEDIUM js-unescaped-html-sink apps/web/public/app.js:29
MEDIUM js-unescaped-html-sink apps/web/public/dns.js:54
MEDIUM sh-remote-script-execution apps/web/public/install.sh:161
MEDIUM sh-remote-script-execution apps/web/public/install.sh:166
MEDIUM sh-remote-script-execution apps/web/public/install.sh:279
MEDIUM sh-remote-script-execution apps/web/public/install.sh:297
MEDIUM sh-remote-script-execution apps/web/public/install.sh:810
MEDIUM sh-remote-script-execution apps/web/public/install.sh:861
MEDIUM sh-remote-script-execution apps/web/public/install.sh:1150
MEDIUM js-unescaped-html-sink apps/web/public/settings.js:96
MEDIUM js-unescaped-html-sink apps/web/public/settings.js:168
MEDIUM js-dynamic-code-execution packages/sdk/src/mcp/tools.ts:80
MEDIUM js-dynamic-code-execution packages/sdk/src/mcp/tools.ts:92
MEDIUM js-dynamic-code-execution packages/sdk/src/mcp/tools.ts:103
MEDIUM js-dynamic-code-execution packages/sdk/src/page.ts:65
MEDIUM js-dynamic-code-execution packages/sdk/src/page.ts:70
MEDIUM sql-template-interpolation services/api/src/store/db.ts:116
MEDIUM js-dynamic-code-execution services/api/src/store/scanner.ts:44
MEDIUM sh-predictable-temp-path start.sh:25
LOW js-dynamic-code-execution apps/desktop/extensions/ai-sidebar/pit-proxy.test.js:8
LOW js-dynamic-code-execution packages/agent-runtime/src/analyze/form-script.test.ts:6
LOW js-unescaped-html-sink packages/agent-runtime/src/analyze/form-script.test.ts:26
LOW js-unescaped-html-sink packages/agent-runtime/src/analyze/form-script.test.ts:49
LOW js-dynamic-code-execution packages/browser-core/src/automation/extract-script.test.ts:6
LOW js-unescaped-html-sink packages/browser-core/src/automation/extract-script.test.ts:35
LOW js-unescaped-html-sink packages/browser-core/src/automation/extract-script.test.ts:47
LOW js-unescaped-html-sink packages/browser-core/src/automation/extract-script.test.ts:70
LOW js-unescaped-html-sink packages/browser-core/src/automation/extract-script.test.ts:89
LOW js-dynamic-code-execution packages/browser-core/src/automation/snapshot-script.test.ts:11
LOW js-unescaped-html-sink packages/browser-core/src/automation/snapshot-script.test.ts:24
LOW js-unescaped-html-sink packages/browser-core/src/automation/snapshot-script.test.ts:63
LOW js-unescaped-html-sink packages/provenance/src/scan.test.ts:47
LOW js-unescaped-html-sink packages/provenance/src/scan.test.ts:170

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

Snippets are redacted; ThreatCrush never prints matched credential material.

@ralyodio
ralyodio merged commit e33e9b9 into main Sep 16, 2026
8 checks passed
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