Skip to content

Commit 14fc63a

Browse files
ralyodioclaude
andcommitted
feat(tor): .onion interstitial "Connecting to Tor…" page (v3.2.14)
Navigating to a .onion with Tor off showed a raw ERR_NAME_NOT_RESOLVED that didn't recover well. Now the onion navigation is swapped for a branded "Connecting to Tor…" page with the live progress bar; once the circuit is up the tab is sent to the onion. Errors (tor not installed / unreachable / failed) show on that page instead of a dead DNS error. Adds onion-connecting.html/.js + web_accessible_resources. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f5615e7 commit 14fc63a

28 files changed

Lines changed: 137 additions & 33 deletions

File tree

‎apps/desktop/extensions/ai-sidebar/background.js‎

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -224,24 +224,39 @@ chrome.runtime.onStartup.addListener(() => {
224224
disableTor().catch(() => {});
225225
});
226226

227-
// Auto-enable Tor when navigating to a .onion site — those only resolve through
228-
// Tor. Turn it on, wait for the circuit, then reload the page now that we route.
227+
// Auto-enable Tor when navigating to a .onion site (they only resolve through
228+
// Tor). Redirect to a "Connecting to Tor…" page so there's no raw DNS error,
229+
// bring Tor up, then send the tab to the onion once it routes.
229230
chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
230231
if (details.frameId !== 0) return; // top-level navigations only
231232
let host;
232233
try { host = new URL(details.url).hostname; } catch (_) { return; }
233234
if (!host.endsWith('.onion')) return;
234235

235236
const { torEnabled } = await chrome.storage.local.get('torEnabled');
236-
if (torEnabled) return; // already routing through Tor
237+
const onion = details.url;
238+
const tabId = details.tabId;
239+
240+
// If Tor is already on, the navigation will resolve through it — leave it.
241+
if (torEnabled) return;
242+
243+
// Swap the failing onion navigation for the connecting page.
244+
const interstitial = chrome.runtime.getURL('onion-connecting.html') + '?u=' + encodeURIComponent(onion);
245+
try { await chrome.tabs.update(tabId, { url: interstitial }); } catch (_) { return; }
237246

238247
const started = await startTorViaHelper();
239-
if (started.error === 'unreachable' || started.error === 'tor-not-installed') return;
240-
const result = await waitForTor(() => {});
248+
if (started.error === 'unreachable' || started.error === 'tor-not-installed') {
249+
chrome.runtime.sendMessage({ type: 'onion-error', reason: started.error }).catch(() => {});
250+
return;
251+
}
252+
const result = await waitForTor((pct) => {
253+
chrome.runtime.sendMessage({ type: 'tor-progress', pct }).catch(() => {});
254+
});
241255
if (result.ready) {
242256
await enableTor(true); // auto: released when the .onion tabs close
243-
// Re-issue the navigation now that traffic routes through Tor.
244-
try { await chrome.tabs.update(details.tabId, { url: details.url }); } catch (_) { /* tab gone */ }
257+
try { await chrome.tabs.update(tabId, { url: onion }); } catch (_) { /* tab gone */ }
258+
} else {
259+
chrome.runtime.sendMessage({ type: 'onion-error', reason: result.error || 'tor-exited' }).catch(() => {});
245260
}
246261
});
247262

‎apps/desktop/extensions/ai-sidebar/manifest.json‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 3,
33
"name": "TronBrowser",
4-
"version": "3.2.13",
4+
"version": "3.2.14",
55
"description": "TronBrowser — privacy-first, AI-native. Branded new tab, private search, CoinPay login, and a bring-your-own-keys AI sidebar.",
66
"icons": {
77
"16": "icons/icon-16.png",
@@ -75,5 +75,16 @@
7575
"options_page": "options.html",
7676
"chrome_url_overrides": {
7777
"newtab": "newtab.html"
78-
}
78+
},
79+
"web_accessible_resources": [
80+
{
81+
"resources": [
82+
"onion-connecting.html",
83+
"onion-connecting.js"
84+
],
85+
"matches": [
86+
"<all_urls>"
87+
]
88+
}
89+
]
7990
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1" />
6+
<title>Connecting to Tor…</title>
7+
<style>
8+
:root { --bg:#05070d; --panel:#0b1020; --line:#1b2540; --cyan:#34e7ff; --fg:#cfe8ff; --muted:#6f86b3; --purple:#7d4698; }
9+
* { box-sizing: border-box; }
10+
html,body { height:100%; margin:0; }
11+
body { background:var(--bg); color:var(--fg); font:15px/1.6 ui-monospace,"SF Mono",Menlo,monospace;
12+
display:flex; align-items:center; justify-content:center; }
13+
.wrap { max-width:520px; padding:32px; text-align:center; }
14+
.onion { font-size:48px; filter:drop-shadow(0 0 14px rgba(125,70,152,.7)); }
15+
h1 { font-size:20px; letter-spacing:1px; margin:14px 0 6px; }
16+
p { color:var(--muted); margin:6px 0; }
17+
.url { color:var(--cyan); word-break:break-all; font-size:12px; margin-top:10px; }
18+
.bar { height:8px; background:var(--line); border-radius:6px; overflow:hidden; margin:22px 0 8px; }
19+
.bar > i { display:block; height:100%; width:5%; background:var(--purple);
20+
box-shadow:0 0 10px rgba(125,70,152,.8); transition:width .4s ease; }
21+
.pct { font-size:12px; color:var(--muted); }
22+
.note { margin-top:18px; font-size:12px; }
23+
.err { color:#ffcf6e; margin-top:16px; }
24+
.hidden { display:none; }
25+
a { color:var(--cyan); }
26+
</style>
27+
</head>
28+
<body>
29+
<div class="wrap">
30+
<div class="onion">🧅</div>
31+
<h1>Connecting to Tor…</h1>
32+
<p>Reaching this <strong>.onion</strong> site securely. The first connection can take a minute while Tor builds a circuit.</p>
33+
<div class="bar"><i id="fill"></i></div>
34+
<div class="pct" id="pct">starting…</div>
35+
<p class="url" id="url"></p>
36+
<p class="err hidden" id="err"></p>
37+
<p class="note">Not Tor-Browser-grade anonymity — for that use the real
38+
<a href="https://www.torproject.org/" target="_blank" rel="noreferrer">Tor Browser</a>.</p>
39+
</div>
40+
<script src="onion-connecting.js"></script>
41+
</body>
42+
</html>
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Interstitial shown while Tor connects to reach a .onion site. The background
2+
// pushes `tor-progress` while bootstrapping, then navigates this tab to the
3+
// onion once routed (or sends `onion-error` if it couldn't connect).
4+
const params = new URLSearchParams(location.search);
5+
const target = params.get('u') || '';
6+
document.getElementById('url').textContent = target;
7+
8+
const fill = document.getElementById('fill');
9+
const pct = document.getElementById('pct');
10+
11+
function setProgress(p) {
12+
const v = Math.max(5, Math.min(100, p));
13+
fill.style.width = v + '%';
14+
pct.textContent = Math.round(p) + '%';
15+
}
16+
17+
function showError(msg) {
18+
const e = document.getElementById('err');
19+
e.textContent = msg;
20+
e.classList.remove('hidden');
21+
pct.textContent = '';
22+
}
23+
24+
chrome.runtime.onMessage.addListener((m) => {
25+
if (!m) return;
26+
if (m.type === 'tor-progress') setProgress(m.pct);
27+
if (m.type === 'onion-error') {
28+
if (m.reason === 'tor-not-installed') {
29+
showError('Tor isn’t installed yet. Run `tron tor` once, then reload this page.');
30+
} else if (m.reason === 'unreachable') {
31+
showError('Couldn’t reach the Tor helper. Restart TronBrowser and try again.');
32+
} else {
33+
showError('Couldn’t connect to Tor. Try reloading this page in a moment.');
34+
}
35+
}
36+
});

‎apps/desktop/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tronbrowser/desktop",
3-
"version": "3.2.13",
3+
"version": "3.2.14",
44
"private": true,
55
"description": "Desktop shell for the TronBrowser Chromium fork",
66
"type": "module",

‎apps/docs/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tronbrowser/docs",
3-
"version": "3.2.13",
3+
"version": "3.2.14",
44
"private": true,
55
"description": "Documentation site",
66
"type": "module",

‎apps/extensions/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tronbrowser/extensions",
3-
"version": "3.2.13",
3+
"version": "3.2.14",
44
"private": true,
55
"description": "TronBrowser extension store — pay $1, list your MV3 extension (tronbrowser.dev/store)",
66
"type": "module",

‎apps/mobile/app.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"slug": "tronbrowserdev",
55
"owner": "profullstack",
66
"scheme": "tronbrowser",
7-
"version": "3.2.13",
7+
"version": "3.2.14",
88
"orientation": "portrait",
99
"userInterfaceStyle": "dark",
1010
"platforms": [

‎apps/mobile/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tronbrowser/mobile",
3-
"version": "3.2.13",
3+
"version": "3.2.14",
44
"private": true,
55
"description": "TronBrowser mobile (Expo / React Native) — Phase 2",
66
"type": "module",

‎apps/web/package.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@tronbrowser/web",
3-
"version": "3.2.13",
3+
"version": "3.2.14",
44
"private": true,
55
"description": "TronBrowser marketing site + web dashboard",
66
"type": "module",

0 commit comments

Comments
 (0)