From 4812a7c2f740c77c94eee284bf9be5faa3b6dc56 Mon Sep 17 00:00:00 2001 From: alexandre-fiotti Date: Sun, 14 Sep 2025 18:25:55 +0200 Subject: [PATCH 01/15] feat: custom relay implem copied from sendme --- Cargo.lock | 5 +++-- Cargo.toml | 1 + src/main.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 145cc6c..cec59cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -110,9 +110,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" dependencies = [ "backtrace", ] @@ -791,6 +791,7 @@ dependencies = [ name = "dumbpipe" version = "0.31.0" dependencies = [ + "anyhow", "clap", "data-encoding", "duct", diff --git a/Cargo.toml b/Cargo.toml index 31c95b4..0630521 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,6 +27,7 @@ tracing = "0.1.40" tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } data-encoding = "2.9.0" n0-snafu = "0.2.2" +anyhow = "1.0.100" [dev-dependencies] duct = "0.13.6" diff --git a/src/main.rs b/src/main.rs index 70ac10c..fd93819 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,10 @@ //! Command line arguments. use clap::{Parser, Subcommand}; use dumbpipe::NodeTicket; -use iroh::{endpoint::Connecting, Endpoint, NodeAddr, SecretKey}; +use iroh::{endpoint::Connecting, Endpoint, NodeAddr, RelayMode, RelayUrl, SecretKey}; use n0_snafu::{Result, ResultExt}; use std::{ - io, - net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}, - str::FromStr, + fmt::{Display, Formatter}, io, net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}, str::FromStr }; use tokio::{ io::{AsyncRead, AsyncWrite, AsyncWriteExt}, @@ -121,7 +119,15 @@ pub struct CommonArgs { /// Otherwise, it will be parsed as a hex string. #[clap(long)] pub custom_alpn: Option, - + + /// The relay URL to use as a home relay, + /// + /// Can be set to "disabled" to disable relay servers and "custom" + /// to configure custom servers. The default is the n0 quickest responding + /// relay if the flag is not set. + #[clap(long, default_value_t = RelayModeOption::Default)] + pub relay: RelayModeOption, + /// The verbosity level. Repeat to increase verbosity. #[clap(short = 'v', long, action = clap::ArgAction::Count)] pub verbose: u8, @@ -148,6 +154,49 @@ fn parse_alpn(alpn: &str) -> Result> { }) } +/// Available command line options for configuring relays. +#[derive(Clone, Debug)] +pub enum RelayModeOption { + /// Disables relays altogether. + Disabled, + /// Uses the default relay servers. + Default, + /// Uses a single, custom relay server by URL. + Custom(RelayUrl), +} + +impl FromStr for RelayModeOption { + type Err = anyhow::Error; + + fn from_str(s: &str) -> Result { + match s { + "disabled" => Ok(Self::Disabled), + "default" => Ok(Self::Default), + _ => Ok(Self::Custom(RelayUrl::from_str(s)?)), + } + } +} + +impl Display for RelayModeOption { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Self::Disabled => f.write_str("disabled"), + Self::Default => f.write_str("default"), + Self::Custom(url) => url.fmt(f), + } + } +} + +impl From for RelayMode { + fn from(value: RelayModeOption) -> Self { + match value { + RelayModeOption::Disabled => RelayMode::Disabled, + RelayModeOption::Default => RelayMode::Default, + RelayModeOption::Custom(url) => RelayMode::Custom(url.into()), + } + } +} + #[derive(Parser, Debug)] pub struct ListenArgs { /// Immediately close our sending side, indicating that we will not transmit any data @@ -291,7 +340,10 @@ async fn create_endpoint( common: &CommonArgs, alpns: Vec>, ) -> Result { - let mut builder = Endpoint::builder().secret_key(secret_key).alpns(alpns); + let mut builder = Endpoint::builder() + .secret_key(secret_key) + .alpns(alpns) + .relay_mode(common.relay.clone().into()); if let Some(addr) = common.ipv4_addr { builder = builder.bind_addr_v4(addr); } From e8c797c5ae8b83a9023b419945a44e738504d032 Mon Sep 17 00:00:00 2001 From: Alexandre Fiotti Date: Fri, 26 Sep 2025 08:52:36 +0200 Subject: [PATCH 02/15] feat: remove anyhow dependency --- Cargo.lock | 1 - Cargo.toml | 1 - src/main.rs | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cec59cb..f403443 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -791,7 +791,6 @@ dependencies = [ name = "dumbpipe" version = "0.31.0" dependencies = [ - "anyhow", "clap", "data-encoding", "duct", diff --git a/Cargo.toml b/Cargo.toml index 0630521..31c95b4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,6 @@ tracing = "0.1.40" tracing-subscriber = { version = "0.3.20", features = ["env-filter"] } data-encoding = "2.9.0" n0-snafu = "0.2.2" -anyhow = "1.0.100" [dev-dependencies] duct = "0.13.6" diff --git a/src/main.rs b/src/main.rs index fd93819..085d3f2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -166,7 +166,7 @@ pub enum RelayModeOption { } impl FromStr for RelayModeOption { - type Err = anyhow::Error; + type Err = iroh::RelayUrlParseError; fn from_str(s: &str) -> Result { match s { From a3c7c371eac16a0d4847d1668b2bed97a07fed3d Mon Sep 17 00:00:00 2001 From: Alexandre Fiotti Date: Fri, 3 Oct 2025 16:04:32 +0200 Subject: [PATCH 03/15] fix: solve code blocking on disabled relay --- src/main.rs | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/main.rs b/src/main.rs index 085d3f2..42b4fd9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -155,7 +155,7 @@ fn parse_alpn(alpn: &str) -> Result> { } /// Available command line options for configuring relays. -#[derive(Clone, Debug)] +#[derive(Clone, Debug, PartialEq)] pub enum RelayModeOption { /// Disables relays altogether. Disabled, @@ -351,6 +351,8 @@ async fn create_endpoint( builder = builder.bind_addr_v6(addr); } let endpoint = builder.bind().await?; + endpoint.online().await; + Ok(endpoint) } @@ -396,11 +398,10 @@ async fn forward_bidi( async fn listen_stdio(args: ListenArgs) -> Result<()> { let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![args.common.alpn()?]).await?; - // wait for the endpoint to figure out its home relay and addresses before making a ticket - endpoint.online().await; - let node = endpoint.node_addr(); - let mut short = node.clone(); - let ticket = NodeTicket::new(node); + // wait for the endpoint to figure out its address before making a ticket + let node_addr = endpoint.node_addr(); + let mut short = node_addr.clone(); + let ticket = NodeTicket::new(node_addr); short.direct_addresses.clear(); let short = NodeTicket::new(short); @@ -457,10 +458,10 @@ async fn listen_stdio(args: ListenArgs) -> Result<()> { async fn connect_stdio(args: ConnectArgs) -> Result<()> { let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![]).await?; - let addr = args.ticket.node_addr(); - let remote_node_id = addr.node_id; + let node_addr = endpoint.node_addr(); + let remote_node_id = node_addr.node_id; // connect to the node, try only once - let connection = endpoint.connect(addr.clone(), &args.common.alpn()?).await?; + let connection = endpoint.connect(node_addr.clone(), &args.common.alpn()?).await?; tracing::info!("connected to {}", remote_node_id); // open a bidi stream, try only once let (mut s, r) = connection.open_bi().await.e()?; @@ -495,9 +496,6 @@ async fn connect_tcp(args: ConnectTcpArgs) -> Result<()> { .context("unable to bind endpoint")?; tracing::info!("tcp listening on {:?}", addrs); - // Wait for our own endpoint to be ready before trying to connect. - endpoint.online().await; - let tcp_listener = match tokio::net::TcpListener::bind(addrs.as_slice()).await { Ok(tcp_listener) => tcp_listener, Err(cause) => { @@ -534,7 +532,7 @@ async fn connect_tcp(args: ConnectTcpArgs) -> Result<()> { forward_bidi(tcp_recv, tcp_send, endpoint_recv, endpoint_send).await?; Ok::<_, n0_snafu::Error>(()) } - let addr = args.ticket.node_addr(); + let node_addr = args.ticket.node_addr(); loop { // also wait for ctrl-c here so we can use it before accepting a connection let next = tokio::select! { @@ -545,11 +543,11 @@ async fn connect_tcp(args: ConnectTcpArgs) -> Result<()> { } }; let endpoint = endpoint.clone(); - let addr = addr.clone(); + let node_addr = node_addr.clone(); let handshake = !args.common.is_custom_alpn(); let alpn = args.common.alpn()?; tokio::spawn(async move { - if let Err(cause) = handle_tcp_accept(next, addr, endpoint, handshake, &alpn).await { + if let Err(cause) = handle_tcp_accept(next, node_addr, endpoint, handshake, &alpn).await { // log error at warn level // // we should know about it, but it's not fatal @@ -568,8 +566,6 @@ async fn listen_tcp(args: ListenTcpArgs) -> Result<()> { }; let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![args.common.alpn()?]).await?; - // wait for the endpoint to figure out its address before making a ticket - endpoint.online().await; let node_addr = endpoint.node_addr(); let mut short = node_addr.clone(); let ticket = NodeTicket::new(node_addr); @@ -650,8 +646,6 @@ async fn listen_unix(args: ListenUnixArgs) -> Result<()> { let socket_path = args.socket_path.clone(); let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![args.common.alpn()?]).await?; - // wait for the endpoint to figure out its address before making a ticket - endpoint.online().await; let node_addr = endpoint.node_addr(); let mut short = node_addr.clone(); let ticket = NodeTicket::new(node_addr); @@ -765,9 +759,6 @@ async fn connect_unix(args: ConnectUnixArgs) -> Result<()> { .context("unable to bind endpoint")?; tracing::info!("unix listening on {:?}", socket_path); - // Wait for our own endpoint to be ready before trying to connect. - endpoint.online().await; - // Remove existing socket file if it exists if let Err(e) = tokio::fs::remove_file(&socket_path).await { if e.kind() != io::ErrorKind::NotFound { @@ -775,10 +766,10 @@ async fn connect_unix(args: ConnectUnixArgs) -> Result<()> { } } - let addr = args.ticket.node_addr(); - tracing::info!("connecting to remote node: {:?}", addr); + let node_addr = args.ticket.node_addr(); + tracing::info!("connecting to remote node: {:?}", node_addr); let connection = endpoint - .connect(addr.clone(), &args.common.alpn()?) + .connect(node_addr.clone(), &args.common.alpn()?) .await .context("failed to connect to remote node")?; tracing::info!("connected to remote node successfully"); From 36799e564aaa28112b2cb5dab57498825d7698c4 Mon Sep 17 00:00:00 2001 From: Alexandre Fiotti Date: Fri, 3 Oct 2025 16:05:06 +0200 Subject: [PATCH 04/15] feat: relay tests added --- tests/cli.rs | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) diff --git a/tests/cli.rs b/tests/cli.rs index afe4441..467259b 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -488,3 +488,78 @@ mod unix_socket_tests { connect_stderr_thread.join().ok(); } } + +#[test] +#[ignore = "flaky"] +fn connect_listen_relay_disabled() { + let listen_to_connect = b"hello from listen"; + let connect_to_listen = b"hello from connect"; + let mut listen = duct::cmd(dumbpipe_bin(), ["listen", "--relay", "disabled"]) + .env_remove("RUST_LOG") + .stdin_bytes(listen_to_connect) + .stderr_to_stdout() + .reader() + .unwrap(); + let header = read_ascii_lines(3, &mut listen).unwrap(); + let header = String::from_utf8(header).unwrap(); + let ticket = header.split_ascii_whitespace().last().unwrap(); + let ticket = NodeTicket::from_str(ticket).unwrap(); + + let connect = duct::cmd(dumbpipe_bin(), ["connect", &ticket.to_string(), "--relay", "disabled"]) + .env_remove("RUST_LOG") + .stdin_bytes(connect_to_listen) + .stderr_null() + .stdout_capture() + .run() + .unwrap(); + + assert!(connect.status.success()); + assert_eq!(&connect.stdout, listen_to_connect); + + let mut listen_stdout = Vec::new(); + listen.read_to_end(&mut listen_stdout).unwrap(); + assert_eq!(&listen_stdout, connect_to_listen); +} + +#[test] +#[ignore = "flaky"] +fn connect_listen_relay_default() { + let listen_to_connect = b"hello from listen"; + let connect_to_listen = b"hello from connect"; + let mut listen = duct::cmd(dumbpipe_bin(), ["listen", "--relay", "default"]) + .env_remove("RUST_LOG") + .stdin_bytes(listen_to_connect) + .stderr_to_stdout() + .reader() + .unwrap(); + let header = read_ascii_lines(3, &mut listen).unwrap(); + let header = String::from_utf8(header).unwrap(); + let ticket = header.split_ascii_whitespace().last().unwrap(); + let ticket = NodeTicket::from_str(ticket).unwrap(); + + let connect = duct::cmd(dumbpipe_bin(), ["connect", &ticket.to_string(), "--relay", "default"]) + .env_remove("RUST_LOG") + .stdin_bytes(connect_to_listen) + .stderr_null() + .stdout_capture() + .run() + .unwrap(); + + assert!(connect.status.success()); + assert_eq!(&connect.stdout, listen_to_connect); + + let mut listen_stdout = Vec::new(); + listen.read_to_end(&mut listen_stdout).unwrap(); + assert_eq!(&listen_stdout, connect_to_listen); +} + +#[test] +fn relay_option_invalid() { + let output = duct::cmd(dumbpipe_bin(), ["listen", "--relay", "invalid-relay-url"]) + .env_remove("RUST_LOG") + .stderr_capture() + .stdout_capture() + .run(); + + assert!(output.is_err() || !output.unwrap().status.success()); +} From 141472e07cf6865291ecee086989ccd335633fa0 Mon Sep 17 00:00:00 2001 From: Alexandre Fiotti Date: Fri, 3 Oct 2025 16:07:33 +0200 Subject: [PATCH 05/15] feat: formated --- src/main.rs | 13 ++++++++----- tests/cli.rs | 36 +++++++++++++++++++++--------------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/main.rs b/src/main.rs index 42b4fd9..566be14 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,7 +4,10 @@ use dumbpipe::NodeTicket; use iroh::{endpoint::Connecting, Endpoint, NodeAddr, RelayMode, RelayUrl, SecretKey}; use n0_snafu::{Result, ResultExt}; use std::{ - fmt::{Display, Formatter}, io, net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}, str::FromStr + fmt::{Display, Formatter}, + io, + net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}, + str::FromStr, }; use tokio::{ io::{AsyncRead, AsyncWrite, AsyncWriteExt}, @@ -119,15 +122,15 @@ pub struct CommonArgs { /// Otherwise, it will be parsed as a hex string. #[clap(long)] pub custom_alpn: Option, - + /// The relay URL to use as a home relay, /// /// Can be set to "disabled" to disable relay servers and "custom" - /// to configure custom servers. The default is the n0 quickest responding - /// relay if the flag is not set. + /// to configure custom servers. The default is the n0 quickest responding + /// relay if the flag is not set. #[clap(long, default_value_t = RelayModeOption::Default)] pub relay: RelayModeOption, - + /// The verbosity level. Repeat to increase verbosity. #[clap(short = 'v', long, action = clap::ArgAction::Count)] pub verbose: u8, diff --git a/tests/cli.rs b/tests/cli.rs index 467259b..35e6dfa 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -505,13 +505,16 @@ fn connect_listen_relay_disabled() { let ticket = header.split_ascii_whitespace().last().unwrap(); let ticket = NodeTicket::from_str(ticket).unwrap(); - let connect = duct::cmd(dumbpipe_bin(), ["connect", &ticket.to_string(), "--relay", "disabled"]) - .env_remove("RUST_LOG") - .stdin_bytes(connect_to_listen) - .stderr_null() - .stdout_capture() - .run() - .unwrap(); + let connect = duct::cmd( + dumbpipe_bin(), + ["connect", &ticket.to_string(), "--relay", "disabled"], + ) + .env_remove("RUST_LOG") + .stdin_bytes(connect_to_listen) + .stderr_null() + .stdout_capture() + .run() + .unwrap(); assert!(connect.status.success()); assert_eq!(&connect.stdout, listen_to_connect); @@ -537,13 +540,16 @@ fn connect_listen_relay_default() { let ticket = header.split_ascii_whitespace().last().unwrap(); let ticket = NodeTicket::from_str(ticket).unwrap(); - let connect = duct::cmd(dumbpipe_bin(), ["connect", &ticket.to_string(), "--relay", "default"]) - .env_remove("RUST_LOG") - .stdin_bytes(connect_to_listen) - .stderr_null() - .stdout_capture() - .run() - .unwrap(); + let connect = duct::cmd( + dumbpipe_bin(), + ["connect", &ticket.to_string(), "--relay", "default"], + ) + .env_remove("RUST_LOG") + .stdin_bytes(connect_to_listen) + .stderr_null() + .stdout_capture() + .run() + .unwrap(); assert!(connect.status.success()); assert_eq!(&connect.stdout, listen_to_connect); @@ -560,6 +566,6 @@ fn relay_option_invalid() { .stderr_capture() .stdout_capture() .run(); - + assert!(output.is_err() || !output.unwrap().status.success()); } From b9f45e09d3e366c6ab1755a3384eca1877d64bfc Mon Sep 17 00:00:00 2001 From: Alexandre Fiotti Date: Fri, 3 Oct 2025 17:02:29 +0200 Subject: [PATCH 06/15] doc: relay section added to the readme --- README.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/README.md b/README.md index 5dd6db9..8ea6276 100644 --- a/README.md +++ b/README.md @@ -173,3 +173,38 @@ echo request1.bin | dumbpipe connect --custom-alpn utf8:/iroh-bytes/2 > if request1.bin contained a valid request for the `/iroh-bytes/2` protocol, response1.bin will now contain the response. + +## Custom Relay Configuration + +By default, dumbpipe uses iroh's automatic relay selection, which picks the fastest responding relay server from the n0 network. You can customize this behavior using the `--relay` option: + +### Disable relays entirely + +If you want to force direct connections only: + +```bash +dumbpipe listen --relay disabled +dumbpipe connect --relay disabled +``` + +This will only attempt direct peer-to-peer connections and won't fall back to relay servers. + +### Use default relays (default behavior) + +```bash +dumbpipe listen --relay default +dumbpipe connect --relay default +``` + +This is the default behavior when no `--relay` option is specified. + +### Use a custom relay server + +If you're running your own relay server or want to use a specific one: + +```bash +dumbpipe listen --relay https://your-relay-server.com +dumbpipe connect --relay https://your-relay-server.com +``` + +**Note**: Both the listener and connector should use the same relay configuration for optimal connectivity. From 49af0b91dc4ac50829341b1c94adedfbaddc6ca0 Mon Sep 17 00:00:00 2001 From: alexandre-fiotti Date: Thu, 16 Oct 2025 07:42:18 +0200 Subject: [PATCH 07/15] feat: timeout on endpoint check --- src/main.rs | 9 +++++++-- tests/cli.rs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 566be14..3631f11 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,7 @@ use std::{ fmt::{Display, Formatter}, io, net::{SocketAddr, SocketAddrV4, SocketAddrV6, ToSocketAddrs}, - str::FromStr, + str::FromStr, time::Duration, }; use tokio::{ io::{AsyncRead, AsyncWrite, AsyncWriteExt}, @@ -354,7 +354,12 @@ async fn create_endpoint( builder = builder.bind_addr_v6(addr); } let endpoint = builder.bind().await?; - endpoint.online().await; + + let _ = tokio::time::timeout(Duration::from_secs(30), async { + if !(common.relay == RelayModeOption::Disabled) { + endpoint.online().await; + } + }).await; Ok(endpoint) } diff --git a/tests/cli.rs b/tests/cli.rs index 35e6dfa..3c44621 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -19,7 +19,7 @@ fn dumbpipe_bin() -> &'static str { /// /// This assumes that the header lines are ASCII and can be parsed byte by byte. fn read_ascii_lines(mut n: usize, reader: &mut impl Read) -> io::Result> { - let mut buf = [0u8; 1]; +let mut buf = [0u8; 1]; let mut res = Vec::new(); loop { if reader.read(&mut buf)? != 1 { From 8f8b51a58cff46c749bbaf7c10a894df141c6f96 Mon Sep 17 00:00:00 2001 From: alexandre-fiotti Date: Thu, 16 Oct 2025 08:00:19 +0200 Subject: [PATCH 08/15] feat: renamed back to addr --- src/main.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main.rs b/src/main.rs index 3631f11..5fca21b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -407,9 +407,9 @@ async fn listen_stdio(args: ListenArgs) -> Result<()> { let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![args.common.alpn()?]).await?; // wait for the endpoint to figure out its address before making a ticket - let node_addr = endpoint.node_addr(); - let mut short = node_addr.clone(); - let ticket = NodeTicket::new(node_addr); + let addr = endpoint.node_addr(); + let mut short = addr.clone(); + let ticket = NodeTicket::new(addr); short.direct_addresses.clear(); let short = NodeTicket::new(short); @@ -466,10 +466,10 @@ async fn listen_stdio(args: ListenArgs) -> Result<()> { async fn connect_stdio(args: ConnectArgs) -> Result<()> { let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![]).await?; - let node_addr = endpoint.node_addr(); - let remote_node_id = node_addr.node_id; + let addr = endpoint.node_addr(); + let remote_node_id = addr.node_id; // connect to the node, try only once - let connection = endpoint.connect(node_addr.clone(), &args.common.alpn()?).await?; + let connection = endpoint.connect(addr.clone(), &args.common.alpn()?).await?; tracing::info!("connected to {}", remote_node_id); // open a bidi stream, try only once let (mut s, r) = connection.open_bi().await.e()?; @@ -540,7 +540,7 @@ async fn connect_tcp(args: ConnectTcpArgs) -> Result<()> { forward_bidi(tcp_recv, tcp_send, endpoint_recv, endpoint_send).await?; Ok::<_, n0_snafu::Error>(()) } - let node_addr = args.ticket.node_addr(); + let addr = args.ticket.node_addr(); loop { // also wait for ctrl-c here so we can use it before accepting a connection let next = tokio::select! { @@ -551,7 +551,7 @@ async fn connect_tcp(args: ConnectTcpArgs) -> Result<()> { } }; let endpoint = endpoint.clone(); - let node_addr = node_addr.clone(); + let node_addr = addr.clone(); let handshake = !args.common.is_custom_alpn(); let alpn = args.common.alpn()?; tokio::spawn(async move { @@ -574,9 +574,9 @@ async fn listen_tcp(args: ListenTcpArgs) -> Result<()> { }; let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![args.common.alpn()?]).await?; - let node_addr = endpoint.node_addr(); - let mut short = node_addr.clone(); - let ticket = NodeTicket::new(node_addr); + let addr = endpoint.node_addr(); + let mut short = addr.clone(); + let ticket = NodeTicket::new(addr); short.direct_addresses.clear(); let short = NodeTicket::new(short); @@ -654,9 +654,9 @@ async fn listen_unix(args: ListenUnixArgs) -> Result<()> { let socket_path = args.socket_path.clone(); let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![args.common.alpn()?]).await?; - let node_addr = endpoint.node_addr(); - let mut short = node_addr.clone(); - let ticket = NodeTicket::new(node_addr); + let addr = endpoint.node_addr(); + let mut short = addr.clone(); + let ticket = NodeTicket::new(addr); short.direct_addresses.clear(); let short = NodeTicket::new(short); @@ -774,10 +774,10 @@ async fn connect_unix(args: ConnectUnixArgs) -> Result<()> { } } - let node_addr = args.ticket.node_addr(); - tracing::info!("connecting to remote node: {:?}", node_addr); + let addr = args.ticket.node_addr(); + tracing::info!("connecting to remote node: {:?}", addr); let connection = endpoint - .connect(node_addr.clone(), &args.common.alpn()?) + .connect(addr.clone(), &args.common.alpn()?) .await .context("failed to connect to remote node")?; tracing::info!("connected to remote node successfully"); From 14fac80bfe3507d9ea1e1c0d49410d638a4310e0 Mon Sep 17 00:00:00 2001 From: alexandre-fiotti Date: Thu, 16 Oct 2025 08:14:22 +0200 Subject: [PATCH 09/15] feat: correct incorrect addr variable change --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 5fca21b..76ed20a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -466,7 +466,7 @@ async fn listen_stdio(args: ListenArgs) -> Result<()> { async fn connect_stdio(args: ConnectArgs) -> Result<()> { let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![]).await?; - let addr = endpoint.node_addr(); + let addr = args.ticket.node_addr(); let remote_node_id = addr.node_id; // connect to the node, try only once let connection = endpoint.connect(addr.clone(), &args.common.alpn()?).await?; From d4ed3d4ca42deec4f3b7c3e4000be39639b0015a Mon Sep 17 00:00:00 2001 From: alexandre-fiotti Date: Thu, 16 Oct 2025 08:17:19 +0200 Subject: [PATCH 10/15] feat: correct help text --- src/main.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 76ed20a..6aef1c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -125,9 +125,9 @@ pub struct CommonArgs { /// The relay URL to use as a home relay, /// - /// Can be set to "disabled" to disable relay servers and "custom" - /// to configure custom servers. The default is the n0 quickest responding - /// relay if the flag is not set. + /// Can be set to "disabled" to disable relay servers, or to a URL + /// to configure custom servers. By default, the quickest responding + /// n0 relay is used if this option is not set. #[clap(long, default_value_t = RelayModeOption::Default)] pub relay: RelayModeOption, From e02dcd3580c4be8d23a9b3940b37f17215580824 Mon Sep 17 00:00:00 2001 From: alexandre-fiotti Date: Thu, 16 Oct 2025 08:26:58 +0200 Subject: [PATCH 11/15] feat: harmonized node_addr naming --- src/main.rs | 22 +++++++++++----------- tests/cli.rs | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main.rs b/src/main.rs index 6aef1c6..241d91f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -407,9 +407,9 @@ async fn listen_stdio(args: ListenArgs) -> Result<()> { let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![args.common.alpn()?]).await?; // wait for the endpoint to figure out its address before making a ticket - let addr = endpoint.node_addr(); - let mut short = addr.clone(); - let ticket = NodeTicket::new(addr); + let node = endpoint.node_addr(); + let mut short = node.clone(); + let ticket = NodeTicket::new(node); short.direct_addresses.clear(); let short = NodeTicket::new(short); @@ -551,11 +551,11 @@ async fn connect_tcp(args: ConnectTcpArgs) -> Result<()> { } }; let endpoint = endpoint.clone(); - let node_addr = addr.clone(); + let addr = addr.clone(); let handshake = !args.common.is_custom_alpn(); let alpn = args.common.alpn()?; tokio::spawn(async move { - if let Err(cause) = handle_tcp_accept(next, node_addr, endpoint, handshake, &alpn).await { + if let Err(cause) = handle_tcp_accept(next, addr, endpoint, handshake, &alpn).await { // log error at warn level // // we should know about it, but it's not fatal @@ -574,9 +574,9 @@ async fn listen_tcp(args: ListenTcpArgs) -> Result<()> { }; let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![args.common.alpn()?]).await?; - let addr = endpoint.node_addr(); - let mut short = addr.clone(); - let ticket = NodeTicket::new(addr); + let node_addr = endpoint.node_addr(); + let mut short = node_addr.clone(); + let ticket = NodeTicket::new(node_addr); short.direct_addresses.clear(); let short = NodeTicket::new(short); @@ -654,9 +654,9 @@ async fn listen_unix(args: ListenUnixArgs) -> Result<()> { let socket_path = args.socket_path.clone(); let secret_key = get_or_create_secret()?; let endpoint = create_endpoint(secret_key, &args.common, vec![args.common.alpn()?]).await?; - let addr = endpoint.node_addr(); - let mut short = addr.clone(); - let ticket = NodeTicket::new(addr); + let node_addr = endpoint.node_addr(); + let mut short = node_addr.clone(); + let ticket = NodeTicket::new(node_addr); short.direct_addresses.clear(); let short = NodeTicket::new(short); diff --git a/tests/cli.rs b/tests/cli.rs index 3c44621..35e6dfa 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -19,7 +19,7 @@ fn dumbpipe_bin() -> &'static str { /// /// This assumes that the header lines are ASCII and can be parsed byte by byte. fn read_ascii_lines(mut n: usize, reader: &mut impl Read) -> io::Result> { -let mut buf = [0u8; 1]; + let mut buf = [0u8; 1]; let mut res = Vec::new(); loop { if reader.read(&mut buf)? != 1 { From beb254b11b87d87b35ffc2fbb31fe9d6a947b4e6 Mon Sep 17 00:00:00 2001 From: alexandre-fiotti Date: Thu, 16 Oct 2025 19:37:36 +0200 Subject: [PATCH 12/15] feat: revew timeout endpoint online implem --- src/main.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 241d91f..d06ab7d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -355,11 +355,11 @@ async fn create_endpoint( } let endpoint = builder.bind().await?; - let _ = tokio::time::timeout(Duration::from_secs(30), async { - if !(common.relay == RelayModeOption::Disabled) { + if !(common.relay == RelayModeOption::Disabled) { + let _ = tokio::time::timeout(Duration::from_secs(30), async { endpoint.online().await; - } - }).await; + }).await; + }; Ok(endpoint) } From 3509cfde93f55f03608043cc5c61a875ddc410d4 Mon Sep 17 00:00:00 2001 From: alexandre-fiotti Date: Thu, 16 Oct 2025 19:43:21 +0200 Subject: [PATCH 13/15] feat: timeout time changed --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index d06ab7d..a7cc924 100644 --- a/src/main.rs +++ b/src/main.rs @@ -356,7 +356,7 @@ async fn create_endpoint( let endpoint = builder.bind().await?; if !(common.relay == RelayModeOption::Disabled) { - let _ = tokio::time::timeout(Duration::from_secs(30), async { + let _ = tokio::time::timeout(Duration::from_secs(5), async { endpoint.online().await; }).await; }; From 400c42c6c0b9460ffd50243d4391903765532590 Mon Sep 17 00:00:00 2001 From: alexandre-fiotti Date: Thu, 16 Oct 2025 20:20:43 +0200 Subject: [PATCH 14/15] feat: future in timeout simplified --- src/main.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index a7cc924..1b52be5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -356,9 +356,7 @@ async fn create_endpoint( let endpoint = builder.bind().await?; if !(common.relay == RelayModeOption::Disabled) { - let _ = tokio::time::timeout(Duration::from_secs(5), async { - endpoint.online().await; - }).await; + let _ = tokio::time::timeout(Duration::from_secs(5), endpoint.online()).await; }; Ok(endpoint) From 1b523fe43b7682772573a79a449005ed2058d6b1 Mon Sep 17 00:00:00 2001 From: alexandre-fiotti Date: Fri, 17 Oct 2025 07:49:58 +0200 Subject: [PATCH 15/15] feat: error rising on endpoint online timeout --- src/main.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1b52be5..f8147ad 100644 --- a/src/main.rs +++ b/src/main.rs @@ -355,9 +355,10 @@ async fn create_endpoint( } let endpoint = builder.bind().await?; - if !(common.relay == RelayModeOption::Disabled) { - let _ = tokio::time::timeout(Duration::from_secs(5), endpoint.online()).await; - }; + if !(common.relay == RelayModeOption::Disabled) + && tokio::time::timeout(Duration::from_secs(5), endpoint.online()).await.is_err() { + eprintln!("Failed to initialize home relay"); + }; Ok(endpoint) }