Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions apps/desktop/launcher/tronbrowser
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,7 @@ fi
# it already wrote is recognised as done rather than imported a second time.
moshpit_nickname() { # cert_file
case "$1" in
*/moshpit-root-ca.crt|*/.moshpit/ca/registry-root.crt) printf 'Moshpit Root CA' ;;
*/moshpit-local-ca.crt|*/.moshpit/ca/ca.crt) printf 'Moshpit Local CA' ;;
*)
_base="$(basename "$1" .crt)"
Expand Down Expand Up @@ -630,7 +631,12 @@ sync_moshpit_trust() {
# Each word below is a literal path or a glob result, so this stays
# whitespace-safe; an unmatched glob arrives as the pattern itself and fails
# the -f test.
for _cert in /usr/local/share/ca-certificates/moshpit-*.crt \
# The registry's root first: the one TronBrowser ships itself (install.sh
# ensure_moshpit_root), which makes every Moshpit name trusted at once. The
# rest is whatever `moshcode dns enable` / `dns trust` installed system-wide.
for _cert in "$DIR/moshpit-root-ca.crt" \
"$HOME/.moshpit/ca/registry-root.crt" \
/usr/local/share/ca-certificates/moshpit-*.crt \
/etc/ca-certificates/trust-source/anchors/moshpit-*.crt \
/etc/pki/ca-trust/source/anchors/moshpit-*.crt \
"$HOME/.moshpit/ca/ca.crt"; do
Expand Down Expand Up @@ -662,9 +668,9 @@ sync_moshpit_trust() {
_flag="C,,"
fi
else
# No openssl: the Local CA is the only anchor Moshpit ships, and it is the
# one file we can identify by name alone.
case "$_nick" in "Moshpit Local CA") _flag="C,," ;; esac
# No openssl: the two CAs are the only anchors Moshpit ships, and they
# are the files we can identify by name alone.
case "$_nick" in "Moshpit Local CA"|"Moshpit Root CA") _flag="C,," ;; esac
fi

if certutil -d "sql:$_nssdb" -A -t "$_flag" -n "$_nick" -i "$_cert" >/dev/null 2>&1; then
Expand Down
43 changes: 43 additions & 0 deletions apps/web/public/install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -811,6 +811,42 @@ ensure_engine() {
return 1
}

# The Moshpit registry runs a certificate authority for the names it holds
# (moshcode apps/pwa/docs/moshpit-ca.md). One root, installed once, and every
# Moshpit name is trusted over https — no per-name imports, no pins to check.
# Fetched here on install and on every upgrade, kept next to the launcher as
# moshpit-root-ca.crt, and the launcher imports it into the browser's trust
# store on every start (sync_moshpit_trust). Two fetches that must agree: the
# fingerprint the registry reports for its root, and the root itself. Skip
# with TB_NO_MOSHPIT_ROOT=1; another registry with TRONBROWSER_MOSHPIT_REGISTRY.
MOSHPIT_REGISTRY="${TRONBROWSER_MOSHPIT_REGISTRY:-https://pit.moshcode.sh}"

ensure_moshpit_root() {
[ "${TB_NO_MOSHPIT_ROOT:-0}" = "1" ] && return 0
_ldir="$(find "$APP_DIR" -maxdepth 3 -type f -name tronbrowser 2>/dev/null | head -n1)"
[ -n "$_ldir" ] || return 0
dest="$(dirname "$_ldir")/moshpit-root-ca.crt"
tmp="$(mktemp -d)"
status="$(curl -fsSL --max-time 15 "$MOSHPIT_REGISTRY/api/moshpit/ca" 2>/dev/null || true)"
case "$status" in
*'"enabled":true'*) ;;
*) rm -rf "$tmp"; return 0 ;; # no CA published: nothing to ship, nothing to say
esac
want="$(printf '%s' "$status" | sed -n 's/.*"fingerprint_sha256":"\([^"]*\)".*/\1/p' | tr -d ':' | tr 'a-f' 'A-F')"
if ! fetch "$MOSHPIT_REGISTRY/api/moshpit/ca.crt" "$tmp/root.crt" 2>/dev/null; then rm -rf "$tmp"; return 0; fi
if command -v openssl >/dev/null 2>&1; then
got="$(openssl x509 -in "$tmp/root.crt" -noout -fingerprint -sha256 2>/dev/null | sed 's/.*=//' | tr -d ':' | tr 'a-f' 'A-F')"
if [ -z "$got" ] || [ -z "$want" ] || [ "$got" != "$want" ]; then
warn "The Moshpit root the registry served does not match the fingerprint it reports; not installing it."
rm -rf "$tmp"; return 1
fi
openssl x509 -in "$tmp/root.crt" -noout -ext basicConstraints 2>/dev/null | grep -q 'CA:TRUE' || { rm -rf "$tmp"; return 1; }
fi
if [ -f "$dest" ] && cmp -s "$tmp/root.crt" "$dest"; then rm -rf "$tmp"; return 0; fi
install -m 0644 "$tmp/root.crt" "$dest" && info "Installed the Moshpit Root CA (every Moshpit name over https) to $dest"
rm -rf "$tmp"
}

ensure_obscura() {
[ "${TB_NO_OBSCURA_INSTALL:-0}" = "1" ] && return 0
obdest="$APP_DIR/obscura-bin"
Expand Down Expand Up @@ -894,6 +930,10 @@ ensure_certutil() {
"$HOME/.moshpit/ca/ca.crt"; do
if [ -f "$_c" ]; then _have_moshpit=1; break; fi
done
# Or the root TronBrowser ships itself (ensure_moshpit_root), which is the
# ordinary case now that the registry signs.
_ldir2="$(find "$APP_DIR" -maxdepth 3 -type f -name tronbrowser 2>/dev/null | head -n1)"
[ -n "$_ldir2" ] && [ -f "$(dirname "$_ldir2")/moshpit-root-ca.crt" ] && _have_moshpit=1
[ "$_have_moshpit" = "1" ] || return 0

info "Setting up certutil (so Moshpit names load over HTTPS)…"
Expand Down Expand Up @@ -971,6 +1011,7 @@ DESKTOP
# abort an install that has already put the browser on disk.
ensure_tor || true # so the in-browser 🧅 Tor toggle works out of the box
ensure_certutil || true # so Moshpit names load over HTTPS on first launch
ensure_moshpit_root || true # the registry's root: every Moshpit name over https
ensure_obscura || true # so 'tron automate' has its scraping engine
brand_macos_icon "$(dirname "$bin")/tronbrowser.png"

Expand Down Expand Up @@ -1080,6 +1121,7 @@ do_upgrade() {
ensure_browser # still make sure Ungoogled Chromium is installed
ensure_tor || true # and that Tor is available for the toggle
ensure_certutil || true # and that Moshpit trust can be written
ensure_moshpit_root || true # and that the registry's root is current
ensure_obscura || true # and that the scraping engine is current
brand_macos_icon "$(find "$APP_DIR" -maxdepth 3 -name tronbrowser.png 2>/dev/null | head -n1)" # re-apply icon (Chromium updates reset it)
info "Re-install anyway with: TB_FORCE=1 tron upgrade"
Expand Down Expand Up @@ -1143,6 +1185,7 @@ case "$cmd" in
remove|uninstall) do_remove ;;
ensure-tor) ensure_tor ;;
ensure-engine) ensure_engine ;;
ensure-moshpit-root) ensure_moshpit_root ;;
ensure-obscura) ensure_obscura ;;
ensure-certutil) ensure_certutil ;;
version|--version|-v) do_version ;;
Expand Down
11 changes: 11 additions & 0 deletions docs/moshpit-pit-toggle.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ The PAC is not `mandatory`: if it ever fails to evaluate, Chromium falls back to

## HTTPS on a pit name

**Since 2026-09-16 the registry signs.** pit.moshcode.sh runs a certificate
authority for the names it holds (moshcode `apps/pwa/docs/moshpit-ca.md`):
one root, 30-day leaves per name issued to whoever controls the name. The
installer fetches that root on install and on `tron upgrade`
(`ensure_moshpit_root`, checked against the fingerprint the registry reports),
keeps it next to the launcher as `moshpit-root-ca.crt`, and the launcher
imports it into the browser's trust store on every start under the nickname
`Moshpit Root CA`, the same one `moshcode dns enable` uses. Once origins serve
registry-signed chains, that root is all a browser needs; the per-name import
below stays for origins that still self-sign and is otherwise idle.

No public CA issues for a name outside the ICANN root, so an origin such as
`chovy.hacker` serves a self-signed leaf for its own name and the registry
publishes the SHA-256 of that key (`/api/moshpit/pins?name=`, the RFC 7469 pin
Expand Down
Loading