diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 121 |
1 files changed, 21 insertions, 100 deletions
diff --git a/src/main.rs b/src/main.rs index 65134d8..217a9a4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,21 +5,18 @@ use std::collections::HashMap; use std::future::IntoFuture; -use std::net::SocketAddr; -use std::path::PathBuf; use std::sync::Arc; use std::sync::atomic::AtomicBool; use std::time::Duration; use anyhow::{Context, Result}; use automerge::AutoCommit; -use clap::Parser; use tokio::sync::{RwLock, broadcast, mpsc, watch}; use tracing::{debug, error, info, trace, warn}; -use url::Url; mod api; mod backoff; +mod config; mod document; mod logging; mod remake; @@ -36,98 +33,16 @@ use transport::firewall::{Firewall, FirewallTransport}; use transport::mycelium::{self, MyceliumSetup, MyceliumSpawnConfig}; use transport::traffic::{CountingTransport, Traffic}; -#[derive(Parser, Debug)] -#[command(version, about = "continuum messaging system", long_about = None)] -struct Args { - #[arg(long)] - debug_api: bool, - - #[arg(long, value_name = "HOSTNAME:PORT", default_value = "127.0.0.1:3000")] - bind_addr: SocketAddr, - - #[arg( - long, - value_name = "SCHEME://HOSTNAME:PORT", - default_value = None, - conflicts_with_all = &["mycelium_key_file", "mycelium_network_key_file", "mycelium_network_name"], - )] - mycelium_rest_url: Option<Url>, - - #[arg(long)] - pub mycelium_key_file: Option<PathBuf>, - - #[arg(long)] - pub mycelium_network_key_file: Option<PathBuf>, - - #[arg(long)] - pub mycelium_network_name: Option<String>, - - #[arg(long)] - pub mycelium_tcp_listen_port: Option<u16>, - - #[arg( - long, - use_value_delimiter = true, - default_value = "tls://127.0.0.1:9651" - )] - /// Peers of the supervised Mycelium daemon (overlay links such as - /// `tls://host:9651`), not continuum peers. - pub mycelium_peers: Vec<String>, - - /// Seeds: how to reach a node of the swarm to join, as a Mycelium public key, a - /// Mycelium IPv6 address, or a host name with an AAAA record for one. Without seeds, - /// the node starts a new swarm (genesis). - #[arg(long, value_name = "STRING,[STRING...]", value_delimiter = ',')] - seeds: Option<Vec<String>>, - - #[arg(long, value_name = "PATH")] - htpasswd: String, - - /// Pre-shared swarm key: only nodes that know it may join (raw bytes, at least 16; - /// e.g. `head -c 32 /dev/urandom`). Without it, anyone who knows the swarm ID can. - #[arg(long, value_name = "PATH")] - swarm_key_file: Option<PathBuf>, - - /// Keepalive interval; dormancy, resync and probe timings derive from it. - #[arg(long, value_name = "SECONDS", default_value_t = 60)] - keepalive_secs: u64, - - /// Neighbours each node keeps (at least 2). Swarms of up to this many + 1 nodes are - /// a full mesh; larger ones sync over several hops. - #[arg(long, value_name = "COUNT", default_value_t = 12)] - neighbours: usize, - - /// A name for this node, shown next to its public key (/peers, metrics, logs). - /// Published in the swarm's member list; not unique, never an identity. - #[arg(long, value_name = "NAME", value_parser = parse_alias)] - alias: Option<String>, - - /// Test hook: start on a non-genesis epoch of this generation (with a fixed dummy - /// id), to check that nodes on different epochs refuse to sync. - #[arg(long, hide = true, requires = "debug_api", value_name = "GENERATION")] - debug_epoch: Option<u64>, -} - -/// Up to 64 characters, no control characters, not blank. -fn parse_alias(value: &str) -> Result<String, String> { - let alias = value.trim(); - if alias.is_empty() { - return Err("must not be blank".to_owned()); - } - if alias.chars().count() > 64 { - return Err("must be at most 64 characters".to_owned()); - } - if alias.chars().any(char::is_control) { - return Err("must not contain control characters".to_owned()); - } - Ok(alias.to_owned()) -} - #[tokio::main] async fn main() -> Result<()> { logging::init(); - let args = Args::parse(); + let config = config::load()?; + if config.print_only { + println!("{}", serde_json::to_string_pretty(&config.report())?); + return Ok(()); + } + let args = &config.settings; // Read before anything starts, so a bad key file fails right away. let swarm_key = match &args.swarm_key_file { @@ -144,13 +59,14 @@ async fn main() -> Result<()> { let mycelium_setup = { let mut spawn_config = MyceliumSpawnConfig { - rest_url_opt: args.mycelium_rest_url, - key_file_opt: args.mycelium_key_file, - network_key_file: args.mycelium_network_key_file, - peers: args.mycelium_peers, + rest_url_opt: args.mycelium_rest_url(), + key_file_opt: args.mycelium_key_file.clone(), + network_key_file: args.mycelium_network_key_file.clone(), + tcp_port: args.mycelium_tcp_listen_port, + peers: args.mycelium_peers.clone(), ..Default::default() }; - if let Some(net_name) = args.mycelium_network_name { + if let Some(net_name) = args.mycelium_network_name.clone() { spawn_config.network_name = net_name; } tokio::select! { @@ -170,7 +86,7 @@ async fn main() -> Result<()> { "Using Mycelium REST API" ); - let seeds: Vec<String> = args.seeds.unwrap_or_default(); + let seeds: Vec<String> = args.seeds.clone(); debug!(seed_count = seeds.len(), "Loaded bootstrap configuration"); let (sync_tx, sync_rx) = mpsc::unbounded_channel(); @@ -220,10 +136,15 @@ async fn main() -> Result<()> { remake: Arc::new(remake::RemakeState::default()), swarm_key, run_id: uuid::Uuid::new_v4().into_bytes(), - neighbours_k: args.neighbours.max(2), - alias: args.alias, + neighbours_k: args.neighbours, + alias: args.alias.clone(), + electorate_window: Duration::from_secs(args.electorate_window_days * 24 * 3600), + blobs: Arc::new(swarm::blob::Blobs::new(args.blob_chunk_kib * 1024)), + protocol_writers: Arc::new(args.protocol_writers.iter().cloned().collect()), + config: Arc::new(config.report()), neighbours: Default::default(), pulling: Default::default(), + announce_pending: Default::default(), rtt: Default::default(), throughput: Default::default(), stability: Default::default(), |
