From 22c079a306600c5b50e9629883d05bfdaf6492ab Mon Sep 17 00:00:00 2001 From: Anthony Ettinger Date: Wed, 16 Sep 2026 14:47:28 +0000 Subject: [PATCH] Ship TronBrowser's own engine: install the portable ungoogled-chromium, prefer it, drop the pin-flag workaround The Flathub ungoogled-chromium ignores NSS user trust (bonita, 152: the leaf sat in the exact database strace showed it opening, every sandbox switch made no difference, net log said No matching issuer found), while the same 152 as a portable build accepts that database. The pin flag from #106 worked but Chromium marks it unsupported at every start. So install.sh now fetches the pinned portable ungoogled-chromium into /engine on install and upgrade, the launcher prefers it after a --version probe, and https on Moshpit names goes back to the silent per-name NSS import. The helper (3.4.3) reports flatpak-engine so the sidebar points at tron upgrade when a Flatpak is still running. Verified end to end in a container: the real launcher picked the engine, the helper imported the leaf, https://chovy.hacker loaded with no flags. Co-Authored-By: Claude Fable 5.1 --- .../extensions/ai-sidebar/sidepanel.js | 8 +-- apps/desktop/launcher/tron-tor-helper | 35 +++------- apps/desktop/launcher/tronbrowser | 38 +++++------ apps/web/public/install.sh | 67 +++++++++++++++++++ docs/moshpit-pit-toggle.md | 28 ++++---- 5 files changed, 109 insertions(+), 67 deletions(-) diff --git a/apps/desktop/extensions/ai-sidebar/sidepanel.js b/apps/desktop/extensions/ai-sidebar/sidepanel.js index c898a12..bf3589c 100644 --- a/apps/desktop/extensions/ai-sidebar/sidepanel.js +++ b/apps/desktop/extensions/ai-sidebar/sidepanel.js @@ -320,10 +320,10 @@ async function togglePit() { const trust = res && res.trust; const httpsTip = !trust ? '' - : trust.available && trust.relaunch - ? 'https:// on a pit name: the first visit records its key (when the registry publishes a matching pin), and it loads after the next TronBrowser restart — this engine only takes pins at start.' - : trust.available - ? 'https:// on a pit name is trusted per name on first use, when the registry publishes its pin.' + : trust.available + ? 'https:// on a pit name is trusted per name on first use, when the registry publishes its pin.' + : trust.why === 'flatpak-engine' + ? 'This TronBrowser is running the Flatpak Chromium, which ignores per-name trust, so https:// on a pit name will warn. Run tron upgrade to get TronBrowser’s own engine, then relaunch.' : trust.why === 'no-certutil' ? 'https:// on a pit name will warn until certutil is installed (Debian/Ubuntu: libnss3-tools, Fedora: nss-tools, Arch: nss).' : 'https:// on a pit name will warn on this platform; run moshcode dns enable for the certificate.'; diff --git a/apps/desktop/launcher/tron-tor-helper b/apps/desktop/launcher/tron-tor-helper index f79e72c..ae7ab0c 100755 --- a/apps/desktop/launcher/tron-tor-helper +++ b/apps/desktop/launcher/tron-tor-helper @@ -54,7 +54,7 @@ BUNDLED_DIR = os.environ.get("TRON_TOR_BIN_DIR", "") PIDFILE = os.environ.get("TRON_TOR_PIDFILE", "") # Bumped whenever the helper protocol/behaviour changes; the launcher kills a # stale helper so the current version always runs. -HELPER_VERSION = "3.4.2" +HELPER_VERSION = "3.4.3" _lock = threading.Lock() _proc = None # the running tor subprocess (or None) _ready = False # True once tor reported Bootstrapped 100% @@ -362,13 +362,11 @@ PIT_NSSDB_EXTRA = os.environ.get("TRON_PIT_NSSDB_EXTRA", "") # What the launcher started: "flatpak" or "native". The Flathub ungoogled-chromium # ignores NSS user trust (verified on bonita: the leaf sat in the very database # strace showed it opening, and Chromium still said "No matching issuer found"), -# but it honours --ignore-certificate-errors-spki-list. So every pin this helper -# accepts is also written to PIT_PINS_FILE, and for a Flatpak engine the launcher -# passes those pins on the command line at the next start. A name first trusted -# mid-session therefore needs one relaunch on a Flatpak engine; the sidebar says so. +# so on that engine the import below cannot take effect. Reported to the sidebar, +# which points at `tron upgrade`: the installer fetches TronBrowser's own engine, +# which honours the store. PIT_ENGINE = os.environ.get("TRON_PIT_ENGINE", "native") PIT_CERT_DIR = os.environ.get("TRON_PIT_CERT_DIR", os.path.expanduser("~/.tronbrowser/pit-certs")) -PIT_PINS_FILE = os.environ.get("TRON_PIT_PINS_FILE", os.path.join(PIT_CERT_DIR, "pins.txt")) _trust_lock = threading.Lock() _trust_seen = {} # name -> (ok, why); retried after a failure only once the pit restarts @@ -450,31 +448,15 @@ def _safe_name(name): return re.sub(r"\.{2,}", ".", re.sub(r"[^a-z0-9.-]", "", name.lower())).strip(".-") -def remember_pin(name, pin): - """Append `name pin` to the pins file the launcher reads (once per pair).""" - try: - os.makedirs(os.path.dirname(PIT_PINS_FILE), mode=0o700, exist_ok=True) - have = set() - if os.path.exists(PIT_PINS_FILE): - with open(PIT_PINS_FILE) as f: - have = {line.strip() for line in f} - line = "%s %s" % (name, pin) - if line not in have: - with open(PIT_PINS_FILE, "a") as f: - f.write(line + "\n") - except OSError as exc: - log("pit: could not record the pin for %s: %s" % (name, exc)) - - def trust_available(): - """Can this machine take a per-name import at all? {available, why}. - `relaunch` is true where the engine only honours pins given at start.""" + """Can this machine take a per-name import at all? {available, why, engine}.""" if platform.system() != "Linux": return {"available": False, "why": "unsupported-platform", "engine": PIT_ENGINE} if not shutil.which("certutil"): return {"available": False, "why": "no-certutil", "engine": PIT_ENGINE} - return {"available": True, "why": "certutil", "engine": PIT_ENGINE, - "relaunch": PIT_ENGINE == "flatpak", "nssdbs": pit_nssdbs()} + if PIT_ENGINE == "flatpak": + return {"available": False, "why": "flatpak-engine", "engine": PIT_ENGINE} + return {"available": True, "why": "certutil", "engine": PIT_ENGINE, "nssdbs": pit_nssdbs()} def _nssdb_groups(): @@ -574,7 +556,6 @@ def _ensure_leaf_trust(name, ip): if is_ca: log("pit: https for %s: certificate is CA:TRUE — refusing to trust a key that could vouch for any name" % name) return False, "ca-true" - remember_pin(name, pin) try: os.makedirs(PIT_CERT_DIR, mode=0o700, exist_ok=True) cert_file = os.path.join(PIT_CERT_DIR, "moshpit-%s.crt" % name) diff --git a/apps/desktop/launcher/tronbrowser b/apps/desktop/launcher/tronbrowser index 7638bbe..c986aa2 100755 --- a/apps/desktop/launcher/tronbrowser +++ b/apps/desktop/launcher/tronbrowser @@ -66,6 +66,17 @@ if [ -n "${TRONBROWSER_BROWSER:-}" ]; then BROWSER="$TRONBROWSER_BROWSER" elif [ -x "$DIR/chrome" ]; then BROWSER="$DIR/chrome" # future: bundled native fork binary +elif [ -x "$DIR/engine/chrome" ] && "$DIR/engine/chrome" --version >/dev/null 2>&1; then + # TronBrowser's own engine: the portable ungoogled-chromium the installer + # fetches next to this shim (install.sh ensure_engine), pinned per release. + # Preferred over anything the distro or Flathub provides because it is the + # one build we have verified end to end — in particular it honours the + # per-user NSS trust store, which the Flathub ungoogled-chromium does not + # (a Moshpit name's certificate sat in the exact database that build opened + # and it still refused it), so https on pit names works here with no flags. + # The --version probe skips it when a shared library is missing, falling + # through to the system candidates rather than failing to start. + BROWSER="$DIR/engine/chrome" else # Ungoogled Chromium ONLY. Never regular Chromium/Chrome, never snap (snap # chromium is regular AND can't be isolated). No fallbacks. @@ -116,10 +127,9 @@ fi if [ -z "$BROWSER" ]; then cat >&2 < prints the switch, or nothing - [ -s "$1" ] || return 0 - _pins="$(awk 'NF >= 2 && $2 !~ /[^A-Za-z0-9+\/=]/ { print $2 }' "$1" | sort -u | paste -sd, -)" - [ -n "$_pins" ] && printf -- '--ignore-certificate-errors-spki-list=%s' "$_pins" -} -PIT_SPKI_FLAG="" -if [ "$BROWSER" = "flatpak" ]; then - PIT_SPKI_FLAG="$(pit_spki_flag "$HOME/.tronbrowser/pit-certs/pins.txt")" -fi -FLAGS="--user-data-dir=$DATA --class=TronBrowser --no-first-run --no-default-browser-check --no-pings --disable-background-networking --disable-breakpad --disable-domain-reliability --disable-sync --disable-features=Translate,OptimizationHints,InterestFeedContentSuggestions,$MV2_KEEP$GPU_OFF_FEATURES --load-extension=$EXT${PIT_SPKI_FLAG:+ $PIT_SPKI_FLAG}" +FLAGS="--user-data-dir=$DATA --class=TronBrowser --no-first-run --no-default-browser-check --no-pings --disable-background-networking --disable-breakpad --disable-domain-reliability --disable-sync --disable-features=Translate,OptimizationHints,InterestFeedContentSuggestions,$MV2_KEEP$GPU_OFF_FEATURES --load-extension=$EXT" # Make the tab-strip audio indicator a clickable mute/unmute control. Upstream # media::kEnableTabMuting is DISABLED_BY_DEFAULT and stock Chrome only turns it diff --git a/apps/web/public/install.sh b/apps/web/public/install.sh index 29290ec..50e25a0 100755 --- a/apps/web/public/install.sh +++ b/apps/web/public/install.sh @@ -478,6 +478,9 @@ download_ungoogled_macos() { # launcher_dir # we install it as part of setup. Skip with TB_NO_BROWSER_INSTALL=1. ensure_browser() { [ "${TB_NO_BROWSER_INSTALL:-0}" = "1" ] && return 0 + # With TronBrowser's own engine on disk there is nothing to install here. + _eldir="$(find "$APP_DIR" -maxdepth 3 -type f -name tronbrowser 2>/dev/null | head -n1)" + if [ -n "$_eldir" ] && [ -x "$(dirname "$_eldir")/engine/chrome" ]; then return 0; fi if [ "$(uname -s)" = "Darwin" ]; then # Where our launcher lives (holds the trust marker for the no-brew path). @@ -690,6 +693,67 @@ download_obscura() { # dest_dir rm -rf "$tmp"; return 1 } +# TronBrowser's own engine: the portable ungoogled-chromium +# (github.com/ungoogled-software/ungoogled-chromium-portablelinux), the same +# build the Docker image runs. ~170 MB, so it is not in the release tarball; +# the installer fetches the pinned release next to the launcher (engine/), like +# Obscura. The launcher prefers it over any distro or Flathub Chromium: it is +# the one build verified end to end, and unlike the Flathub ungoogled-chromium +# it honours the per-user NSS trust store, which is what makes https on Moshpit +# names work with no flags. Linux only (macOS keeps the Homebrew cask). Skip with +# TB_NO_ENGINE_INSTALL=1, pin another release with TRONBROWSER_ENGINE_VERSION. +ENGINE_VERSION="${TRONBROWSER_ENGINE_VERSION:-152.0.7977.82-1}" + +engine_asset() { # -> asset name for this arch, or nothing (Linux only) + [ "$(uname -s)" = "Linux" ] || return 1 + case "$(uname -m)" in + x86_64|amd64) echo "ungoogled-chromium-${ENGINE_VERSION}-x86_64_linux.tar.xz" ;; + aarch64|arm64) echo "ungoogled-chromium-${ENGINE_VERSION}-arm64_linux.tar.xz" ;; + *) return 1 ;; + esac +} + +download_engine() { # dest_dir + dst="$1" + asset="$(engine_asset)" || return 1 + url="https://github.com/ungoogled-software/ungoogled-chromium-portablelinux/releases/download/${ENGINE_VERSION}/${asset}" + tmp="$(mktemp -d)" + info "Downloading TronBrowser's engine, ungoogled-chromium ${ENGINE_VERSION} ($asset, ~170 MB)…" + if fetch "$url" "$tmp/engine.tar.xz" 2>/dev/null && mkdir -p "$tmp/engine" \ + && tar -xJf "$tmp/engine.tar.xz" --strip-components=1 -C "$tmp/engine" 2>/dev/null \ + && [ -x "$tmp/engine/chrome" ]; then + # Swap in whole: a half-written engine dir must never be what the launcher finds. + rm -rf "$dst.new"; mv "$tmp/engine" "$dst.new" + echo "$ENGINE_VERSION" > "$dst.new/VERSION" + rm -rf "$dst.old"; [ -d "$dst" ] && mv "$dst" "$dst.old" + mv "$dst.new" "$dst"; rm -rf "$dst.old" "$tmp" + [ -x "$dst/chrome" ] && return 0 + fi + rm -rf "$tmp"; return 1 +} + +ensure_engine() { + [ "${TB_NO_ENGINE_INSTALL:-0}" = "1" ] && return 0 + [ "$(uname -s)" = "Linux" ] || return 0 + endest="$APP_DIR/engine" + _ldir="$(find "$APP_DIR" -maxdepth 3 -type f -name tronbrowser 2>/dev/null | head -n1)" + [ -n "$_ldir" ] && endest="$(dirname "$_ldir")/engine" + if [ -x "$endest/chrome" ] && [ "$(cat "$endest/VERSION" 2>/dev/null)" = "$ENGINE_VERSION" ]; then return 0; fi + if ! tar --help 2>/dev/null | grep -q -- '-J\|xz'; then + if ! command -v xz >/dev/null 2>&1; then + warn "Couldn't install TronBrowser's engine: 'tar' here cannot read .xz and 'xz' is not installed (Debian/Ubuntu: sudo apt install xz-utils). Falling back to the system or Flatpak Ungoogled Chromium." + return 1 + fi + fi + info "Setting up TronBrowser's engine (ungoogled-chromium ${ENGINE_VERSION})…" + if download_engine "$endest"; then + info "Installed the engine to $endest" + return 0 + fi + warn "Couldn't install TronBrowser's engine; falling back to the system or Flatpak Ungoogled Chromium (https on Moshpit names may warn there). Retry with: curl -fsSL $INSTALL_URL | sh -s -- ensure-engine" + return 1 +} + ensure_obscura() { [ "${TB_NO_OBSCURA_INSTALL:-0}" = "1" ] && return 0 obdest="$APP_DIR/obscura-bin" @@ -843,6 +907,7 @@ StartupNotify=true DESKTOP command -v update-desktop-database >/dev/null 2>&1 && update-desktop-database "$apps_dir" 2>/dev/null || true + ensure_engine || true # TronBrowser's own engine; ensure_browser is the fallback ensure_browser # `|| true` because this script runs under `set -eu`: both of these return 1 # when they could not install their tool, and a missing nice-to-have must not @@ -954,6 +1019,7 @@ do_upgrade() { [ -n "$latest" ] || err "could not resolve the latest release of $REPO" if [ "$current" = "$latest" ] && [ "${TB_FORCE:-0}" != "1" ]; then info "TronBrowser is already up to date ($current)." + ensure_engine || true # and that TronBrowser's own engine is current 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 @@ -1019,6 +1085,7 @@ case "$cmd" in esac ;; remove|uninstall) do_remove ;; ensure-tor) ensure_tor ;; + ensure-engine) ensure_engine ;; ensure-obscura) ensure_obscura ;; ensure-certutil) ensure_certutil ;; version|--version|-v) do_version ;; diff --git a/docs/moshpit-pit-toggle.md b/docs/moshpit-pit-toggle.md index ff47038..19ccbe3 100644 --- a/docs/moshpit-pit-toggle.md +++ b/docs/moshpit-pit-toggle.md @@ -1,6 +1,6 @@ # 🤘 Pit toggle — Moshpit names for one browser session -**Status:** shipped with the AI-sidebar extension + `tron-tor-helper` 3.4.2 +**Status:** shipped with the AI-sidebar extension + `tron-tor-helper` 3.4.3 **Owner:** desktop (`apps/desktop`) **Scope:** resolve Moshpit names in the running browser with one click. Not a replacement for `moshcode dns enable`, which does it for the whole machine. @@ -93,20 +93,22 @@ Flatpak ungoogled-chromium 152 read `data/pki/nssdb` while `.pki/nssdb` existed beside it, and Chromium's net log said "No matching issuer found" until that database held the leaf too. -**Flatpak engines do not honour NSS user trust at all.** Found on bonita with -the Flathub ungoogled-chromium 152: `strace` showed Chromium opening the very +**The Flathub ungoogled-chromium does not honour NSS user trust at all.** Found +on bonita with 152.0.7977.82-1: `strace` showed Chromium opening the very database that held the leaf (peer and anchor trust both tried), single-process and no-sandbox made no difference, and its net log still said "No matching -issuer found". What that build does honour is -`--ignore-certificate-errors-spki-list`, Chromium's own per-key allowance. So -the helper also records every pin it accepts in -`~/.tronbrowser/pit-certs/pins.txt`, and for a Flatpak engine the launcher -passes those pins on the command line at start. Two consequences: a name first -trusted mid-session loads over https after one relaunch (the sidebar says so), -and Chromium shows its one-line "unsupported command-line flag" bar at start on -a machine that has such pins. Native engines take the NSS path and get neither. -The clean way out is a Moshpit CA on the registry side, or shipping the -portable ungoogled-chromium as TronBrowser's own engine, which honours NSS. +issuer found". The same 152 release as a portable build accepts the same +database in every variant tried. So TronBrowser now ships its own engine: +`install.sh ensure_engine` fetches the pinned portable ungoogled-chromium into +`/engine/` next to Tor and Obscura, on install and on +`tron upgrade`, and the launcher prefers `$DIR/engine/chrome` over any system +or Flatpak Chromium (after a `--version` probe, so a machine missing a shared +library falls back rather than failing to start). On that engine the per-name +import above is all that is needed: no flag, no bar, no relaunch. If the pit +is turned on while a Flatpak engine is still running, the sidebar says so and +points at `tron upgrade`. A `--ignore-certificate-errors-spki-list` workaround +was tried and reverted: it works, but Chromium flags it as an unsupported switch +at every start. Linux only for now (Chromium on macOS reads the keychain, which needs an interactive prompt), and it needs `certutil` (Debian/Ubuntu `libnss3-tools`,