refactor: clap CLI parser, safety docs, dead code docs, cross-refs
Audit items 6, 8, 9, 10: #6 - Relay CLI: replaced 154-line manual parse_args() with clap derive (13 flags/options preserved, auto --help, --version from build hash) #8 - wzp-native: added # Safety docs to all 3 unsafe extern "C" fns #9 - wzp-crypto: documented x25519_static_secret/public as reserved for future static-key federation auth (not dead code, intentionally unused) #10 - Cross-references between quality.rs ↔ dred_tuner.rs module docs 368 tests passing, 0 regressions. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -18,10 +18,14 @@ use crate::session::ChaChaSession;
|
||||
pub struct WarzoneKeyExchange {
|
||||
/// Ed25519 signing key (identity).
|
||||
signing_key: SigningKey,
|
||||
/// X25519 static secret (derived from seed, used for identity encryption).
|
||||
/// X25519 static secret derived from identity seed. Reserved for future
|
||||
/// use in static-key federation authentication (not used in current
|
||||
/// ephemeral-only handshake protocol).
|
||||
#[allow(dead_code)]
|
||||
x25519_static_secret: StaticSecret,
|
||||
/// X25519 static public key.
|
||||
/// X25519 static public key derived from identity seed. Reserved for
|
||||
/// future use in static-key federation authentication (not used in
|
||||
/// current ephemeral-only handshake protocol).
|
||||
#[allow(dead_code)]
|
||||
x25519_static_public: X25519PublicKey,
|
||||
/// Ephemeral X25519 secret for the current call (set by generate_ephemeral).
|
||||
|
||||
@@ -26,6 +26,11 @@ pub extern "C" fn wzp_native_version() -> i32 {
|
||||
|
||||
/// Writes a NUL-terminated string into `out` (capped at `cap`) and
|
||||
/// returns bytes written excluding the NUL.
|
||||
///
|
||||
/// # Safety
|
||||
/// `out` must be a valid pointer to at least `cap` contiguous bytes of
|
||||
/// writable memory. Passing a null pointer or zero capacity is safe
|
||||
/// (returns 0), but a dangling non-null pointer is undefined behaviour.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wzp_native_hello(out: *mut u8, cap: usize) -> usize {
|
||||
const MSG: &[u8] = b"hello from wzp-native\0";
|
||||
@@ -273,6 +278,11 @@ pub extern "C" fn wzp_native_audio_capture_available() -> usize {
|
||||
/// Read captured PCM samples from the capture ring. Returns the number
|
||||
/// of `i16` samples actually copied into `out` (may be less than
|
||||
/// `out_len` if the ring is empty).
|
||||
///
|
||||
/// # Safety
|
||||
/// `out` must be a valid pointer to `out_len` contiguous `i16` values.
|
||||
/// The caller must ensure no other thread writes to the same buffer
|
||||
/// concurrently. Passing a null pointer or zero length is safe (returns 0).
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wzp_native_audio_read_capture(out: *mut i16, out_len: usize) -> usize {
|
||||
if out.is_null() || out_len == 0 {
|
||||
@@ -286,6 +296,12 @@ pub unsafe extern "C" fn wzp_native_audio_read_capture(out: *mut i16, out_len: u
|
||||
/// samples actually enqueued (may be less than `in_len` if the ring
|
||||
/// is nearly full — in practice the caller should pace to 20 ms
|
||||
/// frames and spin briefly if the ring is full).
|
||||
///
|
||||
/// # Safety
|
||||
/// `input` must be a valid pointer to `in_len` contiguous `i16` values
|
||||
/// that remain valid for the duration of the call. Passing a null pointer
|
||||
/// or zero length is safe (returns 0). The caller must not free or mutate
|
||||
/// the buffer while this function is executing.
|
||||
#[unsafe(no_mangle)]
|
||||
pub unsafe extern "C" fn wzp_native_audio_write_playout(input: *const i16, in_len: usize) -> usize {
|
||||
if input.is_null() || in_len == 0 {
|
||||
|
||||
@@ -10,6 +10,10 @@
|
||||
//! prediction): when jitter variance spikes >30% over a 200 ms window — typical
|
||||
//! of Starlink satellite handovers — it temporarily boosts DRED to the maximum
|
||||
//! allowed for the current codec before packets actually start dropping.
|
||||
//!
|
||||
//! See also: [`crate::quality`] for discrete tier classification that drives
|
||||
//! codec switching. DredTuner operates within a tier, adjusting DRED
|
||||
//! parameters continuously based on live network metrics.
|
||||
|
||||
use crate::CodecId;
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
//! See also: [`crate::dred_tuner`] for continuous DRED tuning within a tier.
|
||||
|
||||
use std::collections::VecDeque;
|
||||
use std::time::{Duration, Instant};
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ bytes = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
toml = "0.8"
|
||||
anyhow = "1"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
reqwest = { version = "0.12", features = ["json"] }
|
||||
serde_json = "1"
|
||||
rustls = { version = "0.23", default-features = false, features = ["ring", "std"] }
|
||||
|
||||
@@ -12,6 +12,7 @@ use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
use clap::Parser;
|
||||
use tokio::sync::Mutex;
|
||||
use tracing::{debug, error, info, warn};
|
||||
|
||||
@@ -30,6 +31,72 @@ async fn close_transport(t: &dyn wzp_proto::MediaTransport, context: &str) {
|
||||
}
|
||||
}
|
||||
|
||||
/// WarzonePhone relay daemon — SFU, federation, direct-call signaling
|
||||
#[derive(Parser, Debug)]
|
||||
#[command(name = "wzp-relay", version = env!("WZP_BUILD_HASH"))]
|
||||
struct Args {
|
||||
/// Load config from TOML file (creates example if missing)
|
||||
#[arg(short = 'c', long = "config")]
|
||||
config_file: Option<String>,
|
||||
|
||||
/// Identity file path (creates if missing, uses OsRng)
|
||||
#[arg(short = 'i', long)]
|
||||
identity: Option<String>,
|
||||
|
||||
/// Listen address for QUIC connections
|
||||
#[arg(long)]
|
||||
listen: Option<SocketAddr>,
|
||||
|
||||
/// Remote relay address for forwarding (disables room mode)
|
||||
#[arg(long)]
|
||||
remote: Option<SocketAddr>,
|
||||
|
||||
/// featherChat auth endpoint (e.g., https://chat.example.com/v1/auth/validate).
|
||||
/// When set, clients must send a bearer token as first signal message.
|
||||
#[arg(long)]
|
||||
auth_url: Option<String>,
|
||||
|
||||
/// Prometheus metrics HTTP port (e.g., 9090). Disabled if not set.
|
||||
#[arg(long)]
|
||||
metrics_port: Option<u16>,
|
||||
|
||||
/// Peer relay to probe for health monitoring (repeatable)
|
||||
#[arg(long = "probe")]
|
||||
probe: Vec<SocketAddr>,
|
||||
|
||||
/// Enable mesh mode (probes all --probe targets concurrently)
|
||||
#[arg(long)]
|
||||
probe_mesh: bool,
|
||||
|
||||
/// Enable trunk batching for outgoing media in room mode
|
||||
#[arg(long)]
|
||||
trunking: bool,
|
||||
|
||||
/// WebSocket listener port for browser clients (e.g., 8080)
|
||||
#[arg(long)]
|
||||
ws_port: Option<u16>,
|
||||
|
||||
/// Directory to serve static files from (HTML/JS/WASM)
|
||||
#[arg(long)]
|
||||
static_dir: Option<String>,
|
||||
|
||||
/// Declare a room as global (bridged across federation). Repeatable.
|
||||
#[arg(long = "global-room")]
|
||||
global_room: Vec<String>,
|
||||
|
||||
/// Log packet headers for a room ('*' for all rooms)
|
||||
#[arg(long)]
|
||||
debug_tap: Option<String>,
|
||||
|
||||
/// JSONL event log file path for protocol analysis
|
||||
#[arg(long)]
|
||||
event_log: Option<String>,
|
||||
|
||||
/// Print mesh health table and exit (diagnostic)
|
||||
#[arg(long)]
|
||||
mesh_status: bool,
|
||||
}
|
||||
|
||||
/// Parsed CLI result — config + identity path.
|
||||
struct CliResult {
|
||||
config: RelayConfig,
|
||||
@@ -39,25 +106,21 @@ struct CliResult {
|
||||
}
|
||||
|
||||
fn parse_args() -> CliResult {
|
||||
let args: Vec<String> = std::env::args().collect();
|
||||
let args = Args::parse();
|
||||
|
||||
// First pass: extract --config and --identity
|
||||
let mut config_file = None;
|
||||
let mut identity_path = None;
|
||||
let mut i = 1;
|
||||
while i < args.len() {
|
||||
match args[i].as_str() {
|
||||
"--config" | "-c" => { i += 1; config_file = args.get(i).cloned(); }
|
||||
"--identity" | "-i" => { i += 1; identity_path = args.get(i).cloned(); }
|
||||
_ => {}
|
||||
}
|
||||
i += 1;
|
||||
// Handle --mesh-status: print and exit
|
||||
if args.mesh_status {
|
||||
let m = RelayMetrics::new();
|
||||
print!("{}", wzp_relay::probe::mesh_summary(m.registry()));
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
// Track if we need to create the config after identity is known
|
||||
let config_needs_create = config_file.as_ref().map(|p| !std::path::Path::new(p).exists()).unwrap_or(false);
|
||||
let config_needs_create = args.config_file.as_ref()
|
||||
.map(|p| !std::path::Path::new(p).exists())
|
||||
.unwrap_or(false);
|
||||
|
||||
let mut config = if let Some(ref path) = config_file {
|
||||
let mut config = if let Some(ref path) = args.config_file {
|
||||
if config_needs_create {
|
||||
// Will be re-created with personalized info after identity is loaded
|
||||
RelayConfig::default()
|
||||
@@ -73,125 +136,49 @@ fn parse_args() -> CliResult {
|
||||
};
|
||||
|
||||
// CLI flags override config file values
|
||||
let mut i = 1;
|
||||
while i < args.len() {
|
||||
match args[i].as_str() {
|
||||
"--config" | "-c" => { i += 1; } // already handled
|
||||
"--identity" | "-i" => { i += 1; } // already handled
|
||||
"--listen" => {
|
||||
i += 1;
|
||||
config.listen_addr = args.get(i).expect("--listen requires an address")
|
||||
.parse().expect("invalid --listen address");
|
||||
}
|
||||
"--remote" => {
|
||||
i += 1;
|
||||
config.remote_relay = Some(
|
||||
args.get(i).expect("--remote requires an address")
|
||||
.parse().expect("invalid --remote address"),
|
||||
);
|
||||
}
|
||||
"--auth-url" => {
|
||||
i += 1;
|
||||
config.auth_url = Some(
|
||||
args.get(i).expect("--auth-url requires a URL").to_string(),
|
||||
);
|
||||
}
|
||||
"--metrics-port" => {
|
||||
i += 1;
|
||||
config.metrics_port = Some(
|
||||
args.get(i).expect("--metrics-port requires a port number")
|
||||
.parse().expect("invalid --metrics-port number"),
|
||||
);
|
||||
}
|
||||
"--probe" => {
|
||||
i += 1;
|
||||
let addr: SocketAddr = args.get(i)
|
||||
.expect("--probe requires an address")
|
||||
.parse()
|
||||
.expect("invalid --probe address");
|
||||
config.probe_targets.push(addr);
|
||||
}
|
||||
"--probe-mesh" => {
|
||||
config.probe_mesh = true;
|
||||
}
|
||||
"--trunking" => {
|
||||
config.trunking_enabled = true;
|
||||
}
|
||||
"--ws-port" => {
|
||||
i += 1;
|
||||
config.ws_port = Some(
|
||||
args.get(i).expect("--ws-port requires a port number")
|
||||
.parse().expect("invalid --ws-port number"),
|
||||
);
|
||||
}
|
||||
"--static-dir" => {
|
||||
i += 1;
|
||||
config.static_dir = Some(
|
||||
args.get(i).expect("--static-dir requires a directory path").to_string(),
|
||||
);
|
||||
}
|
||||
"--global-room" => {
|
||||
i += 1;
|
||||
config.global_rooms.push(wzp_relay::config::GlobalRoomConfig {
|
||||
name: args.get(i).expect("--global-room requires a room name").to_string(),
|
||||
});
|
||||
}
|
||||
"--debug-tap" => {
|
||||
i += 1;
|
||||
config.debug_tap = Some(
|
||||
args.get(i).expect("--debug-tap requires a room name (or '*' for all)").to_string(),
|
||||
);
|
||||
}
|
||||
"--event-log" => {
|
||||
i += 1;
|
||||
config.event_log = Some(
|
||||
args.get(i).expect("--event-log requires a file path").to_string(),
|
||||
);
|
||||
}
|
||||
"--version" | "-V" => {
|
||||
println!("wzp-relay {}", env!("WZP_BUILD_HASH"));
|
||||
std::process::exit(0);
|
||||
}
|
||||
"--mesh-status" => {
|
||||
// Print mesh table from a fresh registry and exit.
|
||||
// In practice this is useful after the relay has been running;
|
||||
// here we just demonstrate the formatter with an empty registry.
|
||||
let m = RelayMetrics::new();
|
||||
print!("{}", wzp_relay::probe::mesh_summary(m.registry()));
|
||||
std::process::exit(0);
|
||||
}
|
||||
"--help" | "-h" => {
|
||||
eprintln!("Usage: wzp-relay [--config <path>] [--listen <addr>] [--remote <addr>] [--auth-url <url>] [--metrics-port <port>] [--probe <addr>]... [--probe-mesh] [--mesh-status]");
|
||||
eprintln!();
|
||||
eprintln!("Options:");
|
||||
eprintln!(" -c, --config <path> Load config from TOML file (creates example if missing)");
|
||||
eprintln!(" -i, --identity <path> Identity file path (creates if missing, uses OsRng)");
|
||||
eprintln!(" --listen <addr> Listen address (default: 0.0.0.0:4433)");
|
||||
eprintln!(" --remote <addr> Remote relay for forwarding (disables room mode)");
|
||||
eprintln!(" --auth-url <url> featherChat auth endpoint (e.g., https://chat.example.com/v1/auth/validate)");
|
||||
eprintln!(" When set, clients must send a bearer token as first signal message.");
|
||||
eprintln!(" --metrics-port <port> Prometheus metrics HTTP port (e.g., 9090). Disabled if not set.");
|
||||
eprintln!(" --probe <addr> Peer relay to probe for health monitoring (repeatable).");
|
||||
eprintln!(" --probe-mesh Enable mesh mode (mark config flag, probes all --probe targets).");
|
||||
eprintln!(" --mesh-status Print mesh health table and exit (diagnostic).");
|
||||
eprintln!(" --trunking Enable trunk batching for outgoing media in room mode.");
|
||||
eprintln!(" --global-room <name> Declare a room as global (bridged across federation). Repeatable.");
|
||||
eprintln!(" --debug-tap <room> Log packet headers for a room ('*' for all rooms).");
|
||||
eprintln!(" --ws-port <port> WebSocket listener port for browser clients (e.g., 8080).");
|
||||
eprintln!(" --static-dir <dir> Directory to serve static files from (HTML/JS/WASM).");
|
||||
eprintln!();
|
||||
eprintln!("Room mode (default):");
|
||||
eprintln!(" Clients join rooms by name. Packets forwarded to all others (SFU).");
|
||||
std::process::exit(0);
|
||||
}
|
||||
other => {
|
||||
eprintln!("unknown argument: {other}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
if let Some(addr) = args.listen {
|
||||
config.listen_addr = addr;
|
||||
}
|
||||
if let Some(addr) = args.remote {
|
||||
config.remote_relay = Some(addr);
|
||||
}
|
||||
if let Some(url) = args.auth_url {
|
||||
config.auth_url = Some(url);
|
||||
}
|
||||
if let Some(port) = args.metrics_port {
|
||||
config.metrics_port = Some(port);
|
||||
}
|
||||
if !args.probe.is_empty() {
|
||||
config.probe_targets.extend(args.probe);
|
||||
}
|
||||
if args.probe_mesh {
|
||||
config.probe_mesh = true;
|
||||
}
|
||||
if args.trunking {
|
||||
config.trunking_enabled = true;
|
||||
}
|
||||
if let Some(port) = args.ws_port {
|
||||
config.ws_port = Some(port);
|
||||
}
|
||||
if let Some(dir) = args.static_dir {
|
||||
config.static_dir = Some(dir);
|
||||
}
|
||||
for name in args.global_room {
|
||||
config.global_rooms.push(wzp_relay::config::GlobalRoomConfig { name });
|
||||
}
|
||||
if let Some(tap) = args.debug_tap {
|
||||
config.debug_tap = Some(tap);
|
||||
}
|
||||
if let Some(log) = args.event_log {
|
||||
config.event_log = Some(log);
|
||||
}
|
||||
|
||||
CliResult {
|
||||
config,
|
||||
identity_path: args.identity,
|
||||
config_file: args.config_file,
|
||||
config_needs_create,
|
||||
}
|
||||
CliResult { config, identity_path, config_file, config_needs_create }
|
||||
}
|
||||
|
||||
struct RelayStats {
|
||||
|
||||
Reference in New Issue
Block a user