fix(tor): heal torrc at boot, and report liveness instead of leftovers
Three of five reachable fleet nodes had Tor completely dead — Home said "Connected" on all of them — and shipping the generator fix alone would have repaired none of them. Two additions close that loop. heal_on_boot (wired into the bootstrap repair chain): regenerate torrc from current config with the fixed generator, and apply-and-restart ONLY if the live file drifted or Tor is not answering on 9050. A healthy node is left untouched. Without this, regenerate_torrc runs only from the Tor RPC handlers and package install, so a node carrying a poisoned torrc keeps it until someone happens to toggle a Tor setting — and worse, the still-running OLD binary re-poisons on any such toggle: observed live on the dev node at 07:20, when the running daemon rewrote torrc with the unbindable gateway line hours after it had been hand-fixed. The heal makes the fix self-applying on every restart, i.e. the OTA itself. ServerInfo gains tor-running, populated by a real connect to 127.0.0.1:9050 each state refresh. tor-address is read from the hidden-service hostname file, which OUTLIVES a dead daemon — it is a configuration artifact, and the dashboard treating it as liveness is precisely why three dead nodes showed "Connected" for days. Liveness now comes only from the probe; the address stays a separate fact. The heal reports the truth: it re-probes after restarting and warns if Tor still is not answering, rather than assuming success. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
e5a8fce198
commit
aaa0651c8d
@@ -166,6 +166,11 @@ pub async fn ensure_doctor_installed() {
|
||||
Ok(false) => debug!("/opt/archipelago/apps already populated (or no installer copy)"),
|
||||
Err(e) => warn!("Apps dir repair failed (non-fatal): {:#}", e),
|
||||
}
|
||||
match run_tor_torrc_repair().await {
|
||||
Ok(true) => info!("Tor healed at boot (torrc rebuilt and/or daemon restarted)"),
|
||||
Ok(false) => debug!("Tor healthy and torrc in sync — no heal needed"),
|
||||
Err(e) => warn!("Tor boot heal failed (non-fatal): {:#}", e),
|
||||
}
|
||||
match run_polkit_networkmanager_repair().await {
|
||||
Ok(true) => info!(
|
||||
"Installed NetworkManager polkit rule for the archipelago user — Wi-Fi setup enabled"
|
||||
@@ -620,6 +625,33 @@ exit 2
|
||||
}
|
||||
}
|
||||
|
||||
/// Repair Tor at boot so a torrc-generation fix actually reaches the nodes it
|
||||
/// was written for.
|
||||
///
|
||||
/// `regenerate_torrc` only ran from the Tor RPC handlers and package install,
|
||||
/// so a node carrying a bad torrc kept it indefinitely: shipping a fixed binary
|
||||
/// changed nothing until somebody happened to install an app or toggle a Tor
|
||||
/// setting. On 2026-08-09 two of the three reachable nodes had Tor completely
|
||||
/// down from an unbindable `SocksPort <archy-net gateway>` line, one of them
|
||||
/// unnoticed, while the dashboard reported "connected".
|
||||
///
|
||||
/// Non-fatal and conservative: it only restarts Tor when the regenerated torrc
|
||||
/// differs from the live one, or Tor is not answering on 9050.
|
||||
async fn run_tor_torrc_repair() -> Result<bool> {
|
||||
// Same location the RPC handlers use (Config::data_dir); bootstrap runs
|
||||
// before the server owns a Config, and this path is fixed on real installs.
|
||||
let data_dir = Path::new("/var/lib/archipelago");
|
||||
if !data_dir.exists() {
|
||||
debug!("No {} — skipping Tor boot heal", data_dir.display());
|
||||
return Ok(false);
|
||||
}
|
||||
if !Path::new("/etc/tor/torrc").exists() {
|
||||
debug!("No /etc/tor/torrc — Tor not installed here, skipping boot heal");
|
||||
return Ok(false);
|
||||
}
|
||||
crate::api::rpc::tor::heal_on_boot(data_dir).await
|
||||
}
|
||||
|
||||
async fn run_bitcoin_rpc_repair() -> Result<bool> {
|
||||
// bitcoind is launched with -conf=/tmp/rpc.conf and never reads a
|
||||
// datadir bitcoin.conf (apps/bitcoin-core & bitcoin-knots manifest.yml,
|
||||
|
||||
Reference in New Issue
Block a user