use std::str::FromStr; use anyhow::{bail, Result}; use clap::{Parser, ValueEnum}; use iroh_net::{ discovery::{ dns::{N0_DNS_NODE_ORIGIN_PROD, N0_DNS_NODE_ORIGIN_STAGING}, pkarr::{PkarrRelayClient, N0_DNS_PKARR_RELAY_PROD, N0_DNS_PKARR_RELAY_STAGING}, }, dns::node_info::{to_z32, NodeInfo, IROH_TXT_NAME}, key::SecretKey, NodeId, }; use url::Url; const LOCALHOST_PKARR: &str = "http://localhost:8080/pkarr"; const EXAMPLE_ORIGIN: &str = "irohdns.example"; #[derive(ValueEnum, Clone, Debug, Default, Copy, strum::Display)] #[strum(serialize_all = "kebab-case")] pub enum Env { /// Use the staging pkarr relay run by number0. #[default] Staging, /// Use the production pkarr relay run by number0. Prod, /// Use a relay listening at http://localhost:8080 Dev, } /// Publish a record to an irohdns server. /// /// You have to set the IROH_SECRET environment variable to the node secret for which to publish. #[derive(Parser, Debug)] struct Cli { /// Environment to publish to. #[clap(value_enum, short, long, default_value_t = Env::Staging)] env: Env, /// Pkarr Relay URL. If set, the --env option will be ignored. #[clap(long, conflicts_with = "env")] pkarr_relay: Option, /// Home relay server to publish for this node relay_url: Url, /// Create a new node secret if IROH_SECRET is unset. Only for development / debugging. #[clap(short, long)] create: bool, } #[tokio::main] async fn main() -> Result<()> { tracing_subscriber::fmt::init(); let args = Cli::parse(); let secret_key = match std::env::var("IROH_SECRET") { Ok(s) => SecretKey::from_str(&s)?, Err(_) if args.create => { let s = SecretKey::generate(); println!("Generated a new node secret. To reuse, set"); println!("IROH_SECRET={s}"); s } Err(_) => { bail!("Environment variable IROH_SECRET is not set. To create a new secret, use the --create option.") } }; let node_id = secret_key.public(); let pkarr_relay = match (args.pkarr_relay, args.env) { (Some(pkarr_relay), _) => pkarr_relay, (None, Env::Staging) => N0_DNS_PKARR_RELAY_STAGING.parse().expect("valid url"), (None, Env::Prod) => N0_DNS_PKARR_RELAY_PROD.parse().expect("valid url"), (None, Env::Dev) => LOCALHOST_PKARR.parse().expect("valid url"), }; println!("announce {node_id}:"); println!(" relay={}", args.relay_url); println!(); println!("publish to {pkarr_relay} ..."); let pkarr = PkarrRelayClient::new(pkarr_relay); let node_info = NodeInfo::new(node_id, Some(args.relay_url), Default::default()); let signed_packet = node_info.to_pkarr_signed_packet(&secret_key, 30)?; pkarr.publish(&signed_packet).await?; println!("signed packet published."); println!("resolve with:"); match args.env { Env::Staging => { println!(" cargo run --example resolve -- node {}", node_id); println!( " dig {} TXT", fmt_domain(&node_id, N0_DNS_NODE_ORIGIN_STAGING) ) } Env::Prod => { println!( " cargo run --example resolve -- --env prod node {}", node_id ); println!( " dig {} TXT", fmt_domain(&node_id, N0_DNS_NODE_ORIGIN_PROD) ) } Env::Dev => { println!( " cargo run --example resolve -- --env dev node {}", node_id ); println!( " dig @localhost -p 5300 {} TXT", fmt_domain(&node_id, EXAMPLE_ORIGIN) ) } } Ok(()) } fn fmt_domain(node_id: &NodeId, origin: &str) -> String { format!("{}.{}.{}", IROH_TXT_NAME, to_z32(node_id), origin) }