From 3ffac0c751ca825c0bd946c3b61d95041e4dd19f Mon Sep 17 00:00:00 2001 From: Siavash Sameni Date: Fri, 27 Mar 2026 07:49:51 +0400 Subject: [PATCH] Unlock seed once at startup, pass identity to all commands - main.rs unlocks seed once, prompts passphrase once per app launch - Identity passed as parameter to send, recv, register, chat - No more redundant load_seed() calls (was prompting passphrase multiple times) - info command uses pre-unlocked identity directly Co-Authored-By: Claude Opus 4.6 (1M context) --- warzone/crates/warzone-client/src/cli/info.rs | 17 +------ warzone/crates/warzone-client/src/cli/init.rs | 9 ++-- warzone/crates/warzone-client/src/cli/recv.rs | 6 +-- warzone/crates/warzone-client/src/cli/send.rs | 6 +-- warzone/crates/warzone-client/src/main.rs | 45 +++++++++++-------- 5 files changed, 36 insertions(+), 47 deletions(-) diff --git a/warzone/crates/warzone-client/src/cli/info.rs b/warzone/crates/warzone-client/src/cli/info.rs index 05e5ff8..46f0519 100644 --- a/warzone/crates/warzone-client/src/cli/info.rs +++ b/warzone/crates/warzone-client/src/cli/info.rs @@ -1,16 +1 @@ -use crate::keystore; - -pub fn run() -> anyhow::Result<()> { - let seed = keystore::load_seed()?; - let identity = seed.derive_identity(); - 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()) - ); - - Ok(()) -} +// Info is now handled directly in main.rs with the pre-unlocked identity. diff --git a/warzone/crates/warzone-client/src/cli/init.rs b/warzone/crates/warzone-client/src/cli/init.rs index a3cecc7..ee2292d 100644 --- a/warzone/crates/warzone-client/src/cli/init.rs +++ b/warzone/crates/warzone-client/src/cli/init.rs @@ -71,10 +71,11 @@ pub fn run() -> Result<()> { Ok(()) } -/// Register the local bundle with a server. Called automatically before first send. -pub async fn register_with_server(server_url: &str) -> Result<()> { - let seed = keystore::load_seed()?; - let identity = seed.derive_identity(); +/// Register the local bundle with a server using an already-unlocked identity. +pub async fn register_with_server_identity( + server_url: &str, + identity: &warzone_protocol::identity::IdentityKeyPair, +) -> Result<()> { let pub_id = identity.public_identity(); let fp = pub_id.fingerprint.to_string(); diff --git a/warzone/crates/warzone-client/src/cli/recv.rs b/warzone/crates/warzone-client/src/cli/recv.rs index 7577a10..ba37e90 100644 --- a/warzone/crates/warzone-client/src/cli/recv.rs +++ b/warzone/crates/warzone-client/src/cli/recv.rs @@ -1,17 +1,15 @@ use anyhow::{Context, Result}; +use warzone_protocol::identity::IdentityKeyPair; use warzone_protocol::ratchet::RatchetState; use warzone_protocol::types::Fingerprint; use warzone_protocol::x3dh; use x25519_dalek::PublicKey; use crate::cli::send::WireMessage; -use crate::keystore; use crate::net::ServerClient; use crate::storage::LocalDb; -pub async fn run(server_url: &str) -> Result<()> { - let seed = keystore::load_seed()?; - let identity = seed.derive_identity(); +pub async fn run(server_url: &str, identity: &IdentityKeyPair) -> Result<()> { let our_pub = identity.public_identity(); let our_fp = our_pub.fingerprint.to_string(); let db = LocalDb::open()?; diff --git a/warzone/crates/warzone-client/src/cli/send.rs b/warzone/crates/warzone-client/src/cli/send.rs index c7d38ec..cf9298d 100644 --- a/warzone/crates/warzone-client/src/cli/send.rs +++ b/warzone/crates/warzone-client/src/cli/send.rs @@ -1,10 +1,10 @@ use anyhow::{Context, Result}; +use warzone_protocol::identity::IdentityKeyPair; use warzone_protocol::ratchet::{RatchetMessage, RatchetState}; use warzone_protocol::types::Fingerprint; use warzone_protocol::x3dh; use x25519_dalek::PublicKey; -use crate::keystore; use crate::net::ServerClient; use crate::storage::LocalDb; @@ -26,9 +26,7 @@ pub enum WireMessage { }, } -pub async fn run(recipient_fp: &str, message: &str, server_url: &str) -> Result<()> { - let seed = keystore::load_seed()?; - let identity = seed.derive_identity(); +pub async fn run(recipient_fp: &str, message: &str, server_url: &str, identity: &IdentityKeyPair) -> Result<()> { let our_pub = identity.public_identity(); let db = LocalDb::open()?; let client = ServerClient::new(server_url); diff --git a/warzone/crates/warzone-client/src/main.rs b/warzone/crates/warzone-client/src/main.rs index ffbbcec..dda2d3b 100644 --- a/warzone/crates/warzone-client/src/main.rs +++ b/warzone/crates/warzone-client/src/main.rs @@ -33,7 +33,7 @@ enum Commands { }, /// Send an encrypted message Send { - /// Recipient fingerprint (e.g. a3f8:c912:44be:7d01) + /// Recipient fingerprint (e.g. a3f8:c912:...) or @alias recipient: String, /// Message text message: String, @@ -49,7 +49,7 @@ enum Commands { }, /// Launch interactive TUI chat Chat { - /// Peer fingerprint to chat with (optional, can set with /peer in TUI) + /// Peer fingerprint or @alias (optional) peer: Option, /// Server URL #[arg(short, long, default_value = "http://localhost:7700")] @@ -62,35 +62,42 @@ async fn main() -> anyhow::Result<()> { let cli = Cli::parse(); match cli.command { - Commands::Init => cli::init::run()?, - Commands::Recover { words } => cli::recover::run(&words.join(" "))?, - Commands::Info => cli::info::run()?, + // 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()?; + 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(&server).await?; + cli::init::register_with_server_identity(&server, &identity).await?; } Commands::Send { recipient, message, server, } => { - // Auto-register bundle on first send - if let Err(_) = cli::init::register_with_server(&server).await { - eprintln!("Warning: failed to register bundle with server"); - } - cli::send::run(&recipient, &message, &server).await?; + 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).await?; + cli::recv::run(&server, &identity).await?; } Commands::Chat { peer, server } => { - // Auto-register - let _ = cli::init::register_with_server(&server).await; - - let seed = keystore::load_seed()?; - let identity = seed.derive_identity(); - let our_fp = identity.public_identity().fingerprint.to_string(); + let _ = cli::init::register_with_server_identity(&server, &identity).await; let db = storage::LocalDb::open()?; - tui::run_tui(our_fp, peer, server, identity, db).await?; } }