feat(server): lazy-bind FIPS peer listener so fips.install doesn't

need an archipelago restart

Previously the server checked `fips0` once at startup; if the
interface wasn't up (pre-onboarding, or post-onboarding before the
user clicked Activate FIPS), the peer listener never bound and stayed
unreachable until the next archipelago restart.

Replaced with a `peer_late_bind_loop` background task: polls every
30s for an fd00::/8 address on `fips0` and binds the listener the
moment one appears. First tick fires immediately so the hot path —
fips0 already up at startup — is still zero-cost. Cancellation
cascades through the same `tokio::sync::watch` channel the main
listener uses.

Side effects:
- main.rs no longer computes peer_addr eagerly; dropped the unused
  param from serve_with_shutdown.
- FipsTransport::is_available already caches the service probe so
  the 30s poll doesn't thrash systemctl.

Covers task #21. Unblocks the first-boot + onboarding flow for
fresh ISO installs on .253.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dorian
2026-04-19 04:21:20 -04:00
co-authored by Claude Opus 4.7
parent bfe2603f69
commit 84943aaa04
2 changed files with 66 additions and 43 deletions
+4 -13
View File
@@ -160,18 +160,9 @@ async fn main() -> Result<()> {
.parse()
.context("Invalid bind address")?;
// If the FIPS daemon has brought up `fips0` with a ULA address, bind a
// second listener there for peer-to-peer traffic. The peer listener
// applies a path whitelist (see server::is_peer_allowed_path) so FIPS
// peers can only reach signed peer endpoints, not internal surfaces.
// No address → no peer listener (fresh install pre-onboarding, fips
// service down, etc.); peers fall through to Tor until next restart.
let peer_addr: Option<SocketAddr> = fips::iface::fips0_ula().map(|ip| {
SocketAddr::new(std::net::IpAddr::V6(ip), fips::dial::PEER_PORT)
});
if let Some(pa) = peer_addr {
info!("FIPS peer listener will bind {}", pa);
}
// The FIPS peer listener is bound lazily by server::serve_with_shutdown
// on a 30s poll of fips0 — so a post-onboarding fips.install brings it
// online without needing an archipelago restart.
// Spawn background update scheduler
let update_data_dir = config.data_dir.clone();
@@ -223,7 +214,7 @@ async fn main() -> Result<()> {
}
};
server.serve_with_shutdown(addr, peer_addr, shutdown).await?;
server.serve_with_shutdown(addr, shutdown).await?;
// Clean shutdown: remove PID marker so next startup doesn't trigger recovery
crash_recovery::remove_pid_marker(&config.data_dir).await;