use clap::{Parser, Subcommand}; mod cli; mod keystore; mod net; mod storage; mod tui; #[derive(Parser)] #[command(name = "warzone", about = "Warzone messenger client")] struct Cli { #[command(subcommand)] command: Commands, } #[derive(Subcommand)] enum Commands { /// Generate a new identity (seed + keypair + pre-keys) Init, /// Recover identity from BIP39 mnemonic Recover { /// 24-word mnemonic #[arg(num_args = 1..)] words: Vec, }, /// Show your fingerprint and public key Info, /// Register your key bundle with a server Register { /// Server URL #[arg(short, long, default_value = "http://localhost:7700")] server: String, }, /// Send an encrypted message Send { /// Recipient fingerprint (e.g. a3f8:c912:...) or @alias recipient: String, /// Message text message: String, /// Server URL #[arg(short, long, default_value = "http://localhost:7700")] server: String, }, /// Poll for and decrypt messages Recv { /// Server URL #[arg(short, long, default_value = "http://localhost:7700")] server: String, }, /// Launch interactive TUI chat Chat { /// Peer fingerprint or @alias (optional) peer: Option, /// Server URL #[arg(short, long, default_value = "http://localhost:7700")] server: String, }, } #[tokio::main] async fn main() -> anyhow::Result<()> { let cli = Cli::parse(); match cli.command { // These don't need an existing identity Commands::Init => return cli::init::run(), Commands::Recover { words } => return cli::recover::run(&words.join(" ")), _ => {} } // All other commands need the seed — unlock once here let seed = keystore::load_seed()?; // Create a copy for the poll thread (Seed doesn't impl Clone due to Zeroize) let poll_seed = warzone_protocol::identity::Seed::from_bytes(seed.0); let identity = seed.derive_identity(); let our_fp = identity.public_identity().fingerprint.to_string(); match cli.command { Commands::Init | Commands::Recover { .. } => unreachable!(), Commands::Info => { let pub_id = identity.public_identity(); println!("Fingerprint: {}", pub_id.fingerprint); println!("Signing key: {}", hex::encode(pub_id.signing.as_bytes())); println!("Encryption key: {}", hex::encode(pub_id.encryption.as_bytes())); } Commands::Register { server } => { cli::init::register_with_server_identity(&server, &identity).await?; } Commands::Send { recipient, message, server, } => { let _ = cli::init::register_with_server_identity(&server, &identity).await; cli::send::run(&recipient, &message, &server, &identity).await?; } Commands::Recv { server } => { cli::recv::run(&server, &identity).await?; } Commands::Chat { peer, server } => { let _ = cli::init::register_with_server_identity(&server, &identity).await; let db = storage::LocalDb::open()?; tui::run_tui(our_fp, peer, server, identity, poll_seed, db).await?; } } Ok(()) }