fix(ui): Tor status from the live probe, not from a leftover onion address
Demo images / Build & push demo images (push) Successful in 3m14s

Home's Network card computed "Connected" from server-info's tor-address —
a string read from the hidden-service hostname file, which persists on
disk however dead the daemon is. Three fleet nodes ran with Tor down for
days while their dashboards said Connected; that indicator is why nobody
noticed. Server.vue's Tor label had the same flaw one step removed,
inferring "running" from services having onion_address values while the
correct signal (torDaemonRunning, backed by a real socket probe) sat
computed and unused twelve lines above.

Both now read liveness signals only: Home reads the new tor-running field
(server-side connect to 127.0.0.1:9050), Server.vue uses
torDaemonRunning. The address remains displayed as what it is — an
address — never as proof of life.

Verified: vue-tsc clean; rpc-client suite 79/79.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
archipelago
2026-08-09 07:51:13 -04:00
co-authored by Claude Fable 5
parent aaa0651c8d
commit e34937f497
3 changed files with 17 additions and 5 deletions
+3
View File
@@ -25,6 +25,9 @@ export interface ServerInfo {
'status-info': StatusInfo
'lan-address': string | null
'tor-address': string | null
/** Live probe of the Tor daemon. NOT the same as `tor-address`, which is read
* from the hidden-service hostname file and outlives a dead daemon. */
'tor-running': boolean
'node-address'?: string
unread: number
'wifi-ssids': string[]
+7 -4
View File
@@ -424,10 +424,13 @@ function marketplaceDescription(app: MarketplaceApp) {
}
// Network card data
const torConnected = computed(() => {
const torAddr = store.data?.['server-info']?.['tor-address']
return !!torAddr && torAddr.length > 0
})
// Tor liveness, NOT "was an onion ever provisioned". This used to return
// `!!server-info['tor-address']`, but that address is read from the
// hidden-service hostname file on disk and outlives the daemon — so this card
// showed "Connected" on three fleet nodes whose Tor had been dead for days
// (2026-08-09), which is precisely why nobody noticed. `tor-running` is a live
// probe of 127.0.0.1:9050 performed server-side.
const torConnected = computed(() => store.data?.['server-info']?.['tor-running'] === true)
const vpnConnected = computed(() => homeStatus.vpnStatus.connected === true || (!!packages.value['tailscale'] && packages.value['tailscale'].state === PackageState.Running))
const vpnDotClass = computed(() => {
if (vpnConnected.value) return 'bg-orange-400'
+7 -1
View File
@@ -995,8 +995,14 @@ async function checkTorStatus() {
checkingTor.value = true; torStatusLabel.value = 'checking'
try {
await torServicesRes.refresh()
// Report the DAEMON, not whether onion addresses exist. A .onion address is
// a config artifact — the hidden-service hostname file stays on disk when
// Tor is dead — so `some(s => s.onion_address)` reported "running" on three
// fleet nodes whose Tor had been down for days (2026-08-09). The backend
// already returns the truth: tor_running comes from an actual connect to
// 127.0.0.1:9050.
if (torServicesRes.error.value) torStatusLabel.value = 'stopped'
else torStatusLabel.value = torServices.value.some(s => s.onion_address) ? 'running' : 'stopped'
else torStatusLabel.value = torDaemonRunning.value ? 'running' : 'stopped'
} finally { checkingTor.value = false }
}