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) <noreply@anthropic.com>
This commit is contained in:
Dorian 2026-03-31 00:50:43 +01:00
parent 9a294fbb94
commit 5da9e217e6

View File

@ -28,12 +28,25 @@ impl Server {
pub async fn new(config: Config) -> Result<Self> {
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);