diff --git a/iroh-cli/src/commands/gossip.rs b/iroh-cli/src/commands/gossip.rs index 84479854ad..b8e2c8d82e 100644 --- a/iroh-cli/src/commands/gossip.rs +++ b/iroh-cli/src/commands/gossip.rs @@ -9,20 +9,39 @@ use iroh::{ client::{gossip::SubscribeOpts, Iroh}, net::NodeId, }; +use std::str::FromStr as _; use tokio::io::AsyncBufReadExt; /// Commands to manage gossiping. #[derive(Subcommand, Debug, Clone)] #[allow(clippy::large_enum_variant)] pub enum GossipCommands { - /// Subscribe to a topic - #[command(group( - ArgGroup::new("input") - .required(true) - .args(&["topic", "raw_topic"]) - ))] + /// Subscribe to a gossip topic + #[command( + long_about = r#"Subscribe to a gossip topic + +Example usage: + + $ iroh gossip subscribe --topic test --start + +This will print the current node's id. Open another terminal +or another machine and you can join the same topic: + + # on another machine/terminal + $ iroh gossip subscribe --topic test --start + +Any lines entered in stdin will be sent to the given topic +and received messages will be printed to stdout line-by-line. + +The process waits for Ctrl+C to exit."#, + group( + ArgGroup::new("input") + .required(true) + .args(&["topic", "raw_topic"]) + ) + )] Subscribe { - /// Topic string to subscribe to. + /// The topic to subscribe to. /// /// This will be hashed with BLAKE3 to get the actual topic ID. #[clap(long)] @@ -30,7 +49,12 @@ pub enum GossipCommands { /// The raw topic to subscribe to as hex. Needs to be 32 bytes, i.e. 64 hex characters. #[clap(long)] raw_topic: Option, - bootstrap: Vec, + /// The set of nodes that are also part of the gossip swarm to bootstrap with. + /// + /// If empty, this will bootstrap a new swarm. Running the command will print + /// the node's `NodeId`, which can be used as the bootstrap argument in other nodes. + bootstrap: Vec, + /// If enabled, all gossip events will be printed, including neighbor up/down events. #[clap(long, short)] verbose: bool, }, @@ -46,7 +70,13 @@ impl GossipCommands { bootstrap, verbose, } => { - let bootstrap = bootstrap.into_iter().collect(); + let bootstrap = bootstrap + .into_iter() + .map(|node_id| NodeId::from_str(&node_id).map_err(|e| { + anyhow::anyhow!("Failed to parse bootstrap node id \"{node_id}\": {e}\nMust be a valid base32-encoded iroh node id.") + })) + .collect::>()?; + let topic = match (topic, raw_topic) { (Some(topic), None) => blake3::hash(topic.as_bytes()).into(), (None, Some(raw_topic)) => { @@ -57,7 +87,7 @@ impl GossipCommands { } _ => anyhow::bail!("either topic or raw_topic must be provided"), }; - // blake3::hash(topic.as_ref()).into(); + let opts = SubscribeOpts { bootstrap, subscription_capacity: 1024,