host: emit relay-only ticket (drop direct IP candidates)

The host ticket embedded every direct IP candidate the endpoint
discovered — on this machine that was 10 addrs, 7 of them useless
Docker-bridge gateways (172.16.0.0/12) plus LAN/public v4/v6. That
bloated the ticket to ~320 chars and leaked local network topology to
whoever received it.

Keep only the endpoint id + relay URL (~140 chars). The relay
coordinates hole-punching to a direct path after connect, so peer
reachability is unchanged; the direct addrs in the ticket only ever
shaved a moment off the first connection attempt, and n0 DNS discovery
already publishes the full addr keyed by id as a backstop.

Await endpoint.online() (15s cap) before building the ticket so the
relay URL is reliably populated; a relay outage degrades to a
possibly-incomplete ticket rather than a hang.

Experimental — isolated on feat/short-ticket pending an end-to-end
cross-machine connect test before merging to main.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 16:17:38 -04:00
parent 7fa5d410f9
commit 0c9d8eb9f9
+20 -2
View File
@@ -4,8 +4,8 @@ mod serve;
mod wayland;
use anyhow::{Result, bail};
use iroh::Endpoint;
use iroh::endpoint::{Connection, presets};
use iroh::{Endpoint, EndpointAddr};
use iroh_tickets::endpoint::EndpointTicket;
use std::time::Duration;
use tokio::sync::{mpsc, oneshot};
@@ -53,8 +53,26 @@ pub async fn run(opts: HostOpts) -> Result<()> {
.bind()
.await?;
// Relay-only ticket: wait for the home relay to connect, then keep only
// the endpoint id + relay URL and drop the direct IP candidates. The relay
// coordinates hole-punching to a direct path right after connect, so this
// doesn't change whether peers can reach each other — it just keeps the
// ticket short (~140 vs ~320 chars) and stops it from leaking LAN /
// Docker-bridge addresses to whoever receives the ticket. Awaiting online()
// first guarantees the relay URL is actually present (addr() right after
// bind can return before the relay handshake completes); the 15s cap means
// a relay outage degrades to a possibly-incomplete ticket rather than a hang
// (n0 DNS discovery still resolves the id in that case).
if tokio::time::timeout(Duration::from_secs(15), endpoint.online())
.await
.is_err()
{
tracing::warn!("home relay not connected within 15s; ticket may be incomplete");
}
let addr = endpoint.addr();
let ticket = EndpointTicket::new(addr);
let relay_only =
EndpointAddr::new(addr.id).with_addrs(addr.addrs.iter().filter(|a| a.is_relay()).cloned());
let ticket = EndpointTicket::new(relay_only);
let clipboard_ok = opts.interactive && copy_to_clipboard(&ticket.to_string());
print_host_banner(&ticket, display, &opts, &resolution, clipboard_ok);