From 6b78bd692de086b6c589d3ceecacc44d8f8873a1 Mon Sep 17 00:00:00 2001 From: Dorian Date: Sun, 19 Apr 2026 18:09:46 -0400 Subject: [PATCH] fix(fips,kiosk): auto-activate FIPS at onboarding end + 5-min kiosk wait MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. FIPS auto-activate at server startup only fires if fips_key already exists on disk, which on a fresh install is never true until AFTER onboarding. By the time the user completes seed-generate/restore, archipelago has been running for minutes and the startup task has long since exited. User still had to hit Activate. Fix: call spawn_post_onboarding_fips_activate() from the tail of handle_seed_generate and handle_seed_restore — the moment the fips_key materialises, a detached task runs `fips::config::install` + `archipelago-fips.service activate`. Logged only, never blocks the onboarding RPC. 2. Kiosk health-poll window was 30 × 2s (configs/ copy was 60 × 2s but unused — the heredoc in build-auto-installer-iso.sh is what actually lands on disk). On .198's slower hardware archipelago /health wasn't ready within 60s, so Chromium launched against a not-yet-running backend → blank window until manual reboot. Bumped to 150 × 2s (5 min) + TimeoutStartSec=360. .253 was already well within the window; this protects the slower box too. Standalone configs/archipelago-kiosk.service updated in lockstep so the two copies don't drift. Co-Authored-By: Claude Opus 4.7 (1M context) --- core/archipelago/src/api/rpc/seed_rpc.rs | 39 +++++++++++++++++++ image-recipe/build-auto-installer-iso.sh | 15 +++++-- .../configs/archipelago-kiosk.service | 16 ++++---- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/core/archipelago/src/api/rpc/seed_rpc.rs b/core/archipelago/src/api/rpc/seed_rpc.rs index 8f8934cb..65caa925 100644 --- a/core/archipelago/src/api/rpc/seed_rpc.rs +++ b/core/archipelago/src/api/rpc/seed_rpc.rs @@ -26,6 +26,36 @@ impl Drop for OnboardingMnemonicState { const MNEMONIC_TTL: std::time::Duration = std::time::Duration::from_secs(600); // 10 minutes +/// Best-effort: install fips.yaml + start archipelago-fips.service after the +/// seed onboarding has written the fips_key to disk. Runs in a detached task +/// so the user-facing RPC returns immediately — the systemctl calls can take +/// a few seconds the first time on slow hardware. Any failure is logged but +/// does not break onboarding; the user can still hit fips.install manually +/// from the dashboard as an escape hatch. +fn spawn_post_onboarding_fips_activate(data_dir: std::path::PathBuf) { + tokio::spawn(async move { + let identity_dir = data_dir.join("identity"); + if !crate::identity::fips_key_exists(&identity_dir) { + return; + } + // Touch load_fips_keys first so any legacy raw-byte file is migrated + // to bech32 before we copy it into /etc/fips/. + if let Err(e) = crate::identity::load_fips_keys(&identity_dir).await { + tracing::warn!("post-onboarding fips key load/migrate failed: {}", e); + return; + } + if let Err(e) = crate::fips::config::install(&identity_dir).await { + tracing::warn!("post-onboarding fips config install failed: {}", e); + return; + } + if let Err(e) = crate::fips::service::activate(crate::fips::SERVICE_UNIT).await { + tracing::warn!("post-onboarding archipelago-fips activate failed: {}", e); + return; + } + tracing::info!("archipelago-fips auto-activated post-onboarding"); + }); +} + impl RpcHandler { /// Generate a new 24-word BIP-39 mnemonic, derive and persist node keys. /// Returns the words for the user to write down. @@ -54,6 +84,11 @@ impl RpcHandler { // Initialize identity index at 0. crate::seed::save_identity_index(&self.config.data_dir, 0).await?; + // fips_key is now on disk — auto-activate archipelago-fips so the + // user doesn't have to hit an "Activate" button. Detached task; + // the onboarding RPC returns immediately. + spawn_post_onboarding_fips_activate(self.config.data_dir.clone()); + let words: Vec<&str> = mnemonic.words().collect(); // Hold mnemonic in memory for the verify step. @@ -193,6 +228,10 @@ impl RpcHandler { let did = crate::identity::did_key_from_pubkey_hex(&pubkey_hex)?; let nostr_npub = nostr_keys.public_key().to_bech32().unwrap_or_default(); + // Same as seed.generate: the key is materialised, kick the FIPS + // service up without user interaction. + spawn_post_onboarding_fips_activate(self.config.data_dir.clone()); + Ok(serde_json::json!({ "did": did, "nostr_npub": nostr_npub, diff --git a/image-recipe/build-auto-installer-iso.sh b/image-recipe/build-auto-installer-iso.sh index 3fe0f258..9053edf1 100755 --- a/image-recipe/build-auto-installer-iso.sh +++ b/image-recipe/build-auto-installer-iso.sh @@ -2648,15 +2648,22 @@ chmod +x /mnt/target/usr/local/bin/archipelago-kiosk-launcher cat > /mnt/target/etc/systemd/system/archipelago-kiosk.service <<'KIOSKSVC' [Unit] Description=Archipelago Kiosk (X11 + Chromium) -After=archipelago.service -Wants=archipelago.service +After=archipelago.service systemd-user-sessions.service network-online.target +Wants=archipelago.service network-online.target ConditionPathExists=/usr/local/bin/archipelago-kiosk-launcher +Conflicts=getty@tty1.service [Service] Type=simple -ExecStartPre=/bin/bash -c 'for i in $(seq 1 30); do curl -sf http://localhost/health >/dev/null 2>&1 && exit 0; sleep 2; done; exit 0' +# First-boot health-poll window is 300s (150 × 2s). Slow hardware +# (e.g. the atom-class box at .198) was blowing past the old 60s / +# 120s window, so Chromium launched against a not-yet-ready backend +# and showed a blank window that only recovered on reboot. At 300s +# even the unbundled-FileBrowser-pull + archipelago state sync + frontend +# settle fits with headroom. TimeoutStartSec is bumped in lockstep. +ExecStartPre=/bin/bash -c 'for i in $(seq 1 150); do curl -sf http://localhost/health >/dev/null 2>&1 && exit 0; sleep 2; done; exit 0' ExecStart=/usr/local/bin/archipelago-kiosk-launcher -TimeoutStartSec=90 +TimeoutStartSec=360 Restart=always RestartSec=5 diff --git a/image-recipe/configs/archipelago-kiosk.service b/image-recipe/configs/archipelago-kiosk.service index 59492658..6047fc6e 100644 --- a/image-recipe/configs/archipelago-kiosk.service +++ b/image-recipe/configs/archipelago-kiosk.service @@ -7,14 +7,16 @@ Conflicts=getty@tty1.service [Service] Type=simple -# Wait up to 120s for archipelago to serve /health. On first boot it -# can take longer than 30s — the backend initialises state, unbundled -# ISO pulls FileBrowser, and the frontend dist has to settle. The -# previous 30s cap was firing Chromium at a not-yet-ready backend and -# the resulting blank window only recovered on reboot. -ExecStartPre=/bin/bash -c 'for i in $(seq 1 60); do curl -sf http://localhost/health >/dev/null 2>&1 && break; sleep 2; done' +# Wait up to 5 min for archipelago to serve /health. On slow hardware +# first-boot is dominated by the FileBrowser pull (unbundled ISO), +# initial archipelago state sync, and frontend settle — .198 took +# longer than 120s and chromium launched against an empty backend, +# producing a white window that only recovered on reboot. 300s gives +# slow-but-functional hardware enough headroom; TimeoutStartSec is +# bumped in lockstep so systemd doesn't kill us mid-wait. +ExecStartPre=/bin/bash -c 'for i in $(seq 1 150); do curl -sf http://localhost/health >/dev/null 2>&1 && break; sleep 2; done' ExecStart=/usr/local/bin/archipelago-kiosk-launcher -TimeoutStartSec=180 +TimeoutStartSec=360 Restart=always RestartSec=5