Status: design + initial implementation (v0.3.0)
Owner: desktop (apps/desktop)
Scope: convenience Tor routing — not Tor-Browser-grade anonymity.
- In-browser toggle (🧅 Tor in the AI sidebar) — flips your current
session through Tor with one click, no relaunch and no second window. Best
for "make this session go through Tor." Needs a running Tor daemon
(
tron tor). See In-browser toggle. tron --tordedicated session — launches a separate, wiped Tor profile and starts/stops its own daemon for you. Best for a clean, isolated Tor window. See What this is.
Both are convenience routing. Neither is Tor-Browser-grade anonymity — see What this is NOT.
The existing AI-sidebar extension has a 🧅 Tor button in its header. Clicking
it routes the live browser session through the local Tor SOCKS5 proxy using
chrome.proxy (plus a WebRTC-leak guard via chrome.privacy) — no relaunch, no
separate instance. A TOR badge appears on the toolbar icon while it's on.
One click — no terminal, no setup.
An extension can re-route traffic but cannot launch a process, so it can't
start the tor daemon itself. To make the toggle "just work" without imposing
always-on Tor on everyone, the launcher runs a tiny control helper
(launcher/tron-tor-helper, loopback-only on 127.0.0.1:9061) at every browser
launch — both the desktop app entry and the tron CLI go through the same shim,
so both get it. The helper makes no network connection until asked: nobody
connects to Tor unless they flip the toggle on. (The same helper backs the
🤘 Pit toggle — a loopback resolver for Moshpit names, see
moshpit-pit-toggle.md. The two are exclusive: turning
Tor on takes the pit down, because the pit's PAC asks the system resolver about
every host, which under Tor would leak lookups outside it.)
Flow when you click 🧅 Tor:
- Background
POST /start→ the helper spawnstor, waits for it to bootstrap. - Background sets the SOCKS proxy + WebRTC guard.
- It verifies real Tor routing via
check.torproject.org; if that fails it reverts (never leaves a dead proxy) and shows a plain-language reason (e.g. "Tor isn't installed on this computer yet"). - Toggling off clears the proxy and
POST /stopstops the daemon.
The toggle does not use a separate profile, so it does not isolate clearnet
cookies — use tron --tor if you want that isolation. tron tor (start a
standalone daemon) still exists as a manual fallback if the helper can't run
(e.g. no python3).
Note: the helper can only start Tor if a
torbinary is present (bundled next to the launcher, or onPATH). Bundlingtoracross thedistribution/targets is still pending — until then the toggle needstorinstalled, and says so plainly if it isn't.
Implementation: launcher/tron-tor-helper (Python) + launcher/tronbrowser
(starts it) + extensions/ai-sidebar/{background.js,sidepanel.js} +
manifest.json (proxy, privacy perms); proxy config mirrors the unit-tested
apps/desktop/src/tor-proxy.ts.
A --tor mode for TronBrowser that:
- Starts a local Tor daemon (SOCKS5 on
127.0.0.1:9050). - Launches the browser with all traffic routed through it (so
.onionsites resolve and your IP is hidden from sites/ISP at the network layer). - Uses a separate, wiped profile for the Tor session — so clearnet cookies never ride over Tor, and nothing lasts on disk after you close it.
tron --tor # open a Tor window (new-tab page)
tron --tor http://example.onion # open an onion site through Tor--tor requires the Tor daemon (tor) to be installed (or bundled — see
Distribution); the launcher prints
install instructions if it's missing.
Profile note: the launcher shim uses a dedicated, wiped profile dir rather than
--incognito, because command-line--load-extensionextensions don't run in incognito windows (which would break the de-googled new-tab + AI sidebar). The library layer (launchWithTor) still uses--incognitofor the future native fork, where extensions can be allowed in incognito. Both deliver "no lasting local trace + isolated from your clearnet session."
Tor Browser is much safer. This is just a convenience feature in tron.
This gives you network-level privacy: it hides where you're coming from and lets you reach hidden services. It does not make you anonymous against a determined adversary, because Chromium still exposes a large browser fingerprinting surface (canvas/font/WebGL/timing/etc.) that we do not harden the way Tor Browser does.
If your safety, freedom, or livelihood depends on not being identified, use the real Tor Browser — not this. TronBrowser's
--toris for convenience and curiosity (reach.onion, hide your IP, leave no local trace) in your everyday browser. Any UI surfacing it must say so plainly.
See why-not-tor-browser below for the reasoning.
| Adversary / goal | Covered? | Notes |
|---|---|---|
| Site learns your real IP | ✅ | Traffic exits via the Tor network. .onion never sees your IP by design. |
| ISP sees which sites you visit | ✅ | ISP sees "connected to Tor," not the destination. |
Reach .onion hidden services |
✅ | Only resolvable through Tor; works once routed. |
| Local traces on this machine | ✅ | Dedicated profile, wiped on launch — nothing persists after close. |
| Clearnet ↔ Tor session linkage (shared cookies) | ✅ | Separate profile, so clearnet cookies/history never ride over Tor. |
| DNS leak (name resolved outside Tor) | ✅ | DNS is proxied through the SOCKS5 server (remote DNS), never resolved locally. |
| WebRTC IP leak (UDP around the proxy) | ✅ | --force-webrtc-ip-handling-policy=disable_non_proxied_udp. |
| Browser fingerprinting / cross-site correlation | ❌ | Chromium surface not hardened. Use Tor Browser. |
| Malicious JS / browser exploits de-anonymizing you | ❌ | No security-slider / NoScript equivalent. Use Tor Browser. |
Two layers, mirroring the existing split (pure, testable flag data vs. isolated desktop process glue):
Two new LaunchOptions:
tor?: boolean— when true, appendsTOR_FLAGS.incognito?: boolean— when true, appends--incognito. Tor implies incognito unless the caller explicitly passesincognito: false(escape hatch; discouraged).
TOR_FLAGS:
--proxy-server=socks5://127.0.0.1:9050
--proxy-bypass-list=<-loopback> # don't bypass anything; force all through Tor
--force-webrtc-ip-handling-policy=disable_non_proxied_udp
DNS note: Chromium's SOCKS5 proxy performs remote DNS resolution (it hands
the hostname to the proxy), so names — including .onion — are resolved inside
Tor, not on the local resolver. No --host-resolver-rules hack needed.
The existing FORBIDDEN_FLAG_SUBSTRINGS audit still runs over the full list.
Pure, unit-tested helpers:
resolveTorBinary(opts)— prefer a bundledtorshipped next to the browser binary, else fall back to atoronPATH. Per-platform name (tor/tor.exe), mirroringbinary.ts.buildTorArgs(opts)— builds thetorCLI args (--SocksPort,--DataDirectory, optional--ControlPort), pure data.parseBootstrapProgress(line)— parsesBootstrapped NN% (...)log lines into a0..100number (ornull), so we can show progress and know when Tor is ready.
Process glue (thin, not heavily unit-tested):
class TorDaemon—start()spawnstor, watches stdout forBootstrapped 100%, resolves when ready (with a timeout), andstop()kills it. The browser is only launched after bootstrap completes, so the first request never races a half-open circuit.
launch() gains an optional Tor path: when config.tor is set, start a
TorDaemon, await bootstrap, then spawn the browser with Tor flags, and tear the
daemon down when the browser exits.
The real runtime today is the POSIX sh launcher shim, not the TS library
(which targets the future native fork). It implements --tor directly:
- Parse
--torout of the args (rest pass through as URLs). - Resolve
tor: a bundled$DIR/torfirst, else one onPATH; clear install message if missing. - Point the browser at a separate, wiped profile (
<data>-tor) so the Tor session is isolated from the clearnet profile; the daemon's own state lives under<data>/tor. - Start
tor --SocksPort 127.0.0.1:9050 --DataDirectory …, poll its log forBootstrapped 100%(60s timeout, bail if it dies), then add the proxy flags. - Do not
execin Tor mode — keep the shell alive so anEXIT/INT/TERMtrap stops the daemon when the browser closes. (macOSopendetaches, so there the trap is disarmed and the user is told the Tor pid to kill.)
Usage:
tron --tor # Tor window, de-googled new-tab
tron --tor http://example.onion # open an onion site
TRONBROWSER_TOR_PORT=9150 tron --tor # custom SOCKS portTo make --tor work out of the box, each target in distribution/ must ship or
depend on a tor binary:
- deb/rpm:
Depends/Requires: tor. - AppImage / Flatpak / Snap / macOS / Windows: bundle a static
tornext to the browser binary (resolved byresolveTorBinary). - Homebrew/AUR/Nix/etc.: declare
toras a dependency.
Until then, --tor requires a system tor on PATH and surfaces a clear error
if missing.
Tor Browser is a hardened Firefox fork plus years of anti-fingerprinting work (letterboxing, canvas/font/WebGL/timing resistance, first-party isolation, disabled APIs) maintained by a dedicated team. Replicating that on Chromium is a months-long, never-finished effort, and we'd still be weaker than the real thing. The Tor Project explicitly discourages Tor-over-Chromium for anonymity. So we ship the genuinely-useful network layer and are honest about the ceiling.
- Tor toggle + bootstrap/circuit indicator in the AI sidebar/settings UI.
- Bundle
toracrossdistribution/targets (see above). - "New circuit for this site" action.
- Detect and warn when the user navigates to a clearnet site that's likely to fingerprint them.