diff --git a/crates/wzp-relay/src/config.rs b/crates/wzp-relay/src/config.rs index 01d9e14..a771b42 100644 --- a/crates/wzp-relay/src/config.rs +++ b/crates/wzp-relay/src/config.rs @@ -3,8 +3,24 @@ use serde::{Deserialize, Serialize}; use std::net::SocketAddr; -/// Configuration for the relay daemon. +/// A federated peer relay. #[derive(Clone, Debug, Serialize, Deserialize)] +pub struct PeerConfig { + /// Address of the peer relay (e.g., "193.180.213.68:4433"). + pub url: String, + /// Expected TLS certificate fingerprint (hex, with colons). + pub fingerprint: String, + /// Optional human-readable label. + #[serde(default)] + pub label: Option, +} + +/// Configuration for the relay daemon. +/// +/// All fields have defaults, so a minimal TOML file only needs the +/// fields you want to override (e.g., just `[[peers]]`). +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(default)] pub struct RelayConfig { /// Address to listen on for incoming connections (client-facing). pub listen_addr: SocketAddr, @@ -44,6 +60,9 @@ pub struct RelayConfig { pub ws_port: Option, /// Directory to serve static files from (HTML/JS/WASM for web clients). pub static_dir: Option, + /// Federation peer relays. + #[serde(default)] + pub peers: Vec, } impl Default for RelayConfig { @@ -62,6 +81,14 @@ impl Default for RelayConfig { trunking_enabled: false, ws_port: None, static_dir: None, + peers: Vec::new(), } } } + +/// Load relay configuration from a TOML file. +pub fn load_config(path: &str) -> Result { + let content = std::fs::read_to_string(path)?; + let config: RelayConfig = toml::from_str(&content)?; + Ok(config) +} diff --git a/crates/wzp-relay/src/main.rs b/crates/wzp-relay/src/main.rs index 3388754..8f2c6b7 100644 --- a/crates/wzp-relay/src/main.rs +++ b/crates/wzp-relay/src/main.rs @@ -24,11 +24,34 @@ use wzp_relay::room::{self, RoomManager}; use wzp_relay::session_mgr::SessionManager; fn parse_args() -> RelayConfig { - let mut config = RelayConfig::default(); let args: Vec = std::env::args().collect(); + + // Check for --config first to use as base + let mut config_file = None; + let mut i = 1; + while i < args.len() { + if args[i] == "--config" { + i += 1; + config_file = args.get(i).cloned(); + } + i += 1; + } + + let mut config = if let Some(ref path) = config_file { + wzp_relay::config::load_config(path) + .unwrap_or_else(|e| { + eprintln!("failed to load config from {path}: {e}"); + std::process::exit(1); + }) + } else { + RelayConfig::default() + }; + + // CLI flags override config file values let mut i = 1; while i < args.len() { match args[i].as_str() { + "--config" => { i += 1; } // already handled "--listen" => { i += 1; config.listen_addr = args.get(i).expect("--listen requires an address") @@ -90,9 +113,10 @@ fn parse_args() -> RelayConfig { std::process::exit(0); } "--help" | "-h" => { - eprintln!("Usage: wzp-relay [--listen ] [--remote ] [--auth-url ] [--metrics-port ] [--probe ]... [--probe-mesh] [--mesh-status]"); + eprintln!("Usage: wzp-relay [--config ] [--listen ] [--remote ] [--auth-url ] [--metrics-port ] [--probe ]... [--probe-mesh] [--mesh-status]"); eprintln!(); eprintln!("Options:"); + eprintln!(" --config Load configuration from TOML file (peers, listen, etc.)"); eprintln!(" --listen Listen address (default: 0.0.0.0:4433)"); eprintln!(" --remote Remote relay for forwarding (disables room mode)"); eprintln!(" --auth-url featherChat auth endpoint (e.g., https://chat.example.com/v1/auth/validate)"); @@ -258,18 +282,27 @@ async fn main() -> anyhow::Result<()> { let relay_fp = relay_seed.derive_identity().public_identity().fingerprint; info!(addr = %config.listen_addr, fingerprint = %relay_fp, "WarzonePhone relay starting"); - // Print federation hint with our public IP + listen port - let listen_port = config.listen_addr.port(); - let public_ip = detect_public_ip(); - if let Some(ip) = &public_ip { - info!("federation: to peer with this relay, add to peers config:"); - info!(" - url: \"{ip}:{listen_port}\""); - info!(" fingerprint: \"{relay_fp}\""); - } - let (server_config, cert_der) = wzp_transport::server_config_from_seed(&relay_seed.0); let tls_fp = wzp_transport::tls_fingerprint(&cert_der); info!(tls_fingerprint = %tls_fp, "TLS certificate (deterministic from relay identity)"); + + // Print federation hint with our public IP + listen port + TLS fingerprint + let listen_port = config.listen_addr.port(); + let public_ip = detect_public_ip(); + if let Some(ip) = &public_ip { + info!("federation: to peer with this relay, add to relay.toml:"); + info!(" [[peers]]"); + info!(" url = \"{ip}:{listen_port}\""); + info!(" fingerprint = \"{tls_fp}\""); + } + + // Log configured peers + if !config.peers.is_empty() { + info!(count = config.peers.len(), "federation peers configured"); + for p in &config.peers { + info!(url = %p.url, label = ?p.label, " peer"); + } + } let endpoint = wzp_transport::create_endpoint(config.listen_addr, Some(server_config))?; // Forward mode