diff --git a/core/archipelago/src/server.rs b/core/archipelago/src/server.rs index 742d7c6b..651901e1 100644 --- a/core/archipelago/src/server.rs +++ b/core/archipelago/src/server.rs @@ -385,6 +385,69 @@ impl Server { }); } + // Periodic federation state sync — every 30 min we call + // federation::sync_with_peer on each Trusted peer. Without this + // users had to manually click Sync for `fips_npub`/transport + // badge/state updates to propagate; now it happens in the + // background. Staggers peers with a 5s delay so we don't thunder + // the Tor SOCKS proxy. Sync itself already prefers FIPS. + { + let data_dir = config.data_dir.clone(); + let state = state_manager.clone(); + tokio::spawn(async move { + // First run 60s after boot to let onboarding settle. + tokio::time::sleep(Duration::from_secs(60)).await; + let mut interval = tokio::time::interval(Duration::from_secs(1800)); + interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay); + loop { + interval.tick().await; + let Ok(nodes) = crate::federation::load_nodes(&data_dir).await else { + continue; + }; + if nodes.is_empty() { + continue; + } + let (data, _) = state.get_snapshot().await; + let Ok(local_did) = + crate::identity::did_key_from_pubkey_hex(&data.server_info.pubkey) + else { + continue; + }; + let identity_dir = data_dir.join("identity"); + let Ok(node_identity) = + crate::identity::NodeIdentity::load_or_create(&identity_dir).await + else { + continue; + }; + + for node in &nodes { + if node.trust_level == crate::federation::TrustLevel::Untrusted { + continue; + } + match crate::federation::sync_with_peer( + &data_dir, + node, + &local_did, + |bytes| node_identity.sign(bytes), + ) + .await + { + Ok(_) => debug!( + "Periodic federation sync ok: {}", + node.did.chars().take(20).collect::() + ), + Err(e) => debug!( + "Periodic federation sync with {}: {}", + node.did.chars().take(20).collect::(), + e + ), + } + tokio::time::sleep(Duration::from_secs(5)).await; + } + } + }); + } + // Container health monitoring — auto-restart unhealthy containers // Respects webhook config: skips when disabled or ContainerCrash not subscribed crate::health_monitor::spawn_health_monitor(state_manager.clone(), config.data_dir.clone());