Files
featherChat/warzone/crates/warzone-client/src/main.rs
Siavash Sameni d7b71efdbc Fix DB lock error: clear message + instructions, fix passphrase reprompt
Storage:
- Detects sled lock contention, shows actionable error:
  "Database locked by another warzone process"
  with ps command to find the process and rm command to force unlock

TUI:
- Poll loop no longer calls load_seed() (was re-prompting passphrase)
- Seed passed from main.rs to run_tui to poll_loop
- Single passphrase prompt per app launch

Warnings fixed:
- Removed unused `Context` import in tui/app.rs
- Added #[allow(dead_code)] on validate_token (used when auth middleware wired)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 08:24:53 +04:00

109 lines
3.3 KiB
Rust

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<String>,
},
/// 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<String>,
/// 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(())
}