From 974fce58705fffb2ffe92e3e000a2f27ccf29f42 Mon Sep 17 00:00:00 2001 From: Dorian Date: Wed, 22 Apr 2026 03:52:22 -0400 Subject: [PATCH] release(v1.7.32-alpha): fix frontend tarball layout + mDNS shutdown hang MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - HOTFIX: v1.7.31-alpha's frontend tarball was packaged with a `neode-ui/` top-level directory instead of the flat layout v1.7.30 and earlier used. Nodes that applied v1.7.31 ended up with `/opt/archipelago/web-ui/neode-ui/index.html` instead of `/opt/archipelago/web-ui/index.html`, and nginx returned 403/500. v1.7.32's tarball is built with `tar -C web/dist/neode-ui .` so files land directly at web-ui root. Broken nodes auto-heal on this update (web-ui dir is replaced). - transport/lan.rs: add Drop impl that calls ServiceDaemon::shutdown() on the mdns_sd daemon. Without this the OS thread it spawns, plus the blocking `receiver.recv()` task, keep the tokio runtime alive past SIGTERM — long enough for systemd's TimeoutStopSec to SIGKILL the service and mark it Failed. Was visible on every update: "shut down cleanly" logged, then 15s later systemd forcibly kills. - main.rs: after logging "Archipelago shut down cleanly", call `std::process::exit(0)` explicitly. Belt-and-suspenders against any future non-daemon thread creeping in (reqwest resolver pool, etc.) and causing the same SIGKILL regression. Co-Authored-By: Claude Opus 4.7 (1M context) --- core/Cargo.lock | 2 +- core/archipelago/Cargo.toml | 2 +- core/archipelago/src/main.rs | 11 ++++++++++- core/archipelago/src/transport/lan.rs | 12 ++++++++++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/core/Cargo.lock b/core/Cargo.lock index 6514b5c5..1c648364 100644 --- a/core/Cargo.lock +++ b/core/Cargo.lock @@ -80,7 +80,7 @@ checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "archipelago" -version = "1.7.31-alpha" +version = "1.7.32-alpha" dependencies = [ "anyhow", "archipelago-container", diff --git a/core/archipelago/Cargo.toml b/core/archipelago/Cargo.toml index 37f93996..edc724e3 100644 --- a/core/archipelago/Cargo.toml +++ b/core/archipelago/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "archipelago" -version = "1.7.31-alpha" +version = "1.7.32-alpha" edition = "2021" description = "Archipelago Bitcoin Node OS - Native backend" authors = ["Archipelago Team"] diff --git a/core/archipelago/src/main.rs b/core/archipelago/src/main.rs index 97495fba..21f73be7 100644 --- a/core/archipelago/src/main.rs +++ b/core/archipelago/src/main.rs @@ -229,5 +229,14 @@ async fn main() -> Result<()> { crash_recovery::remove_pid_marker(&config.data_dir).await; info!("Archipelago shut down cleanly"); - Ok(()) + + // Hard-exit after logging. All business state is persisted by now + // (connections drained, PID marker removed, disk flushes done via + // tokio::fs awaits). Letting tokio try to drop the runtime instead + // can stall for 15s+ on non-daemon OS threads we don't directly + // own (mdns_sd daemon, reqwest resolver pool, etc.) — long enough + // for systemd's TimeoutStopSec to SIGKILL us and mark the service + // Failed, which makes an otherwise-successful update look like a + // crash in `systemctl status`. + std::process::exit(0); } diff --git a/core/archipelago/src/transport/lan.rs b/core/archipelago/src/transport/lan.rs index 73265839..610948e7 100644 --- a/core/archipelago/src/transport/lan.rs +++ b/core/archipelago/src/transport/lan.rs @@ -160,6 +160,18 @@ impl LanTransport { } } +impl Drop for LanTransport { + // The mdns_sd daemon runs on its own OS thread and the browse + // listener task blocks on a sync channel. Without this call both + // keep the process alive past SIGTERM, long enough for systemd to + // SIGKILL us — which makes a normal update look like a crash. + fn drop(&mut self) { + if let Some(daemon) = self.daemon.take() { + let _ = daemon.shutdown(); + } + } +} + impl NodeTransport for LanTransport { fn kind(&self) -> TransportKind { TransportKind::Lan