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`,