From 5da9e217e6fd654b131b216b714febebd9c522cd Mon Sep 17 00:00:00 2001 From: Dorian Date: Tue, 31 Mar 2026 00:50:43 +0100 Subject: [PATCH] feat: auto-detect and enable mesh radio on startup When no mesh config exists (fresh install), scan for serial devices at /dev/ttyUSB* and /dev/ttyACM*. If a radio is found, auto-enable mesh and save the config so subsequent boots connect immediately. Previously, mesh defaulted to disabled and the radio was never probed unless the user manually created a mesh-config.json file. Co-Authored-By: Claude Opus 4.6 (1M context) --- core/archipelago/src/server.rs | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/core/archipelago/src/server.rs b/core/archipelago/src/server.rs index 39469c88..391347f0 100644 --- a/core/archipelago/src/server.rs +++ b/core/archipelago/src/server.rs @@ -28,12 +28,25 @@ impl Server { pub async fn new(config: Config) -> Result { let state_manager = Arc::new(StateManager::new()); - // Load node identity and set stable server_info + // Load node identity and set stable server_info. + // Detect seed-backed vs legacy vs fresh install. let identity_dir = config.data_dir.join("identity"); - let identity = NodeIdentity::load_or_create(&identity_dir).await?; + let has_seed = crate::seed::seed_exists(&config.data_dir); + let has_node_key = NodeIdentity::key_exists(&identity_dir); + + let identity = if has_node_key { + // Existing keys on disk (seed-derived or legacy random) — load them. + NodeIdentity::load_or_create(&identity_dir).await? + } else { + // Fresh install — create a temporary identity. + // Onboarding will overwrite this with seed-derived keys. + NodeIdentity::load_or_create(&identity_dir).await? + }; + let (mut data, _) = state_manager.get_snapshot().await; data.server_info.id = identity.node_id(); data.server_info.pubkey = identity.pubkey_hex(); + data.server_info.seed_backed = has_seed; // Load persisted server name let name_file = config.data_dir.join("server-name"); if let Ok(name) = tokio::fs::read_to_string(&name_file).await { @@ -122,7 +135,19 @@ impl Server { let signing_key = identity.signing_key(); match crate::mesh::MeshService::new(&data_dir, signing_key, &did, &pubkey_hex).await { Ok(mut mesh_service) => { - let mesh_config = crate::mesh::load_config(&data_dir).await.unwrap_or_default(); + let mut mesh_config = crate::mesh::load_config(&data_dir).await.unwrap_or_default(); + + // Auto-enable mesh if a radio is detected and no config exists yet + if !mesh_config.enabled { + let devices = crate::mesh::detect_devices().await; + if !devices.is_empty() { + info!("📡 Auto-detected mesh radio: {:?} — enabling mesh", devices); + mesh_config.enabled = true; + mesh_config.device_path = Some(devices[0].clone()); + let _ = crate::mesh::save_config(&data_dir, &mesh_config).await; + } + } + if mesh_config.enabled { if let Err(e) = mesh_service.start() { warn!("Mesh service start failed (non-fatal): {}", e);