From c91398c4fe24a2b6f6c537a2e51caed455dca115 Mon Sep 17 00:00:00 2001 From: Frando Date: Wed, 10 Jun 2026 13:52:57 +0200 Subject: [PATCH 01/10] update docs for 1.0-rc.1 --- concepts/endpoints.mdx | 6 +-- concepts/tickets.mdx | 2 +- connecting/endpoint-hooks.mdx | 68 +++++++++++++++---------- connecting/local-discovery.mdx | 31 +++++++----- deployment/other-languages.mdx | 7 +-- deployment/wasm-browser-support.mdx | 9 ++-- examples/chat.mdx | 8 +-- protocols/writing-a-protocol.mdx | 78 +++++++++++++---------------- snippets/relay-endpoint-config.mdx | 8 +-- transports/nym.mdx | 4 +- transports/tor.mdx | 4 +- 11 files changed, 114 insertions(+), 111 deletions(-) diff --git a/concepts/endpoints.mdx b/concepts/endpoints.mdx index 9df5563..864f340 100644 --- a/concepts/endpoints.mdx +++ b/concepts/endpoints.mdx @@ -52,7 +52,7 @@ Endpoint Addresses or [`EndpointAddrs`](https://docs.rs/iroh/latest/iroh/struct. ```rust pub struct EndpointAddr { - pub id: PublicKey, + pub id: EndpointId, pub addrs: BTreeSet, } ``` @@ -73,8 +73,8 @@ default: use iroh::{Endpoint, endpoint::presets}; // enables dialing by EndpointAddrs that only have EndpointIDs by default: -let ep = Endpoint::builder() - .bind(presets::N0) +let ep = Endpoint::builder(presets::N0) + .bind() .await?; ``` diff --git a/concepts/tickets.mdx b/concepts/tickets.mdx index abd5107..8910996 100644 --- a/concepts/tickets.mdx +++ b/concepts/tickets.mdx @@ -18,7 +18,7 @@ Here's an example ticket: docaaacarwhmusoqf362j3jpzrehzkw3bqamcp2mmbhn3fmag3mzzfjp4beahj2v7aezhojvfqi5wltr4vxymgzqnctryyup327ct7iy4s5noxy6aaa ``` -Yes, they're long. But they pack a lot of information: an endpoint address (node ID, relay URL, and direct addresses) plus optional application-specific data like a document ID or blob hash. +Yes, they're long. But they pack a lot of information: an endpoint address (endpoint ID, relay URL, and direct addresses) plus optional application-specific data like a document ID or blob hash. The key insight: instead of making users copy and paste just a hash and then figure out where to find the data, tickets combine *what* you want with *where to get it from*. This gives iroh everything needed to establish a connection immediately. diff --git a/connecting/endpoint-hooks.mdx b/connecting/endpoint-hooks.mdx index 769c9e5..457f56f 100644 --- a/connecting/endpoint-hooks.mdx +++ b/connecting/endpoint-hooks.mdx @@ -17,7 +17,7 @@ Hooks run at two points: 1. **Before an outgoing connection starts**. No packets have been sent yet. 2. **After the QUIC/TLS handshake completes** for both incoming and outgoing connections. The remote endpoint ID, ALPN, and other metadata are available, but no application data has been sent or received yet. -Hooks are registered with `Endpoint::builder().hooks(...)`. If multiple hooks are installed, they run in the order they were added, and a rejection from any hook short-circuits the rest. +Hooks are registered with `Endpoint::builder(preset).hooks(...)`. If multiple hooks are installed, they run in the order they were added, and a rejection from any hook short-circuits the rest. Note that hooks cannot *use* connections, they can only *observe* or *reject* them. This is an important separation of concerns: If hooks were allowed to use the connections in any way, they could interfere with the actual protocols running within these connections. Hooks can, however, *reject* connections before they are passed on to protocol handlers. This makes it possible to implement custom authentication schemes with hooks that work without any support from the protocols running in these connections. @@ -25,13 +25,14 @@ Note that hooks cannot *use* connections, they can only *observe* or *reject* th ## Example: Observing connection events -This example shows a minimal hook implementation that logs the context available at each stage. It does not alter behavior, only observes. +This example shows a minimal hook implementation that logs the context available at each stage and spawns a task to watch the connection's network paths. It does not alter behavior, only observes. ```rust use iroh::{ - Endpoint, EndpointAddr, Watcher, - endpoint::{AfterHandshakeOutcome, BeforeConnectOutcome, ConnectionInfo, EndpointHooks}, + Endpoint, EndpointAddr, + endpoint::{AfterHandshakeOutcome, BeforeConnectOutcome, Connection, EndpointHooks, presets}, }; +use n0_future::StreamExt; use tracing::info; /// Our hooks instance. @@ -54,20 +55,27 @@ impl EndpointHooks for LogHooks { // Runs after the handshake for both incoming and outgoing connections. // - // `ConnectionInfo` gives information about a connection, but doesn't allow to use it otherwise. - async fn after_handshake(&self, conn: &ConnectionInfo) -> AfterHandshakeOutcome { - // This tells us whether `conn` is an incoming or outgoing connection. - let side = conn.side(); + // The hook receives the `Connection` by reference. We shouldn't clone the connection here, + // because this would prevent "close-on-drop" semantics that are often expected in other + // code paths. Instead, we use a weak handle and a stream of path events. + async fn after_handshake(&self, conn: &Connection) -> AfterHandshakeOutcome { let remote = conn.remote_id().fmt_short(); - - info!(%remote, alpn=?conn.alpn(), ?side, "connection established"); - - // We can spawn a task to observe network path changes for this connection. - let mut path_updates = conn.paths(); + info!(%remote, alpn=?conn.alpn(), side=?conn.side(), "connection established"); + let mut path_events = conn.path_events(); + let handle = conn.weak_handle(); tokio::spawn(async move { - while let Ok(paths) = path_updates.updated().await { - info!(%remote, ?paths, "paths updated"); + while let Some(event) = path_events.next().await { + info!(%remote, ?event, "path event"); + // Upgrade the weak handle briefly to inspect the connection or path state. + if let Some(conn) = handle.upgrade() { + for path in conn.paths().iter() { + // Inspect path state or log as desired. + info!(%remote, dst=?path.remote_addr(), rx=path.stats().udp_rx.bytes, "path info"); + } + } } + // The path event stream closes once the connection closes. + info!(%remote, "connection closed"); }); AfterHandshakeOutcome::Accept @@ -75,10 +83,13 @@ impl EndpointHooks for LogHooks { } #[tokio::main] -async fn main() -> n0_error::Result<()> { +async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); // Install the hooks on our endpoint. - let _endpoint = Endpoint::builder().hooks(LogHooks).bind().await?; + let _endpoint = Endpoint::builder(presets::N0) + .hooks(LogHooks) + .bind() + .await?; // Use `endpoint` normally... Ok(()) } @@ -89,16 +100,16 @@ async fn main() -> n0_error::Result<()> { Hooks can be used to enforce policy. If a hook returns a rejection result, the connection is immediately aborted. The example below rejects all incoming connections after the handshake. Outgoing connections will still dial. -In real applications, you would inspect `ConnectionInfo` and reject connections by checking the connection's remote id or ALPN against authentication state in your app. +In real applications, you would inspect the `Connection` and reject connections by checking the connection's remote id or ALPN against authentication state in your app. ```rust -use iroh::endpoint::{AfterHandshakeOutcome, ConnectionInfo, Endpoint, EndpointHooks, Side}; +use iroh::endpoint::{AfterHandshakeOutcome, Connection, Endpoint, EndpointHooks, Side, presets}; #[derive(Debug)] struct RejectIncomingHook; impl EndpointHooks for RejectIncomingHook { - async fn after_handshake(&self, conn: &ConnectionInfo) -> AfterHandshakeOutcome { + async fn after_handshake(&self, conn: &Connection) -> AfterHandshakeOutcome { // Unconditionally reject all incoming connections. // In actual apps, you could conditionally allow or accept by checking the connection's // ALPN and remote id. @@ -114,9 +125,12 @@ impl EndpointHooks for RejectIncomingHook { } #[tokio::main] -async fn main() -> n0_error::Result<()> { +async fn main() -> anyhow::Result<()> { tracing_subscriber::fmt::init(); - let _endpoint = Endpoint::builder().hooks(RejectIncomingHook).bind().await?; + let _endpoint = Endpoint::builder(presets::N0) + .hooks(RejectIncomingHook) + .bind() + .await?; Ok(()) } ``` @@ -125,7 +139,7 @@ async fn main() -> n0_error::Result<()> { There are a few fully-featured examples for using hooks in the iroh repository. -### [Authentication layer](https://github.com/n0-computer/iroh/blob/feat-multipath/iroh/examples/auth-hook.rs) +### [Authentication layer](https://github.com/n0-computer/iroh/blob/main/iroh/examples/auth-hook.rs) Demonstrates how to build an authentication flow on top of hooks. This pattern keeps authentication separate from your application protocols while still integrating cleanly with iroh’s connection lifecycle. @@ -134,18 +148,18 @@ Demonstrates how to build an authentication flow on top of hooks. This pattern k * Incoming connections are checked against a set of authorized remote ids. * If an incoming connection comes from a peer that hasn't successfully performed pre-auth, the connection is rejected. -### [Monitoring connection and path events](https://github.com/n0-computer/iroh/blob/feat-multipath/iroh/examples/monitor-connections.rs) +### [Monitoring connection and path events](https://github.com/n0-computer/iroh/blob/main/iroh/examples/monitor-connections.rs) This example demonstrates how hooks can feed information to external tasks, giving you flexible observability. -* A hook sends each `ConnectionInfo` to a monitoring task. +* A hook forwards a weak handle for each connection to a monitoring task. * The monitor can record events and stats. -### [Aggregating information about remote endpoints](https://github.com/n0-computer/iroh/blob/feat-multipath/iroh/examples/remote-info.rs) +### [Aggregating information about remote endpoints](https://github.com/n0-computer/iroh/blob/main/iroh/examples/remote-info.rs) This example implements a `RemoteMap` that tracks and aggregates information about all remotes our endpoint knows about. This can be useful if your app needs to choose between remotes, or for building diagnostic tools. -* A hook forwards `ConnectionInfo` updates into a worker task. +* A hook forwards a weak handle for each connection into a worker task. * The worker maintains a map of all remotes with counts of active connections and observed statistics (e.g. latency/RTT, whether relay/IP paths were used, etc). * The `RemoteMap` exposes a simple API to query all known remotes and their aggregate metrics. diff --git a/connecting/local-discovery.mdx b/connecting/local-discovery.mdx index 4f39b9f..a125c1e 100644 --- a/connecting/local-discovery.mdx +++ b/connecting/local-discovery.mdx @@ -14,26 +14,31 @@ networks. ## Usage -Local Discovery is _not_ enabled by default, and must be enabled explicitly. -You'll need to add the `discovery-local-network` feature flag to your -`Cargo.toml` to use it. +mDNS discovery is not enabled by default. It lives in the separate +[`iroh-mdns-address-lookup`](https://crates.io/crates/iroh-mdns-address-lookup) +crate, which you add alongside `iroh`. ```toml [dependencies] -# Make sure to use the most recent version here instead of nn. (at the time of writing: 0.32) -iroh = { version = "0.nn", features = ["address-lookup-mdns"] } +iroh = "1.0.0-rc.1" +iroh-mdns-address-lookup = "0.3" ``` -Then configure your endpoint to use local discovery concurrently with the default DNS discovery: +Add the `MdnsAddressLookup` to the endpoint with `Endpoint::builder`. We run it next to the default DNS discovery, so connections still work when the two endpoints are not on the same local network. ```rust use iroh::{Endpoint, endpoint::presets}; - -let mdns = iroh::address_lookup::mdns::MdnsAddressLookup::builder(); -let ep = Endpoint::builder() - .address_lookup(mdns) - .bind(presets::N0) - .await?; +use iroh_mdns_address_lookup::MdnsAddressLookup; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let endpoint = Endpoint::builder(presets::N0) + .address_lookup(MdnsAddressLookup::builder()) + .bind() + .await?; + // your code here + Ok(()) +} ``` -For more information on how mDNS discovery works, see the [mDNS documentation](https://docs.rs/iroh/latest/iroh/address_lookup/mdns/index.html). +For more on how mDNS discovery works, see the [`iroh-mdns-address-lookup` documentation](https://docs.rs/iroh-mdns-address-lookup). diff --git a/deployment/other-languages.mdx b/deployment/other-languages.mdx index de4e5e7..1cd20be 100644 --- a/deployment/other-languages.mdx +++ b/deployment/other-languages.mdx @@ -77,11 +77,8 @@ with `serverCertificateHashes`, or WebRTC. We may expand iroh's browser support to make use of these to try to generate direct connections even when a browser node is involved in the connection. -**`iroh` crate features**: As of iroh version 0.33, you need to disable all optional features on iroh for the Wasm build to succeed. -To do so, depend on iroh via `iroh = { version = "0.33", default-features = false }`. -This will install a version of iroh with default features, except it doesn't track `metrics` locally. -We'll get rid of this limitation very soon (likely with iroh version 0.34). -Non-default features like `discovery-local-network` or `discovery-dht` will likely never be available in browsers, unless browser APIs making them possible are added. +**`iroh` crate features**: you need to disable iroh's default features for the Wasm build to succeed. +To do so, depend on iroh via `iroh = { version = "1.0.0-rc.1", default-features = false }`. **npm package**: Currently we don't bundle iroh's Wasm build as an NPM package. There is no technical limitation for this: You could build this today! diff --git a/deployment/wasm-browser-support.mdx b/deployment/wasm-browser-support.mdx index 4016df2..4870782 100644 --- a/deployment/wasm-browser-support.mdx +++ b/deployment/wasm-browser-support.mdx @@ -35,11 +35,9 @@ node is involved in the connection. ### `iroh` crate features -As of iroh version 0.33, you need to disable all optional features on iroh for the Wasm build to succeed. -To do so, depend on iroh via `iroh = { version = "0.33", default-features = false }`. -This will install a version of iroh with default features, except it doesn't track `metrics` locally. -We'll get rid of this limitation very soon (likely with iroh version 0.34). -Non-default features like `discovery-local-network` or `discovery-dht` will likely never be available in browsers, unless browser APIs making them possible are added. +You need to disable iroh's default features for the Wasm build to succeed. +To do so, depend on iroh via `iroh = { version = "1.0.0-rc.1", default-features = false }`. +This drops the `metrics` feature, so iroh no longer tracks metrics locally. ### npm package @@ -71,4 +69,3 @@ Otherwise feel free to open a discussion or a thread on that discussion with you We're expanding browser support from the iroh crate to our off-the-shelf protocols. [iroh-gossip](https://www.iroh.computer/proto/iroh-gossip) already supports being built for browsers starting with version 0.33. - diff --git a/examples/chat.mdx b/examples/chat.mdx index a549769..e8a398d 100644 --- a/examples/chat.mdx +++ b/examples/chat.mdx @@ -66,13 +66,13 @@ async fn main() -> Result<()> { // This allows you to // dial by `EndpointId`, and allows you to be // dialed by `EndpointId`. - let endpoint = Endpoint::builder() + let endpoint = Endpoint::builder(presets::N0) // Pass in your secret key. If you don't pass // in a secret key a new one will be generated // for you each time. .secret_key(secret_key) // Bind the endpoint to the socket. - .bind(presets::N0) + .bind() .await?; println!("> our endpoint id: {}", endpoint.id()); @@ -104,8 +104,8 @@ async fn main() -> Result<()> { // We've removed the `SecretKey::generate` method. // The `Endpoint` will generate a `SecretKey` for // you under the hood if you don't supply one. - let endpoint = Endpoint::builder() - .bind(presets::N0) + let endpoint = Endpoint::builder(presets::N0) + .bind() .await?; println!("> our endpoint id: {}", endpoint.id()); diff --git a/protocols/writing-a-protocol.mdx b/protocols/writing-a-protocol.mdx index dad9a1e..73e0128 100644 --- a/protocols/writing-a-protocol.mdx +++ b/protocols/writing-a-protocol.mdx @@ -26,7 +26,7 @@ The easiest way to start listening for incoming connections is by using iroh's [ ```rs async fn start_accept_side() -> anyhow::Result { - let endpoint = iroh::Endpoint::.bind().await?; + let endpoint = iroh::Endpoint::bind(iroh::endpoint::presets::N0).await?; let router = iroh::protocol::Router::builder(endpoint) .spawn(); @@ -64,30 +64,22 @@ impl iroh::protocol::ProtocolHandler for Echo { /// /// The returned future runs on a newly spawned tokio task, so it can run as long as /// the connection lasts without blocking other connections. - fn accept(&self, connection: iroh::Connection) -> n0_future::boxed::BoxFuture> { - Box::pin(async move { - // TODO! - - Ok(()) - }) + async fn accept(&self, connection: iroh::endpoint::Connection) -> Result<(), iroh::protocol::AcceptError> { + // TODO! + Ok(()) } } ``` - -We're using the `n0-future` crate for the return type of `accept` here. -This is just a shorthand for `std::pin::Pin> + Send + 'static>>` (which is a mouthful!). -This shorthand is also provided by `futures-lite`, `futures-util` and many more. -We simply use `n0-future` as it re-exports all the crates we've vetted and commonly use at number 0. - - +`ProtocolHandler::accept` is an `async fn` that returns `Result<(), AcceptError>`. The errors you hit while driving a connection (stream, read, and write errors) all convert into `AcceptError`, so you can use `?` throughout. + The `accept` function is going to get called once an incoming connection with the correct ALPN is established. Now, we can modify our router so it handles incoming connections with our newly created custom protocol: ```rs async fn start_accept_side() -> anyhow::Result { - let endpoint = iroh::Endpoint::bind(iroh::presets::N0).await?; + let endpoint = iroh::Endpoint::bind(iroh::endpoint::presets::N0).await?; let router = iroh::protocol::Router::builder(endpoint) .accept(ALPN, Echo) // This makes the router handle incoming connections with our ALPN via Echo::accept! @@ -101,39 +93,37 @@ async fn start_accept_side() -> anyhow::Result { ## Implementing the Accepting Side At the moment, the `Echo::accept` function is still stubbed out. -The way it is currently implemented, it would drop the `iroh::Connection` immediately, causing the connection to close. +The way it is currently implemented, it would drop the `Connection` immediately, causing the connection to close. Instead, we need to hold on to either the connection or one of its streams for as long as we want to interact with it. -We'll do that by moving the connection to the future we return from `Echo::accept` and handling the protocol logic within that future. +We'll do that by handling the protocol logic directly in the `Echo::accept` method, which keeps the connection alive for as long as it runs. This implements a simple [request-response pattern](/protocols/using-quic#request-and-response): ```rs impl ProtocolHandler for Echo { - fn accept(&self, connection: Connection) -> BoxFuture> { - Box::pin(async move { - // We can get the remote's endpoint id from the connection. - let endpoint_id = connection.remote_id()?; - println!("accepted connection from {endpoint_id}"); - - // Our protocol is a simple request-response protocol, so we expect the - // connecting peer to open a single bi-directional stream. - let (mut send, mut recv) = connection.accept_bi().await?; - - // Echo any bytes received back directly. - // This will keep copying until the sender signals the end of data on the stream. - let bytes_sent = tokio::io::copy(&mut recv, &mut send).await?; - println!("Copied over {bytes_sent} byte(s)"); - - // By calling `finish` on the send stream we signal that we will not send anything - // further, which makes the receive stream on the other end terminate. - send.finish()?; - - // Wait until the remote closes the connection, which it does once it - // received the response. - connection.closed().await; - - Ok(()) - }) + async fn accept(&self, connection: Connection) -> Result<(), AcceptError> { + // We can get the remote's endpoint id from the connection. + let endpoint_id = connection.remote_id(); + println!("accepted connection from {endpoint_id}"); + + // Our protocol is a simple request-response protocol, so we expect the + // connecting peer to open a single bi-directional stream. + let (mut send, mut recv) = connection.accept_bi().await?; + + // Echo any bytes received back directly. + // This will keep copying until the sender signals the end of data on the stream. + let bytes_sent = tokio::io::copy(&mut recv, &mut send).await?; + println!("Copied over {bytes_sent} byte(s)"); + + // By calling `finish` on the send stream we signal that we will not send anything + // further, which makes the receive stream on the other end terminate. + send.finish()?; + + // Wait until the remote closes the connection, which it does once it + // received the response. + connection.closed().await; + + Ok(()) } } ``` @@ -164,7 +154,7 @@ This follows the [request-response pattern](/protocols/using-quic#request-and-re ```rs async fn connect_side(addr: EndpointAddr) -> Result<()> { - let endpoint = Endpoint::bind(iroh::presets::N0).await?; + let endpoint = Endpoint::bind(iroh::endpoint::presets::N0).await?; // Open a connection to the accepting endpoint let conn = endpoint.connect(addr, ALPN).await?; @@ -250,7 +240,7 @@ Putting it all together, you only need to change the `start_accept_side` functio ```rs async fn start_accept_side() -> anyhow::Result { - let endpoint = Endpoint::builder() + let endpoint = Endpoint::builder(iroh::endpoint::presets::N0) // The accept side needs to opt-in to the protocols it accepts, // as any connection attempts that can't be found with a matching ALPN // will be rejected. diff --git a/snippets/relay-endpoint-config.mdx b/snippets/relay-endpoint-config.mdx index 1cccd22..1f7fdd6 100644 --- a/snippets/relay-endpoint-config.mdx +++ b/snippets/relay-endpoint-config.mdx @@ -1,14 +1,14 @@ ```rust -use iroh::Endpoint; -use iroh::relay::RelayUrl; +use iroh::{Endpoint, RelayMap, RelayMode, RelayUrl, endpoint::presets}; #[tokio::main] async fn main() -> anyhow::Result<()> { let relay_url1: RelayUrl = "YOUR_RELAY_URL_US".parse()?; let relay_url2: RelayUrl = "YOUR_RELAY_URL_EU".parse()?; + let relay_map = RelayMap::from_iter([relay_url1, relay_url2]); - let endpoint = Endpoint::builder() - .relay_mode(iroh::endpoint::RelayMode::Custom(vec![relay_url1, relay_url2])) + let endpoint = Endpoint::builder(presets::N0) + .relay_mode(RelayMode::Custom(relay_map)) .bind() .await?; diff --git a/transports/nym.mdx b/transports/nym.mdx index e19a80f..9c394ef 100644 --- a/transports/nym.mdx +++ b/transports/nym.mdx @@ -27,10 +27,10 @@ use nym_sdk::mixnet::MixnetClient; let nym_client = MixnetClient::connect_new().await?; let transport = Arc::new(NymUserTransport::new(nym_client)); -let endpoint = Endpoint::builder() +let endpoint = Endpoint::builder(presets::N0) .clear_ip_transports() .add_custom_transport(transport) - .bind(presets::N0) + .bind() .await?; ``` diff --git a/transports/tor.mdx b/transports/tor.mdx index 0cc8709..91cad57 100644 --- a/transports/tor.mdx +++ b/transports/tor.mdx @@ -37,10 +37,10 @@ let transport = TorCustomTransport::builder() .build(secret_key.clone()) .await?; -let endpoint = Endpoint::builder() +let endpoint = Endpoint::builder(presets::N0) .secret_key(secret_key) .preset(transport.preset()) - .bind(presets::N0) + .bind() .await?; ``` From dda210f2b8517b2ceb1132a62f571844cfc3bc77 Mon Sep 17 00:00:00 2001 From: Frando Date: Wed, 10 Jun 2026 14:11:24 +0200 Subject: [PATCH 02/10] improve dns docs --- concepts/discovery.mdx | 24 +++++---- connecting/dht-discovery.mdx | 47 +++++++++++++----- connecting/dns-discovery.mdx | 95 ++++++++++++++++++++++-------------- 3 files changed, 104 insertions(+), 62 deletions(-) diff --git a/concepts/discovery.mdx b/concepts/discovery.mdx index 1ad32fc..7c21ee6 100644 --- a/concepts/discovery.mdx +++ b/concepts/discovery.mdx @@ -1,28 +1,26 @@ --- -title: "Discovery" +title: "Address Lookup" --- -Discovery is the glue that connects an [Endpoint](/concepts/endpoints#endpoint-identifiers) to something we can dial. Discovery services resolve EndpointIDs to either a home Relay URL or direct-dialing information. +Address Lookup is the glue that connects an [Endpoint](/concepts/endpoints#endpoint-identifiers) to something we can dial. Address Lookup services resolve EndpointIDs to either a home Relay URL or direct-dialing information. - More details on discovery in the address lookup module [documentation](https://docs.rs/iroh/latest/iroh/address_lookup/index.html) + More details can be found in the address lookup module [documentation](https://docs.rs/iroh/latest/iroh/address_lookup/index.html) -Endpoint discovery is an automated system for an [Endpoint](/concepts/endpoints) to retrieve addressing information. Each iroh endpoint will automatically publish their own addressing information with configured discovery services. Usually this means publishing which [Home Relay](/concepts/relays) an endpoint is findable at, but they could also publish their direct addresses. +Address lookup is an automated system for an [Endpoint](/concepts/endpoints) to retrieve addressing information. Each iroh endpoint will automatically publish their own addressing information with configured address lookup services. Usually this means publishing which [Home Relay](/concepts/relays) an endpoint is findable at, but they could also publish their direct addresses. -## Discovery Services - -There are four different implementations of the discovery service in iroh. **By default, iroh uses DNS discovery** to resolve EndpointIDs to addresses. Local and DHT discovery are **disabled by default** and must be explicitly configured if you want to use them. +## Address Lookup Services +iroh ships several address lookup implementations. **By default, iroh uses DNS/Pkarr address lookup** to resolve EndpointIDs to addresses. DNS/Pkarr adress lookup publishes signed records to a server and resolves them via DNS. | Name | Description | Default | | --- | --- | --- | -| [DNS](#endpoint-discovery-via-dns) | uses a custom Domain Name System server | ✅ Enabled | +| [DNS/Pkarr](#endpoint-discovery-via-dns) | publishes signed records to a server and resolves them over DNS | ✅ Enabled | | [Local](#local-discovery) | uses an mDNS-like system to find endpoints on the local network | ❌ Disabled | -| [Pkarr](#endpoint-announces-via-pkarr) | uses Pkarr Servers over HTTP | ✅ Enabled (via DNS) | -| [DHT](#dht-discovery) | uses the BitTorrent Mainline DHT | ❌ Disabled | +| [DHT](#dht-discovery) | publishes the same signed records to the BitTorrent Mainline DHT | ❌ Disabled | ### The motivation @@ -57,7 +55,7 @@ discovery in the future, that needed to work in tandem with whatever federated solution we came up with. Second, it was very important for us as an organization that we do not invent -protocols and specs unnecessarily. Can we instead leverage standards that have +protocols and specs unnecessarily. Can we instead build on standards that have stood the test of time (and scrutiny) in novel ways to solve our problem? It turns out, we can! And we can do it using one of the oldest and most dependable technologies we have on the internet: DNS. Using the DNS standard along side @@ -126,8 +124,8 @@ Those packets are published to a Pkarr relay server, which is a HTTP service handling PUT requests with the signed packets. iroh's Pkarr server is [`iroh-dns-server`](https://crates.io/crates/iroh-dns-server), which serves the received records over DNS. Pkarr packets can also be published onto the -bittorrent mainline DHT, as specified by Pkarr (this is not yet supported in -iroh natively, see below for our plans). +BitTorrent Mainline DHT, as specified by Pkarr. iroh supports this through +[DHT discovery](/connecting/dht-discovery), which is opt-in. DNS servers that support this spec will receive these Pkarr signed packets, check their signature and format validity, and then serve each contained record, diff --git a/connecting/dht-discovery.mdx b/connecting/dht-discovery.mdx index 8ea2c14..bd8ab5d 100644 --- a/connecting/dht-discovery.mdx +++ b/connecting/dht-discovery.mdx @@ -2,25 +2,46 @@ title: "DHT" --- -Discovery via DHT in iroh uses the [BitTorrent Mainline](https://en.wikipedia.org/wiki/Mainline_DHT) distributed hash table (DHT) to publish & resolve EndpointIds. +DHT discovery publishes and resolves endpoint records on the +[BitTorrent Mainline](https://en.wikipedia.org/wiki/Mainline_DHT) distributed hash +table. The records are the same signed records that +[DNS discovery](/connecting/dns-discovery) uses. The difference is where they +are stored. DNS discovery publishes them to a hosted server and resolves over DNS, +while DHT discovery puts them on the BitTorrent Mainline DHT. That removes the +dependency on a hosted server: any endpoint can publish and resolve without a +central party, at the cost of slower lookups than DNS. -DHT Discovery is _not_ enabled by default, and must be enabled by the user. You'll need to add the `discovery-pkarr-dht` feature flag to your `Cargo.toml` to use it. +DHT discovery is not enabled by default. It lives in the separate +[`iroh-mainline-address-lookup`](https://crates.io/crates/iroh-mainline-address-lookup) +crate, which you add alongside `iroh`. ```toml [dependencies] -# Make sure to use the most recent version here instead of nn. (at the time of writing: 0.32) -iroh = { version = "0.nn", features = ["discovery-pkarr-dht"] } +iroh = "1.0.0-rc.1" +iroh-mainline-address-lookup = "0.3" ``` -Then configure your endpoint to use DHT discovery concurrently with mDNS discovery, but not with the default DNS discovery, use the `Endpoint::empty_builder` method. This requires specifying a `RelayMode`, which in this example we will keep default. +Add the `DhtAddressLookup` to the endpoint with `Endpoint::builder`. You can run it +next to the default DNS discovery, or build from `presets::Minimal` to use only the +DHT. ```rust -use iroh::Endpoint; - -let dht_discovery = iroh::discovery::dht::DhtDiscovery::builder(); -let mdns = iroh::discovery::mdns::MdnsDiscovery::builder(); -let ep = Endpoint::empty_builder(iroh::RelayMode::Default) - .discovery(dnt_discovery) - .bind() - .await?; +use iroh::{Endpoint, endpoint::presets}; +use iroh_mainline_address_lookup::DhtAddressLookup; + +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let endpoint = Endpoint::builder(presets::N0) + .address_lookup(DhtAddressLookup::builder()) + .bind() + .await?; + // your code here + Ok(()) +} ``` + +DHT address lookup also combines well with [mDNS address lookup](/connecting/local-discovery) +for both global and local discovery without depending on centralized infrastructure. + +For the record format shared with DNS discovery, see the +[Discovery concept page](/concepts/discovery). diff --git a/connecting/dns-discovery.mdx b/connecting/dns-discovery.mdx index 84c83d4..5ff411f 100644 --- a/connecting/dns-discovery.mdx +++ b/connecting/dns-discovery.mdx @@ -2,27 +2,31 @@ title: "DNS" --- -By Default, iroh uses the DNS discovery system to find other peers with a given EndpointId. +By default, iroh uses DNS address lookup to find other endpoints by their `EndpointId`. -iroh will use a standard DNS server to publish and resolve EndpointIds to their -home relay addresses. This allows any iroh endpoint to be discoverable globally, -as long as they are connected to the same relay that is reachable from the public -internet. +An endpoint publishes a signed record that maps its `EndpointId` to its home relay +URL (and optionally its direct addresses), and resolves the same kind of record for +endpoints it wants to dial. -## Using DNS Discovery +## How records are published and resolved -DNS discovery is the default discovery mechanism in iroh, so you don't need to -do anything special to enable it. +Each endpoint creates a set of records with its adressing information, and puts it into a signed [Pkarr](https://pkarr.org) packet. The packet is signed by the endpoint's secret key. +The endpoint publishes this packet to a DNS/Pkarr server via HTTP. +Other endpoints can then resolve these packets either via DNS queries or via HTTP. -[Number 0](https://n0.computer) provides a set of public DNS servers that are +[Number 0](https://n0.computer) provides a set of public DNS/Pkarr servers that are free to use, and are configured by default. You're more than welcome to run production systems using the public relays if you find performance acceptable. The public servers do rate-limit traffic, there is no guaranteed uptime. - If you need more capacity or uptime guarantees or SLAs, you can run your own DNS server, or [contact us about hosted DNS options](https://cal.com/team/number-0/n0-protocol-services?overlayCalendar=true). +## Using DNS address lookup + +DNS address lookup is part of the `presets::N0` defaults, so you do not need to enable +it explicitly. The `N0` preset adds a publisher and a resolver, both pointed at the +n0-hosted server at `dns.iroh.link`. ```rust use iroh::{Endpoint, endpoint::presets}; @@ -41,42 +45,61 @@ async fn main() -> anyhow::Result<()> { } ``` -To discover other endpoints over DNS, you can use an EndpointId or a [Ticket](/concepts/tickets) that contains -an EndpointId. - -## Use your own DNS Server - -By default, iroh will look up the endpoint on a public shared instance of the -DNS discovery server. If you'd like to run your own private DNS discovery server -for more guaranteed privacy and uptime guarantees, you can configure iroh to use -it. - +To discover another endpoint over DNS you need its `EndpointId`, either directly +or from a [ticket](/concepts/tickets) that contains one. Resolution then happens +automatically when you connect. -Two nodes must connect to the same DNS discovery server to find each other using -DNS discovery. +## Use your own server -TODO: rust code for custom dns server - -## Disable DNS Discovery - -DNS discovery is opt-out, so if you don't want your endpoint to be discoverable -via DNS, you can disable it by using the `Endpoint::empty_builder` method -instead of `Endpoint::builder`. +Two endpoints must publish to and resolve from the same server to find each other. +To use your own deployment of +[`iroh-dns-server`](https://crates.io/crates/iroh-dns-server), build the endpoint +from `presets::Minimal` (which adds no address lookup) and configure the publisher and +resolver yourself. +`PkarrPublisher::builder` takes the relay URL to PUT records to. +`DnsAddressLookup::builder` takes the origin domain to query under. ```rust -use iroh::Endpoint; +use iroh::{ + Endpoint, + address_lookup::{DnsAddressLookup, PkarrPublisher}, + endpoint::presets, +}; +use url::Url; + #[tokio::main] async fn main() -> anyhow::Result<()> { - let endpoint = Endpoint::empty_builder().bind().await?; - // your code here - Ok(()) + let pkarr_relay: Url = "https://my-dns-server.example/pkarr".parse()?; + let origin_domain = "my-dns-server.example".to_string(); + + let endpoint = Endpoint::builder(presets::Minimal) + .address_lookup(PkarrPublisher::builder(pkarr_relay)) + .address_lookup(DnsAddressLookup::builder(origin_domain)) + .bind() + .await?; + // your code here + Ok(()) } -``` +``` -## Important notes +By default the `PkarrPublisher` publishes only the home relay URL, not direct IP +addresses. To publish addresses as well, set an `AddrFilter` on the builder with +`PkarrPublisher::builder(url).addr_filter(AddrFilter::unfiltered())`. +## Disable DNS address lookup +DNS address lookup is optional. To build an endpoint without it, use `presets::Minimal` +instead of `presets::N0`. `Minimal` sets up a crypto provider but adds no +address lookup and no relays, so the endpoint is not published anywhere. +```rust +use iroh::{Endpoint, endpoint::presets}; -For more information on how DNS discovery works, see the [Discovery](/concepts/discovery) concept page. +#[tokio::main] +async fn main() -> anyhow::Result<()> { + let endpoint = Endpoint::builder(presets::Minimal).bind().await?; + // your code here + Ok(()) +} +``` From 14d31b81f8461b5a456c0d3fa61b462a783057f0 Mon Sep 17 00:00:00 2001 From: Frando Date: Wed, 10 Jun 2026 14:32:18 +0200 Subject: [PATCH 03/10] discovery -> address lookup --- about/faq.mdx | 33 ++++++++++--------- .../{discovery.mdx => address-lookup.mdx} | 26 +++++++-------- concepts/endpoints.mdx | 22 ++++++------- concepts/tickets.mdx | 2 +- connect-two-endpoints.mdx | 2 +- connecting/creating-endpoint.mdx | 4 +-- ...t-discovery.mdx => dht-address-lookup.mdx} | 20 +++++------ ...s-discovery.mdx => dns-address-lookup.mdx} | 2 +- connecting/gossip.mdx | 4 +-- ...discovery.mdx => local-address-lookup.mdx} | 10 +++--- deployment/dedicated-infrastructure.mdx | 2 +- docs.json | 28 +++++++++++++--- examples/chat.mdx | 4 +-- iroh-services/metrics/glossary.mdx | 6 ++-- protocols/automerge.mdx | 2 +- what-is-iroh.mdx | 4 +-- 16 files changed, 97 insertions(+), 74 deletions(-) rename concepts/{discovery.mdx => address-lookup.mdx} (81%) rename connecting/{dht-discovery.mdx => dht-address-lookup.mdx} (57%) rename connecting/{dns-discovery.mdx => dns-address-lookup.mdx} (95%) rename connecting/{local-discovery.mdx => local-address-lookup.mdx} (71%) diff --git a/about/faq.mdx b/about/faq.mdx index 1750ca1..a0d8f28 100644 --- a/about/faq.mdx +++ b/about/faq.mdx @@ -47,15 +47,17 @@ Running your own relay doesn't affect interoperability. Your endpoints can still Yes. When you share a `EndpointAddr`s with "direct addresses", then iroh will try to use these addresses to establish a connection with or without a relay. -If you're in a local network together you can enable [local network discovery](https://docs.rs/iroh/latest/iroh/endpoint/struct.Builder.html#method.discovery_local_network) to help establish connections in LANs even when the `EndpointAddr` doesn't contain direct addresses. +If you're in a local network together you can enable [local network address lookup](/connecting/local-address-lookup) to help establish connections in LANs even when the `EndpointAddr` doesn't contain direct addresses. ## How can I control which relay servers iroh connects to? Iroh will only talk to relay servers that it knows URLs for. By default iroh is configured with 3 relay servers from the [default `RelayMap`](https://docs.rs/iroh/latest/iroh/defaults/prod/index.html). -If you do not disable the default discovery services or other discovery services, then iroh might connect to relay servers discovered that way. -By changing iroh's relay mode or relay map you can control the home relay the endpoint connects to, and by wrapping or writing your own `Discovery` service, you gain control over the relay URLs iroh can discover. +If your endpoint has address lookup services configured (which is +the default with the `N0` preset), then iroh might connect to +relay servers discovered that way. +By changing iroh's relay mode or relay map you can control the home relay the endpoint connects to, and by wrapping or writing your own `AddressLookup` service, you gain control over the relay URLs iroh can discover. ## How can I monitor endpoint connection status to update a UI? @@ -75,15 +77,16 @@ This returns an `impl Watchable` that reflects whether the endpoin Iroh gives you full control over which endpoints are allowed to connect via [endpoint hooks](/connecting/endpoint-hooks). Hooks let you intercept incoming connections before they're accepted, so you can allow or reject them based on the connecting endpoint's ID, your own allowlist/denylist logic, or any application-specific policy. -## What is "Discovery" in iroh and which one should I enable? +## What is "Address Lookup" in iroh and which one should I enable? -For most usage, using the services that are enabled by default in the `iroh::Endpoint::builder` is the best default. -Discovery helps iroh find ways to connect to a specific Endpoint ID. +For most usage, using the services that are enabled with the `iroh::endpoint::presets::N0` preset is the best default. + +Address Lookup helps iroh find ways to connect to a specific Endpoint ID. The Endpoint ID on its own can only be used to identify if you're talking to the right recipient, but doesn't tell how to address the recipient on its own. -Via configured discovery mechanisms, iroh resolves an Endpoint ID to IP addresses and relay URLs that help to actually attempt a connection. -For more information on available discovery mechanisms, take a look at the [address lookup module](https://docs.rs/iroh/latest/iroh/address_lookup/index.html). -It's also possible to combine multiple discovery mechanisms at once, or write your own. -We think it's particularly helpful to write application-specific discovery mechanisms that are tailored to an application's need. +Via configured address lookup mechanisms, iroh resolves an Endpoint ID to IP addresses and relay URLs that help to actually attempt a connection. +For more information on available address lookup mechanisms, take a look at the [address lookup docs](/concepts/address-lookup). +It's also possible to combine multiple address lookup mechanisms at once, or write your own. +We think it's particularly helpful to write application-specific address lookup mechanisms that are tailored to an application's need. ## What ports does iroh use? @@ -105,8 +108,8 @@ Iroh supports custom transports, which means you can route connections over Tor. The company behind iroh is number 0. It is partly venture capital and partly founder backed (as in: founders have invested their own money). Number 0 is healthy and has investors we actually think are a value-add. -We earn revenue through [Iroh Services](https://services.iroh.computer), which provides managed relay and DNS discovery infrastructure to keep your endpoints connected, from free public infrastructure for development and testing to dedicated cloud deployments for production. -We rely on iroh remaining open source, and are committed to keeping it that way, including server-side code for relays and DNS discovery. +We earn revenue through [Iroh Services](https://services.iroh.computer), which provides managed relay and DNS address lookup infrastructure to keep your endpoints connected, from free public infrastructure for development and testing to dedicated cloud deployments for production. +We rely on iroh remaining open source, and are committed to keeping it that way, including server-side code for relays and DNS address lookup. ## How does iroh compare to WebRTC? @@ -115,7 +118,7 @@ Both iroh and WebRTC solve the same core problem (establishing direct P2P connec **WebRTC** was designed for real-time media (audio and video) in browsers. It brings a complex stack: ICE for NAT traversal, STUN/TURN for relay, DTLS for encryption, SCTP for data channels, and a signaling layer that you have to implement yourself (WebRTC deliberately leaves signaling unspecified). This flexibility is powerful but adds significant complexity and is more expensive to run at scale. -**Iroh** is built around QUIC (TLS 1.3) and focuses on reliable, encrypted data connections. The API is simpler: you connect to an endpoint ID and get a QUIC connection. Relays are stateless and cheap. Hole-punching works roughly 9 out of 10 times. There's no signaling layer to design; discovery mechanisms handle address resolution. +**Iroh** is built around QUIC (TLS 1.3) and focuses on reliable, encrypted data connections. The API is simpler: you connect to an endpoint ID and get a QUIC connection. Relays are stateless and cheap. Hole-punching works roughly 9 out of 10 times. There's no signaling layer to design; address lookup mechanisms handle address resolution. - **Browser support.** Both WebRTC and iroh work in browsers. [Get started with iroh in the browser](https://docs.iroh.computer/deployment/other-languages#webassembly-and-browsers). However, WebRTC remains the only choice for hole-punched connections due to the current state of Web APIs. - **Media streaming.** WebRTC has first-class support for audio and video. Iroh is a general-purpose data transport, but you can run [MoQ (Media over QUIC)](https://www.iroh.computer/blog/secure-video-everywhere) on top of iroh for low-latency media streaming. @@ -148,7 +151,7 @@ Iroh is designed to be modular: you get a solid, reliable connection layer and c Where iroh tends to win: -- **Peer discovery and NAT traversal.** Iroh's [NAT traversal](/concepts/nat-traversal) and discovery story is significantly smoother. Getting peers to find each other reliably is one of the hardest parts of libp2p in practice; iroh has largely solved this. +- **Peer discovery and NAT traversal.** Iroh's [NAT traversal](/concepts/nat-traversal) and address lookup story is significantly smoother. Getting peers to find each other reliably is one of the hardest parts of libp2p in practice; iroh has largely solved this. - **Direct messaging.** Iroh gives you reliable, encrypted QUIC connections directly to specific peers. Straightforward to build routing or direct message delivery on top of. - **Gossip.** [iroh-gossip](https://github.com/n0-computer/iroh-gossip/) provides gossipsub-like fan-out on top of iroh connections. - **Blob transfer.** Iroh has a built-in [blob transfer protocol](https://docs.rs/iroh-blobs) for exchanging hash-addressed data, similar to what you'd use IPFS for. @@ -178,7 +181,7 @@ Adopting the current best post-quantum-secure algorithm, for example Xyber, woul A Xyber public key is 37x larger than an Ed25519 public key. This has implications for connection establishment speed: For example, the initial handshake for a connection wouldn't fit into a normal UDP packet anymore. -It also means DNS packets used for DNS discovery at the moment might get fragmented, etc. +It also means DNS packets used for DNS address lookup at the moment might get fragmented, etc. It would also mean Endpoint IDs would be exactly 37x as big. To support post-quantum-cryptography, we would need to trade off usability with the risk should a sufficiently powerful quantum computers would become real. We believe it is much more important to serve existing use cases efficiently, so they have encryption *today*. diff --git a/concepts/discovery.mdx b/concepts/address-lookup.mdx similarity index 81% rename from concepts/discovery.mdx rename to concepts/address-lookup.mdx index 7c21ee6..95e0c8f 100644 --- a/concepts/discovery.mdx +++ b/concepts/address-lookup.mdx @@ -14,13 +14,13 @@ Address lookup is an automated system for an [Endpoint](/concepts/endpoints) to ## Address Lookup Services -iroh ships several address lookup implementations. **By default, iroh uses DNS/Pkarr address lookup** to resolve EndpointIDs to addresses. DNS/Pkarr adress lookup publishes signed records to a server and resolves them via DNS. +iroh ships several address lookup implementations. **By default, iroh uses DNS/Pkarr address lookup** to resolve EndpointIDs to addresses. It publishes signed records to a server and resolves them via DNS. | Name | Description | Default | | --- | --- | --- | -| [DNS/Pkarr](#endpoint-discovery-via-dns) | publishes signed records to a server and resolves them over DNS | ✅ Enabled | -| [Local](#local-discovery) | uses an mDNS-like system to find endpoints on the local network | ❌ Disabled | -| [DHT](#dht-discovery) | publishes the same signed records to the BitTorrent Mainline DHT | ❌ Disabled | +| [DNS/Pkarr](#endpoint-address-lookup-via-dns) | publishes signed records to a server and resolves them over DNS | ✅ Enabled | +| [Local](/connecting/local-address-lookup) | uses an mDNS-like system to find endpoints on the local network | ❌ Disabled | +| [DHT](/connecting/dht-address-lookup) | publishes the same signed records to the BitTorrent Mainline DHT | ❌ Disabled | ### The motivation @@ -46,12 +46,12 @@ address data? ### The solution -We had two "guiding lights" while doing research on global endpoint discovery: +We had two "guiding lights" while doing research on global endpoint address lookup: First, we needed to see a path forward that would allow for a fully distributed topology, even if our first solution had a federated structure. If we (or some contributor) wanted to create or opt into a fully p2p version of global endpoint -discovery in the future, that needed to work in tandem with whatever federated +address lookup in the future, that needed to work in tandem with whatever federated solution we came up with. Second, it was very important for us as an organization that we do not invent @@ -60,7 +60,7 @@ stood the test of time (and scrutiny) in novel ways to solve our problem? It turns out, we can! And we can do it using one of the oldest and most dependable technologies we have on the internet: DNS. Using the DNS standard along side Pkarr (public-key addressable resource records), **we now have global endpoint -discovery in iroh**! +address lookup in iroh**! ## The approach @@ -88,11 +88,11 @@ on an `iroh-dns` server that is run by [n0.computer](https://n0.computer). From there, others can discover your dialing information by resolving your EndpointID using regular DNS. It's worth noting that others must still learn your endpoint ID for this to work. -The following sections describe the format of the Pkarr publishing records and endpoint discovery via DNS queries in greater detail. +The following sections describe the format of the Pkarr publishing records and endpoint address lookup via DNS queries in greater detail. -## Endpoint discovery via DNS +## Endpoint address lookup via DNS -When connecting to an unknown `EndpointId`, the DNS discovery mechanism in iroh will perform a DNS query to discover relays or addresses for the endpoint. The DNS discovery is configured with a *origin domain* (which defaults to *dns.iroh.link*, a server run by n0). iroh will then perform a DNS query through the configured DNS resolver (which defaults to using the host system's nameservers): +When connecting to an unknown `EndpointId`, the DNS address lookup mechanism in iroh will perform a DNS query to discover relays or addresses for the endpoint. The DNS address lookup is configured with a *origin domain* (which defaults to *dns.iroh.link*, a server run by n0). iroh will then perform a DNS query through the configured DNS resolver (which defaults to using the host system's nameservers): `_iroh.. TXT` @@ -109,7 +109,7 @@ This spec defines the following attributes: - `addr= ..` A space-separated list of socket addresses for this iroh endpoint. Each address is an IPv4 or IPv6 address with a port (e.g. `1.2.3.4:7367` or `[::1]:3521`) -**Ready to use DNS discovery?** Learn how to configure DNS discovery in your application in the [DNS Discovery guide](/connecting/dns-discovery). +**Ready to use DNS address lookup?** Learn how to configure DNS address lookup in your application in the [DNS Address Lookup guide](/connecting/dns-address-lookup). ## Endpoint announces via `pkarr` @@ -125,12 +125,12 @@ handling PUT requests with the signed packets. iroh's Pkarr server is [`iroh-dns-server`](https://crates.io/crates/iroh-dns-server), which serves the received records over DNS. Pkarr packets can also be published onto the BitTorrent Mainline DHT, as specified by Pkarr. iroh supports this through -[DHT discovery](/connecting/dht-discovery), which is opt-in. +[DHT address lookup](/connecting/dht-address-lookup), which is opt-in. DNS servers that support this spec will receive these Pkarr signed packets, check their signature and format validity, and then serve each contained record, with the DNS server's configured *origin domain* appended to all record names. -**Want to publish via Pkarr?** This is handled automatically when using DNS discovery. See the [DNS Discovery guide](/connecting/dns-discovery) for configuration details. +**Want to publish via Pkarr?** This is handled automatically when using DNS address lookup. See the [DNS Address Lookup guide](/connecting/dns-address-lookup) for configuration details. diff --git a/concepts/endpoints.mdx b/concepts/endpoints.mdx index 864f340..cfe3ad4 100644 --- a/concepts/endpoints.mdx +++ b/concepts/endpoints.mdx @@ -59,14 +59,14 @@ pub struct EndpointAddr { You'll interact with `EndpointAddr`s a fair amount when working with iroh. It's also quite normal to construct addresses manually from, say, endpoint identifiers stored in your application database. -When we call [`connect`](https://docs.rs/iroh/latest/iroh/endpoint/struct.Endpoint.html#method.connect) on an [Endpoint](https://www.iroh.computer/docs/concepts/endpoint), we need to pass either a `EndpointAddr`, or something that can turn into a `EndpointAddr`. In iroh `Endpoint`s will have different fields populated depending on where they came from, and the discovery services you've configured your endpoint with. +When we call [`connect`](https://docs.rs/iroh/latest/iroh/endpoint/struct.Endpoint.html#method.connect) on an [Endpoint](https://www.iroh.computer/docs/concepts/endpoint), we need to pass either a `EndpointAddr`, or something that can turn into a `EndpointAddr`. In iroh `Endpoint`s will have different fields populated depending on where they came from, and the address lookup services you've configured your endpoint with. -### Interaction with discovery +### Interaction with address lookup From the above struct, the only _required_ field is the `id`. And because of this, there's an implementation of `From` that can turn `EndpointIDs` directly -into EndpointAddrs. _but this will only work if you have a discovery service -that can resolve EndpointIDs enabled_. Thankfully, we enable discovery by +into EndpointAddrs. _but this will only work if you have an address lookup service +that can resolve EndpointIDs enabled_. Thankfully, we enable address lookup by default: ```rust @@ -78,24 +78,24 @@ let ep = Endpoint::builder(presets::N0) .await?; ``` -This is why we actively encourage configuring a discovery service, and DNS is the most common one we recommend. Because we're in p2p land dialing details & even home relays for an endpoint can change on very short notice, making this data go stale quickly. Endpoint Identifiers are a practical source of stability that counteracts this. +This is why we actively encourage configuring an address lookup service, and DNS is the most common one we recommend. Because we're in p2p land dialing details & even home relays for an endpoint can change on very short notice, making this data go stale quickly. Endpoint Identifiers are a practical source of stability that counteracts this. ### When to provide full details If you have full dialing details, it's well worth providing them as part of a `EndpointAddr` passed to `connect`. Iroh can use this to skip the network -roundtrip required to either do initial address discovery, or update cached +roundtrip required to either do initial address lookup, or update cached addresses. So if you have a source of up to date home relay & dialing info, provide it! ### What to persist in your application When storing endpoint information in your application database, what you should -persist depends on whether you're using discovery: +persist depends on whether you're using address lookup: -**If you're using discovery (recommended):** +**If you're using address lookup (recommended):** Store just the `EndpointID`. When you need to connect, construct an -`EndpointAddr` from the ID and let discovery resolve the current dialing +`EndpointAddr` from the ID and let address lookup resolve the current dialing details. This is the most robust approach since relay URLs and direct addresses can change frequently in P2P networks. @@ -108,9 +108,9 @@ let addr = EndpointAddr::from(endpoint_id); endpoint.connect(addr, ALPN).await?; ``` -**If you're not using discovery:** +**If you're not using address lookup:** You'll need to store full `EndpointAddr` information (including the `addrs` -field with relay and direct address information). Without discovery, iroh has no +field with relay and direct address information). Without address lookup, iroh has no way to resolve an `EndpointID` to dialing details. Keep in mind that stored dialing details can become stale quickly. Providing diff --git a/concepts/tickets.mdx b/concepts/tickets.mdx index 8910996..bec2a5a 100644 --- a/concepts/tickets.mdx +++ b/concepts/tickets.mdx @@ -37,7 +37,7 @@ Tickets work well in QR codes, can be sent via messaging apps, or published to a - You're building long-lived connections where dialing details change frequently - You can cache `EndpointID`s and let iroh resolve dialing details at runtime -If you have *any* means of coordinating (a database, server, or gossip protocol), we recommend you work with `EndpointID`s directly instead of tickets. Let iroh handle the discovery and connection details transparently. +If you have *any* means of coordinating (a database, server, or gossip protocol), we recommend you work with `EndpointID`s directly instead of tickets. Let iroh handle the address lookup and connection details transparently. ## Creating tickets diff --git a/connect-two-endpoints.mdx b/connect-two-endpoints.mdx index eb0506b..bd88b7e 100644 --- a/connect-two-endpoints.mdx +++ b/connect-two-endpoints.mdx @@ -47,7 +47,7 @@ An `EndpointTicket` wraps this address into a serializable format: a short strin This out-of-band information must reach the sender somehow so that endpoints can discover each other while still bootstrapping a secure, end-to-end encrypted connection. In this example we just use a string for users to copy and paste, but in your app you could publish it to a server, send it as a QR code, or pass it as a URL query parameter. It's up to you. -For more on how this works, see [Tickets](/concepts/tickets) and [Discovery](/concepts/discovery). +For more on how this works, see [Tickets](/concepts/tickets) and [Address Lookup](/concepts/address-lookup). ## The receiver diff --git a/connecting/creating-endpoint.mdx b/connecting/creating-endpoint.mdx index 11d3fd9..67c178e 100644 --- a/connecting/creating-endpoint.mdx +++ b/connecting/creating-endpoint.mdx @@ -92,7 +92,7 @@ keychain, or an OS secret store — not in source control. With an endpoint created, you can now start discovering and connecting to other endpoints. Explore the following guides to learn more: -- [DNS Global Discovery](/connecting/dns-discovery) -- [Local Network Discovery](/connecting/local-discovery) +- [DNS Global Address Lookup](/connecting/dns-address-lookup) +- [Local Network Address Lookup](/connecting/local-address-lookup) - [Gossip and Topic Broadcast](/connecting/gossip) - [Writing a Protocol](/protocols/writing-a-protocol) diff --git a/connecting/dht-discovery.mdx b/connecting/dht-address-lookup.mdx similarity index 57% rename from connecting/dht-discovery.mdx rename to connecting/dht-address-lookup.mdx index bd8ab5d..ee09042 100644 --- a/connecting/dht-discovery.mdx +++ b/connecting/dht-address-lookup.mdx @@ -2,16 +2,16 @@ title: "DHT" --- -DHT discovery publishes and resolves endpoint records on the +DHT address lookup publishes and resolves endpoint records on the [BitTorrent Mainline](https://en.wikipedia.org/wiki/Mainline_DHT) distributed hash table. The records are the same signed records that -[DNS discovery](/connecting/dns-discovery) uses. The difference is where they -are stored. DNS discovery publishes them to a hosted server and resolves over DNS, -while DHT discovery puts them on the BitTorrent Mainline DHT. That removes the +[DNS address lookup](/connecting/dns-address-lookup) uses. The difference is where they +are stored. DNS address lookup publishes them to a hosted server and resolves over DNS, +while DHT address lookup puts them on the BitTorrent Mainline DHT. That removes the dependency on a hosted server: any endpoint can publish and resolve without a central party, at the cost of slower lookups than DNS. -DHT discovery is not enabled by default. It lives in the separate +DHT address lookup is not enabled by default. It lives in the separate [`iroh-mainline-address-lookup`](https://crates.io/crates/iroh-mainline-address-lookup) crate, which you add alongside `iroh`. @@ -22,7 +22,7 @@ iroh-mainline-address-lookup = "0.3" ``` Add the `DhtAddressLookup` to the endpoint with `Endpoint::builder`. You can run it -next to the default DNS discovery, or build from `presets::Minimal` to use only the +next to the default DNS address lookup, or build from `presets::Minimal` to use only the DHT. ```rust @@ -40,8 +40,8 @@ async fn main() -> anyhow::Result<()> { } ``` -DHT address lookup also combines well with [mDNS address lookup](/connecting/local-discovery) -for both global and local discovery without depending on centralized infrastructure. +DHT address lookup also combines well with [mDNS address lookup](/connecting/local-address-lookup) +for both global and local address lookup without depending on centralized infrastructure. -For the record format shared with DNS discovery, see the -[Discovery concept page](/concepts/discovery). +For the record format shared with DNS address lookup, see the +[Address Lookup concept page](/concepts/address-lookup). diff --git a/connecting/dns-discovery.mdx b/connecting/dns-address-lookup.mdx similarity index 95% rename from connecting/dns-discovery.mdx rename to connecting/dns-address-lookup.mdx index 5ff411f..31e9717 100644 --- a/connecting/dns-discovery.mdx +++ b/connecting/dns-address-lookup.mdx @@ -10,7 +10,7 @@ endpoints it wants to dial. ## How records are published and resolved -Each endpoint creates a set of records with its adressing information, and puts it into a signed [Pkarr](https://pkarr.org) packet. The packet is signed by the endpoint's secret key. +Each endpoint creates a set of records with its addressing information, and puts it into a signed [Pkarr](https://pkarr.org) packet. The packet is signed by the endpoint's secret key. The endpoint publishes this packet to a DNS/Pkarr server via HTTP. Other endpoints can then resolve these packets either via DNS queries or via HTTP. diff --git a/connecting/gossip.mdx b/connecting/gossip.mdx index fecf922..313a448 100644 --- a/connecting/gossip.mdx +++ b/connecting/gossip.mdx @@ -54,7 +54,7 @@ use n0_future::StreamExt; #[tokio::main] async fn main() -> Result<()> { - // create an iroh endpoint that includes the standard discovery mechanisms + // create an iroh endpoint that includes the standard address lookup mechanisms // we've built at number0 let endpoint = Endpoint::bind(presets::N0).await?; @@ -112,7 +112,7 @@ already members of the topic. These peers will help you discover other peers in the topic. You can obtain bootstrap peers through various means, such as: - A predefined list of known peers hardcoded in your application. - A configuration file or environment variable. -- A discovery service that provides a list of active peers for the topic. +- An address lookup service that provides a list of active peers for the topic. #### Sharing a ticket diff --git a/connecting/local-discovery.mdx b/connecting/local-address-lookup.mdx similarity index 71% rename from connecting/local-discovery.mdx rename to connecting/local-address-lookup.mdx index a125c1e..2377f94 100644 --- a/connecting/local-discovery.mdx +++ b/connecting/local-address-lookup.mdx @@ -2,19 +2,19 @@ title: "mDNS" --- -The mDNS discovery mechanism will automatically broadcast your endpoint's +The mDNS address lookup mechanism will automatically broadcast your endpoint's presence on the local network, and listen for other endpoints doing the same. When another endpoint is discovered, the dialing information is exchanged, and a connection can be established directly over the local network without needing a relay. -Devices need to be connected to the same local network for mDNS discovery to +Devices need to be connected to the same local network for mDNS address lookup to work. This can be a Wi-Fi network, an Ethernet network, or even a mobile hotspot. mDNS is not designed to work over the internet or across different networks. ## Usage -mDNS discovery is not enabled by default. It lives in the separate +mDNS address lookup is not enabled by default. It lives in the separate [`iroh-mdns-address-lookup`](https://crates.io/crates/iroh-mdns-address-lookup) crate, which you add alongside `iroh`. @@ -24,7 +24,7 @@ iroh = "1.0.0-rc.1" iroh-mdns-address-lookup = "0.3" ``` -Add the `MdnsAddressLookup` to the endpoint with `Endpoint::builder`. We run it next to the default DNS discovery, so connections still work when the two endpoints are not on the same local network. +Add the `MdnsAddressLookup` to the endpoint with `Endpoint::builder`. We run it next to the default DNS address lookup, so connections still work when the two endpoints are not on the same local network. ```rust use iroh::{Endpoint, endpoint::presets}; @@ -41,4 +41,4 @@ async fn main() -> anyhow::Result<()> { } ``` -For more on how mDNS discovery works, see the [`iroh-mdns-address-lookup` documentation](https://docs.rs/iroh-mdns-address-lookup). +For more on how mDNS address lookup works, see the [`iroh-mdns-address-lookup` documentation](https://docs.rs/iroh-mdns-address-lookup). diff --git a/deployment/dedicated-infrastructure.mdx b/deployment/dedicated-infrastructure.mdx index 7c3ebe4..74b6564 100644 --- a/deployment/dedicated-infrastructure.mdx +++ b/deployment/dedicated-infrastructure.mdx @@ -8,7 +8,7 @@ By default, iroh will use public shared infrastructure to facilitate connections address lookup and end-to-end encryption over relays. This infrastructure comprises: 1. [Relays](/concepts/relays) -2. [Address Lookup](/concepts/discovery) +2. [Address Lookup](/concepts/address-lookup) Relays forward traffic when direct connections are not possible as well as facilitates NAT traversal for direct connections. These servers are managed and diff --git a/docs.json b/docs.json index f21ff59..870cae3 100644 --- a/docs.json +++ b/docs.json @@ -35,7 +35,7 @@ "pages": [ "concepts/endpoints", "concepts/tickets", - "concepts/discovery", + "concepts/address-lookup", "concepts/relays", "concepts/nat-traversal", "concepts/protocols" @@ -46,9 +46,9 @@ "expanded": false, "pages": [ "connecting/creating-endpoint", - "connecting/dns-discovery", - "connecting/dht-discovery", - "connecting/local-discovery", + "connecting/dns-address-lookup", + "connecting/dht-address-lookup", + "connecting/local-address-lookup", "connecting/gossip", "connecting/endpoint-hooks" ] @@ -183,6 +183,26 @@ { "source": "/protocols/kv-crdts", "destination": "/protocols/documents" + }, + { + "source": "/concepts/discovery", + "destination": "/concepts/address-lookup", + "permanent": true + }, + { + "source": "/connecting/dns-discovery", + "destination": "/connecting/dns-address-lookup", + "permanent": true + }, + { + "source": "/connecting/dht-discovery", + "destination": "/connecting/dht-address-lookup", + "permanent": true + }, + { + "source": "/connecting/local-discovery", + "destination": "/connecting/local-address-lookup", + "permanent": true } ] } diff --git a/examples/chat.mdx b/examples/chat.mdx index e8a398d..0e26ec6 100644 --- a/examples/chat.mdx +++ b/examples/chat.mdx @@ -62,7 +62,7 @@ async fn main() -> Result<()> { let secret_key = SecretKey::generate(); // Create an endpoint. - // By default we turn on our n0 discovery services. + // By default we turn on our n0 address lookup services. // This allows you to // dial by `EndpointId`, and allows you to be // dialed by `EndpointId`. @@ -553,7 +553,7 @@ use serde::{Deserialize, Serialize}; /// /// By default a new endpoint id is created when starting the example. /// -/// By default, we use the default n0 discovery services to dial by `EndpointId`. +/// By default, we use the default n0 address lookup services to dial by `EndpointId`. #[derive(Parser, Debug)] struct Args { /// Set your nickname. diff --git a/iroh-services/metrics/glossary.mdx b/iroh-services/metrics/glossary.mdx index b53f6f3..58f1060 100644 --- a/iroh-services/metrics/glossary.mdx +++ b/iroh-services/metrics/glossary.mdx @@ -29,9 +29,9 @@ Metrics in this section relate to relays and relay servers (visibility about rel | relay.bytes_recv | Total bytes received (relay side) | Bytes | | relay.bytes_sent | Total bytes sent (relay side) | Bytes | | relay.conns_rx_ratelimited | Connections rate-limited events | Count | -| relay.disco_packets_dropped | Discovery packets dropped | Count | -| relay.disco_packets_recv | Discovery packets received | Count | -| relay.disco_packets_sent | Discovery packets sent | Count | +| relay.disco_packets_dropped | Address Lookup packets dropped | Count | +| relay.disco_packets_recv | Address Lookup packets received | Count | +| relay.disco_packets_sent | Address Lookup packets sent | Count | | relay.disconnects | Disconnect events | Count | | relay.frames_rx_ratelimited | Frames rate-limited events | Count | | relay.got_ping | Got ping events | Count | diff --git a/protocols/automerge.mdx b/protocols/automerge.mdx index e7e1c49..6c8fd9e 100644 --- a/protocols/automerge.mdx +++ b/protocols/automerge.mdx @@ -11,7 +11,7 @@ eventually converging to the same state on every replica. ## Example This example highlights how to integrate [automerge's local-first CRDT](https://automerge.org/docs/hello/) with -[iroh's connectivity](/concepts/discovery). +[iroh's connectivity](/concepts/address-lookup). ```bash diff --git a/what-is-iroh.mdx b/what-is-iroh.mdx index f58f2be..2333e9b 100644 --- a/what-is-iroh.mdx +++ b/what-is-iroh.mdx @@ -41,7 +41,7 @@ flowchart TB app["Your application"] protocols["Protocols
blobs, docs, gossip, yours"] router["Router
dispatches connections by ALPN"] - endpoint["Endpoint
identity, discovery, NAT, relay"] + endpoint["Endpoint
identity, address lookup, NAT, relay"] quic["QUIC + TLS 1.3"] transport["Transport
UDP default, Tor, Nym, BLE"] @@ -59,7 +59,7 @@ flowchart TB stream multiplexing over that transport. - **[Endpoint](/concepts/endpoints)** is the connection-level API. It gives each node a stable `EndpointID`, finds peers through - [discovery](/concepts/discovery), traverses NATs, and falls back to + [address lookup](/concepts/address-lookup), traverses NATs, and falls back to [relays](/concepts/relays) when a direct path isn't available. - **Router** listens on an endpoint and dispatches each incoming connection to the right protocol handler based on its From ed21cc4730fac99ef0ceb0501fc72861ce10750b Mon Sep 17 00:00:00 2001 From: Frando Date: Wed, 10 Jun 2026 20:21:51 +0200 Subject: [PATCH 04/10] DNS fixes --- concepts/address-lookup.mdx | 10 ++++------ connecting/dns-address-lookup.mdx | 29 ++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/concepts/address-lookup.mdx b/concepts/address-lookup.mdx index 95e0c8f..2c833d7 100644 --- a/concepts/address-lookup.mdx +++ b/concepts/address-lookup.mdx @@ -2,15 +2,13 @@ title: "Address Lookup" --- - -Address Lookup is the glue that connects an [Endpoint](/concepts/endpoints#endpoint-identifiers) to something we can dial. Address Lookup services resolve EndpointIDs to either a home Relay URL or direct-dialing information. +Address Lookup is the glue that connects an [Endpoint ID](/concepts/endpoints#endpoint-identifiers) to something we can dial. Address Lookup services resolve Endpoint IDs to either their home Relay URL or direct-dialing information. More details can be found in the address lookup module [documentation](https://docs.rs/iroh/latest/iroh/address_lookup/index.html) -Address lookup is an automated system for an [Endpoint](/concepts/endpoints) to retrieve addressing information. Each iroh endpoint will automatically publish their own addressing information with configured address lookup services. Usually this means publishing which [Home Relay](/concepts/relays) an endpoint is findable at, but they could also publish their direct addresses. - +Address Lookup services form an automated system for an [Endpoint](/concepts/endpoints) to retrieve addressing information. Each iroh endpoint will automatically publish their own addressing information with configured address lookup services. Usually this means publishing which [Home Relay](/concepts/relays) an endpoint is findable at, but they could also publish their direct addresses. ## Address Lookup Services @@ -22,7 +20,7 @@ iroh ships several address lookup implementations. **By default, iroh uses DNS/P | [Local](/connecting/local-address-lookup) | uses an mDNS-like system to find endpoints on the local network | ❌ Disabled | | [DHT](/connecting/dht-address-lookup) | publishes the same signed records to the BitTorrent Mainline DHT | ❌ Disabled | -### The motivation +## Background on address lookup and DNS/Pkarr We want iroh to establish connections with as little friction as possible. Our first big push toward chipping away at this goal was adding [NAT traversal](/concepts/nat-traversal) into @@ -83,7 +81,7 @@ that endpoint. So the Pkarr packet currently only needs to contain the EndpointI the relay URL of its preferred relay server (which we call its "home relay"). When Pkarr publishing is enabled on your iroh endpoint, your endpoint will create a Pkarr packet with its EndpointID and relay URL, sign it, and defaults to publishing -on an `iroh-dns` server that is run by [n0.computer](https://n0.computer). +on an `iroh-dns-server` instance that is run by [n0.computer](https://n0.computer). From there, others can discover your dialing information by resolving your EndpointID using regular DNS. It's worth noting that others must still learn your endpoint ID for this to work. diff --git a/connecting/dns-address-lookup.mdx b/connecting/dns-address-lookup.mdx index 31e9717..9d5037c 100644 --- a/connecting/dns-address-lookup.mdx +++ b/connecting/dns-address-lookup.mdx @@ -22,32 +22,43 @@ If you need more capacity or uptime guarantees or SLAs, you can run your own DNS server, or [contact us about hosted DNS options](https://cal.com/team/number-0/n0-protocol-services?overlayCalendar=true). +You can read more about the design of the DNS/Pkarr address lookup system on the +[address lookup](/concepts/address-lookup) page. + + ## Using DNS address lookup DNS address lookup is part of the `presets::N0` defaults, so you do not need to enable it explicitly. The `N0` preset adds a publisher and a resolver, both pointed at the n0-hosted server at `dns.iroh.link`. +To discover another endpoint over DNS you need its `EndpointId`, either directly +or from a [ticket](/concepts/tickets) that contains one. Resolution then happens +automatically when you connect. + ```rust use iroh::{Endpoint, endpoint::presets}; -use iroh_tickets::endpoint::EndpointTicket; #[tokio::main] async fn main() -> anyhow::Result<()> { + // Bind an endpoint. let endpoint = Endpoint::bind(presets::N0).await?; - - println!("endpoint id: {:?}", endpoint.id()); - - let ticket = EndpointTicket::new(endpoint.addr()); - println!("Share this ticket to let others connect to your endpoint: {ticket}"); + endpoint.online().await?; + // We print its Endpoint ID. + let endpoint_id = endpoint.id(); + println!("endpoint id: {endpoint_id}"); + + // Another endpoint needs only the Endpoint ID to connect, + // because both endpoints use DNS/Pkarr address lookup services + // via the `N0` preset. + let other_endpoint = Endpoint::bind(presets::N0).await?; + let _conn = other_endpoint.connect(id, b"your-alpn").await?; + // It works :) Ok(()) } ``` -To discover another endpoint over DNS you need its `EndpointId`, either directly -or from a [ticket](/concepts/tickets) that contains one. Resolution then happens -automatically when you connect. ## Use your own server From 442c54788a2c901c0ab9bd0b234f009c124b8be9 Mon Sep 17 00:00:00 2001 From: Frando Date: Wed, 10 Jun 2026 20:24:36 +0200 Subject: [PATCH 05/10] fix --- connecting/dns-address-lookup.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/connecting/dns-address-lookup.mdx b/connecting/dns-address-lookup.mdx index 9d5037c..4f8c901 100644 --- a/connecting/dns-address-lookup.mdx +++ b/connecting/dns-address-lookup.mdx @@ -51,8 +51,8 @@ async fn main() -> anyhow::Result<()> { // Another endpoint needs only the Endpoint ID to connect, // because both endpoints use DNS/Pkarr address lookup services // via the `N0` preset. - let other_endpoint = Endpoint::bind(presets::N0).await?; - let _conn = other_endpoint.connect(id, b"your-alpn").await?; + let other = Endpoint::bind(presets::N0).await?; + let _conn = other.connect(endpoint_id, b"your-alpn").await?; // It works :) Ok(()) From 0ceb5c60261c916905e17161a536b48a5413ae23 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Thu, 11 Jun 2026 11:04:14 -0700 Subject: [PATCH 06/10] Add quick reference to top of each language guide Each language page now opens with supported platforms (from the overview matrix), a link to the API reference, and an example app. Co-Authored-By: Claude Fable 5 --- languages/javascript.mdx | 4 ++++ languages/kotlin.mdx | 4 ++++ languages/python.mdx | 4 ++++ languages/rust.mdx | 4 ++++ languages/swift.mdx | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/languages/javascript.mdx b/languages/javascript.mdx index 9b2d73b..2808b00 100644 --- a/languages/javascript.mdx +++ b/languages/javascript.mdx @@ -3,6 +3,10 @@ title: "JavaScript" description: "Use iroh from Node.js via the iroh-ffi bindings." --- +- **Platforms:** Node.js 20.3+ on macOS (arm64), Linux (x86_64, aarch64 — glibc + musl, armv7), Windows (x86_64, aarch64), Android (aarch64, armv7) ([full matrix](/languages#platform-support)) +- **API reference:** [JavaScript API docs](https://n0-computer.github.io/iroh-ffi/js/) +- **Example code:** [iroh-js test scripts](https://github.com/n0-computer/iroh-ffi/tree/main/iroh-js/test) — runnable scripts exercising endpoints, keys, and relays + The JavaScript bindings to iroh are published on npm as [`@number0/iroh`](https://www.npmjs.com/package/@number0/iroh) and shipped from [iroh-ffi](https://github.com/n0-computer/iroh-ffi). They're built with [napi-rs](https://napi.rs/), so the package distributes prebuilt native binaries — no Rust toolchain or local compilation needed. ## Install diff --git a/languages/kotlin.mdx b/languages/kotlin.mdx index 50b2537..20bb9ac 100644 --- a/languages/kotlin.mdx +++ b/languages/kotlin.mdx @@ -3,6 +3,10 @@ title: "Kotlin" description: "Use iroh from Kotlin on the JVM or Android." --- +- **Platforms:** macOS (arm64, x86_64), Linux (x86_64, aarch64 — glibc), Windows (x86_64); Android (aarch64, armv7) via [building from source](#building-from-source) ([full matrix](/languages#platform-support)) +- **API reference:** [Kotlin API docs](https://n0-computer.github.io/iroh-ffi/kotlin/) +- **Example app:** [hello-iroh-ffi](https://github.com/n0-computer/hello-iroh-ffi/tree/main/kotlin-android) — Jetpack Compose demo streaming live positions between two devices + The Kotlin bindings live in [iroh-ffi](https://github.com/n0-computer/iroh-ffi), generated by [uniffi-rs](https://github.com/mozilla/uniffi-rs) from the same Rust core as the Swift and Python bindings. Published to Maven Central as `computer.iroh:iroh`. ## Prerequisites diff --git a/languages/python.mdx b/languages/python.mdx index f12b3da..ad443a0 100644 --- a/languages/python.mdx +++ b/languages/python.mdx @@ -3,6 +3,10 @@ title: "Python" description: "Use iroh from Python via the iroh-ffi bindings." --- +- **Platforms:** macOS (arm64, x86_64), Linux (x86_64, aarch64 — glibc), Windows (x86_64) ([full matrix](/languages#platform-support)) +- **API reference:** [Python API docs](https://n0-computer.github.io/iroh-ffi/python/) +- **Example app:** [main.py](https://github.com/n0-computer/iroh-ffi/blob/main/python/main.py) — two-peer echo over QUIC + The Python bindings to iroh are published as prebuilt wheels on [PyPI](https://pypi.org/project/iroh/) and shipped from [iroh-ffi](https://github.com/n0-computer/iroh-ffi). The bindings are generated by [uniffi-rs](https://github.com/mozilla/uniffi-rs), so every class, method, and enum carries a docstring you can introspect from a REPL. ## Install diff --git a/languages/rust.mdx b/languages/rust.mdx index 9583820..5af8d10 100644 --- a/languages/rust.mdx +++ b/languages/rust.mdx @@ -3,6 +3,10 @@ title: "Rust" description: "Add iroh to your Rust project." --- +- **Platforms:** iOS, Android, macOS, Linux (glibc + musl), Windows — every officially supported platform ([full matrix](/languages#platform-support)) +- **API reference:** [docs.rs/iroh](https://docs.rs/iroh) +- **Example app:** [sendme](https://github.com/n0-computer/sendme) — CLI for direct file transfers between devices + iroh is written in Rust, so the Rust crate is the most fully-featured way to use it. Everything in these docs assumes Rust unless explicitly tagged otherwise. ## Install diff --git a/languages/swift.mdx b/languages/swift.mdx index 7ca20aa..88e9f80 100644 --- a/languages/swift.mdx +++ b/languages/swift.mdx @@ -3,6 +3,10 @@ title: "Swift" description: "Build an iOS + macOS app on top of iroh via the Swift bindings." --- +- **Platforms:** iOS (device + simulator), macOS (arm64) ([full matrix](/languages#platform-support)) +- **API reference:** [Swift API docs](https://n0-computer.github.io/iroh-ffi/swift/documentation/irohlib/) +- **Example app:** [hello-iroh-ffi](https://github.com/n0-computer/hello-iroh-ffi/tree/main/swift) — SwiftUI demo streaming live positions between two devices + The Swift bindings to iroh ship from [iroh-ffi](https://github.com/n0-computer/iroh-ffi) and are generated by [uniffi-rs](https://github.com/mozilla/uniffi-rs). The package distributes a prebuilt xcframework for iOS device, iOS simulator, and macOS, so adding iroh to your app is the same as adding any other Swift Package — no Rust toolchain required. This tutorial walks through creating a new Xcode project from scratch and wiring up a SwiftUI app that binds an iroh `Endpoint` and prints its endpoint id. From 83bf8a216eefd7d0dc46d5790bcefeedca0e7903 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Thu, 11 Jun 2026 11:27:19 -0700 Subject: [PATCH 07/10] Use platform tables in language quick references Replace the inline platform lists with per-language tables and remove em dashes from the quick reference blocks. Co-Authored-By: Claude Fable 5 --- languages/javascript.mdx | 13 +++++++++++-- languages/kotlin.mdx | 11 +++++++++-- languages/python.mdx | 10 ++++++++-- languages/rust.mdx | 12 ++++++++++-- languages/swift.mdx | 9 +++++++-- 5 files changed, 45 insertions(+), 10 deletions(-) diff --git a/languages/javascript.mdx b/languages/javascript.mdx index 2808b00..20b03e8 100644 --- a/languages/javascript.mdx +++ b/languages/javascript.mdx @@ -3,9 +3,18 @@ title: "JavaScript" description: "Use iroh from Node.js via the iroh-ffi bindings." --- -- **Platforms:** Node.js 20.3+ on macOS (arm64), Linux (x86_64, aarch64 — glibc + musl, armv7), Windows (x86_64, aarch64), Android (aarch64, armv7) ([full matrix](/languages#platform-support)) +| Platform | Architectures | +| --- | --- | +| macOS | arm64 | +| Linux | x86_64, aarch64 (glibc and musl), armv7 | +| Windows | x86_64, aarch64 | +| Android | aarch64, armv7 | + +All platforms require Node.js 20.3.0 or newer. + - **API reference:** [JavaScript API docs](https://n0-computer.github.io/iroh-ffi/js/) -- **Example code:** [iroh-js test scripts](https://github.com/n0-computer/iroh-ffi/tree/main/iroh-js/test) — runnable scripts exercising endpoints, keys, and relays +- **Example code:** [iroh-js test scripts](https://github.com/n0-computer/iroh-ffi/tree/main/iroh-js/test), runnable scripts covering endpoints, keys, and relays +- **All languages:** [platform support matrix](/languages#platform-support) The JavaScript bindings to iroh are published on npm as [`@number0/iroh`](https://www.npmjs.com/package/@number0/iroh) and shipped from [iroh-ffi](https://github.com/n0-computer/iroh-ffi). They're built with [napi-rs](https://napi.rs/), so the package distributes prebuilt native binaries — no Rust toolchain or local compilation needed. diff --git a/languages/kotlin.mdx b/languages/kotlin.mdx index 20bb9ac..bbe1313 100644 --- a/languages/kotlin.mdx +++ b/languages/kotlin.mdx @@ -3,9 +3,16 @@ title: "Kotlin" description: "Use iroh from Kotlin on the JVM or Android." --- -- **Platforms:** macOS (arm64, x86_64), Linux (x86_64, aarch64 — glibc), Windows (x86_64); Android (aarch64, armv7) via [building from source](#building-from-source) ([full matrix](/languages#platform-support)) +| Platform | Architectures | +| --- | --- | +| macOS | arm64, x86_64 | +| Linux | x86_64, aarch64 (glibc) | +| Windows | x86_64 | +| Android | aarch64, armv7 (requires [building from source](#building-from-source)) | + - **API reference:** [Kotlin API docs](https://n0-computer.github.io/iroh-ffi/kotlin/) -- **Example app:** [hello-iroh-ffi](https://github.com/n0-computer/hello-iroh-ffi/tree/main/kotlin-android) — Jetpack Compose demo streaming live positions between two devices +- **Example app:** [hello-iroh-ffi](https://github.com/n0-computer/hello-iroh-ffi/tree/main/kotlin-android), a Jetpack Compose demo that streams live positions between two devices +- **All languages:** [platform support matrix](/languages#platform-support) The Kotlin bindings live in [iroh-ffi](https://github.com/n0-computer/iroh-ffi), generated by [uniffi-rs](https://github.com/mozilla/uniffi-rs) from the same Rust core as the Swift and Python bindings. Published to Maven Central as `computer.iroh:iroh`. diff --git a/languages/python.mdx b/languages/python.mdx index ad443a0..e548258 100644 --- a/languages/python.mdx +++ b/languages/python.mdx @@ -3,9 +3,15 @@ title: "Python" description: "Use iroh from Python via the iroh-ffi bindings." --- -- **Platforms:** macOS (arm64, x86_64), Linux (x86_64, aarch64 — glibc), Windows (x86_64) ([full matrix](/languages#platform-support)) +| Platform | Architectures | +| --- | --- | +| macOS | arm64, x86_64 | +| Linux | x86_64, aarch64 (glibc) | +| Windows | x86_64 | + - **API reference:** [Python API docs](https://n0-computer.github.io/iroh-ffi/python/) -- **Example app:** [main.py](https://github.com/n0-computer/iroh-ffi/blob/main/python/main.py) — two-peer echo over QUIC +- **Example app:** [main.py](https://github.com/n0-computer/iroh-ffi/blob/main/python/main.py), a two-peer echo over QUIC +- **All languages:** [platform support matrix](/languages#platform-support) The Python bindings to iroh are published as prebuilt wheels on [PyPI](https://pypi.org/project/iroh/) and shipped from [iroh-ffi](https://github.com/n0-computer/iroh-ffi). The bindings are generated by [uniffi-rs](https://github.com/mozilla/uniffi-rs), so every class, method, and enum carries a docstring you can introspect from a REPL. diff --git a/languages/rust.mdx b/languages/rust.mdx index 5af8d10..32cc8ac 100644 --- a/languages/rust.mdx +++ b/languages/rust.mdx @@ -3,9 +3,17 @@ title: "Rust" description: "Add iroh to your Rust project." --- -- **Platforms:** iOS, Android, macOS, Linux (glibc + musl), Windows — every officially supported platform ([full matrix](/languages#platform-support)) +| Platform | Architectures | +| --- | --- | +| iOS | device and simulator | +| Android | aarch64, armv7 | +| macOS | arm64, x86_64 | +| Linux | x86_64, aarch64, armv7 (glibc and musl) | +| Windows | x86_64, aarch64 | + - **API reference:** [docs.rs/iroh](https://docs.rs/iroh) -- **Example app:** [sendme](https://github.com/n0-computer/sendme) — CLI for direct file transfers between devices +- **Example app:** [sendme](https://github.com/n0-computer/sendme), a CLI for direct file transfers between devices +- **All languages:** [platform support matrix](/languages#platform-support) iroh is written in Rust, so the Rust crate is the most fully-featured way to use it. Everything in these docs assumes Rust unless explicitly tagged otherwise. diff --git a/languages/swift.mdx b/languages/swift.mdx index 88e9f80..7234602 100644 --- a/languages/swift.mdx +++ b/languages/swift.mdx @@ -3,9 +3,14 @@ title: "Swift" description: "Build an iOS + macOS app on top of iroh via the Swift bindings." --- -- **Platforms:** iOS (device + simulator), macOS (arm64) ([full matrix](/languages#platform-support)) +| Platform | Architectures | +| --- | --- | +| iOS | device and simulator | +| macOS | arm64 | + - **API reference:** [Swift API docs](https://n0-computer.github.io/iroh-ffi/swift/documentation/irohlib/) -- **Example app:** [hello-iroh-ffi](https://github.com/n0-computer/hello-iroh-ffi/tree/main/swift) — SwiftUI demo streaming live positions between two devices +- **Example app:** [hello-iroh-ffi](https://github.com/n0-computer/hello-iroh-ffi/tree/main/swift), a SwiftUI demo that streams live positions between two devices +- **All languages:** [platform support matrix](/languages#platform-support) The Swift bindings to iroh ship from [iroh-ffi](https://github.com/n0-computer/iroh-ffi) and are generated by [uniffi-rs](https://github.com/mozilla/uniffi-rs). The package distributes a prebuilt xcframework for iOS device, iOS simulator, and macOS, so adding iroh to your app is the same as adding any other Swift Package — no Rust toolchain required. From dcdbd2ebd57c1420d8fc656989ae99c84a9d83cf Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Thu, 11 Jun 2026 11:33:28 -0700 Subject: [PATCH 08/10] Remove em dashes from language guide prose Co-Authored-By: Claude Fable 5 --- languages/javascript.mdx | 4 ++-- languages/kotlin.mdx | 8 ++++---- languages/python.mdx | 2 +- languages/swift.mdx | 12 ++++++------ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/languages/javascript.mdx b/languages/javascript.mdx index 20b03e8..03d5ca6 100644 --- a/languages/javascript.mdx +++ b/languages/javascript.mdx @@ -16,7 +16,7 @@ All platforms require Node.js 20.3.0 or newer. - **Example code:** [iroh-js test scripts](https://github.com/n0-computer/iroh-ffi/tree/main/iroh-js/test), runnable scripts covering endpoints, keys, and relays - **All languages:** [platform support matrix](/languages#platform-support) -The JavaScript bindings to iroh are published on npm as [`@number0/iroh`](https://www.npmjs.com/package/@number0/iroh) and shipped from [iroh-ffi](https://github.com/n0-computer/iroh-ffi). They're built with [napi-rs](https://napi.rs/), so the package distributes prebuilt native binaries — no Rust toolchain or local compilation needed. +The JavaScript bindings to iroh are published on npm as [`@number0/iroh`](https://www.npmjs.com/package/@number0/iroh) and shipped from [iroh-ffi](https://github.com/n0-computer/iroh-ffi). They're built with [napi-rs](https://napi.rs/), so the package distributes prebuilt native binaries. No Rust toolchain or local compilation is needed. ## Install @@ -50,7 +50,7 @@ await ep.close() A minimal sender/receiver pair over a bidirectional stream: ```javascript -// main.mjs — run: `node main.mjs receiver` or `node main.mjs sender ` +// main.mjs: run `node main.mjs receiver` or `node main.mjs sender ` import { Endpoint, EndpointTicket } from '@number0/iroh' const ALPN = Array.from(Buffer.from('hello-iroh/0')) diff --git a/languages/kotlin.mdx b/languages/kotlin.mdx index bbe1313..1bdbe53 100644 --- a/languages/kotlin.mdx +++ b/languages/kotlin.mdx @@ -36,7 +36,7 @@ dependencies { ``` -The published artifact is single-platform — it does not yet ship a multiplatform build, so it won't cover Android or arbitrary host triples out of the box. If you need a target the artifact doesn't cover, [build the bindings from source](#building-from-source). +The published artifact is single-platform. It does not yet ship a multiplatform build, so it won't cover Android or arbitrary host triples out of the box. If you need a target the artifact doesn't cover, [build the bindings from source](#building-from-source). ## Hello, iroh @@ -56,7 +56,7 @@ fun main() = runBlocking { } ``` -Drop this in any module that depends on the generated `computer.iroh` package. The API mirrors the Swift and Python bindings — same `Endpoint.bind`, same `EndpointOptions`, same presets. +Drop this in any module that depends on the generated `computer.iroh` package. The API mirrors the Swift and Python bindings: same `Endpoint.bind`, same `EndpointOptions`, same presets. ## Next steps @@ -78,13 +78,13 @@ Drop this in any module that depends on the generated `computer.iroh` package. T ## Foreground and backgrounding -On Android, the OS aggressively suspends background processes — sockets get torn down, threads stop scheduling, and an iroh endpoint that was happily accepting connections a moment ago goes silent. Treat each foreground/background transition as a lifecycle event for your endpoint: +On Android, the OS aggressively suspends background processes: sockets get torn down, threads stop scheduling, and an iroh endpoint that was happily accepting connections a moment ago goes silent. Treat each foreground/background transition as a lifecycle event for your endpoint: - **Going to background**: call `ep.shutdown()` to close cleanly and release sockets. Persist the endpoint's secret key (`ep.secretKey().toBytes()`) somewhere durable (e.g. `EncryptedSharedPreferences`) so the next bind keeps the same endpoint id. - **Returning to foreground**: re-bind with `EndpointOptions(secretKey = persistedBytes, preset = presetN0(), ...)`. Discovery republishes the new direct addresses automatically. - **Staying live while backgrounded**: run iroh inside an [Android foreground service](https://developer.android.com/develop/background-work/services/foreground-services) (with the appropriate `FOREGROUND_SERVICE_*` permission for your use case). Without one, Android will eventually kill the network sockets regardless of how the coroutine scope is structured. -For desktop JVM apps these constraints don't apply — keep the endpoint bound for the lifetime of the process and call `shutdown()` on exit. +For desktop JVM apps these constraints don't apply. Keep the endpoint bound for the lifetime of the process and call `shutdown()` on exit. ## Building from source diff --git a/languages/python.mdx b/languages/python.mdx index e548258..2676c26 100644 --- a/languages/python.mdx +++ b/languages/python.mdx @@ -86,7 +86,7 @@ The [main.py example](https://github.com/n0-computer/iroh-ffi/blob/main/python/m # Terminal 1 python main.py serve -# Terminal 2 — paste the ticket printed from Terminal 1 +# Terminal 2: paste the ticket printed from Terminal 1 python main.py connect ``` diff --git a/languages/swift.mdx b/languages/swift.mdx index 7234602..ff8b31e 100644 --- a/languages/swift.mdx +++ b/languages/swift.mdx @@ -12,7 +12,7 @@ description: "Build an iOS + macOS app on top of iroh via the Swift bindings." - **Example app:** [hello-iroh-ffi](https://github.com/n0-computer/hello-iroh-ffi/tree/main/swift), a SwiftUI demo that streams live positions between two devices - **All languages:** [platform support matrix](/languages#platform-support) -The Swift bindings to iroh ship from [iroh-ffi](https://github.com/n0-computer/iroh-ffi) and are generated by [uniffi-rs](https://github.com/mozilla/uniffi-rs). The package distributes a prebuilt xcframework for iOS device, iOS simulator, and macOS, so adding iroh to your app is the same as adding any other Swift Package — no Rust toolchain required. +The Swift bindings to iroh ship from [iroh-ffi](https://github.com/n0-computer/iroh-ffi) and are generated by [uniffi-rs](https://github.com/mozilla/uniffi-rs). The package distributes a prebuilt xcframework for iOS device, iOS simulator, and macOS, so adding iroh to your app is the same as adding any other Swift Package, with no Rust toolchain required. This tutorial walks through creating a new Xcode project from scratch and wiring up a SwiftUI app that binds an iroh `Endpoint` and prints its endpoint id. @@ -133,7 +133,7 @@ Pick a destination and hit Run. - **iOS Simulator**: choose any iPhone simulator destination. - **iOS device**: select your device. You'll need to trust your developer certificate in **Settings → General → VPN & Device Management** the first time. -Two different installs print two different endpoint ids — that's expected, since each launch generates a fresh `SecretKey`. If you want a stable identity across launches, persist `endpoint.secretKey().toBytes()` (e.g. in `UserDefaults` for a demo, Keychain for production) and pass it back via `EndpointOptions(secretKey: ...)` on subsequent launches. +Two different installs print two different endpoint ids. That's expected, since each launch generates a fresh `SecretKey`. If you want a stable identity across launches, persist `endpoint.secretKey().toBytes()` (e.g. in `UserDefaults` for a demo, Keychain for production) and pass it back via `EndpointOptions(secretKey: ...)` on subsequent launches. ## Building against an unreleased iroh-ffi @@ -160,7 +160,7 @@ Then in Xcode, **File → Add Package Dependencies… → Add Local…** and pic - Full DocC reference for `IrohLib` — every type, method, and option generated from the bindings. + Full DocC reference for `IrohLib`: every type, method, and option generated from the bindings. @@ -173,12 +173,12 @@ Then in Xcode, **File → Add Package Dependencies… → Add Local…** and pic ## Troubleshooting -**`Undefined symbols: _nw_interface_get_index` on iOS.** You skipped step 3 — add `-framework Network` to **Other Linker Flags** for the iOS SDKs. +**`Undefined symbols: _nw_interface_get_index` on iOS.** You skipped step 3. Add `-framework Network` to **Other Linker Flags** for the iOS SDKs. -**`product being built is not an allowed client of SwiftUICore`.** You skipped step 5 — set **Enable Previews** to **No**. +**`product being built is not an allowed client of SwiftUICore`.** You skipped step 5. Set **Enable Previews** to **No**. **`No such module 'IrohLib'`.** The package didn't resolve. Pull down the package from **File → Packages → Reset Package Caches** and try the resolve again. -**`bind failed: NoNetwork` or hang on macOS.** Check **Signing & Capabilities** — both **Incoming** and **Outgoing** network sandbox boxes need to be checked. +**`bind failed: NoNetwork` or hang on macOS.** Check **Signing & Capabilities**: both **Incoming** and **Outgoing** network sandbox boxes need to be checked. **`The developer disk image could not be mounted on this device`.** Your device is running a newer iOS than your Xcode supports. Update Xcode (App Store for stable, [developer.apple.com](https://developer.apple.com/download/applications) for betas) so the matching Developer Disk Image is available, or fall back to the simulator while you wait. From 7cd8f6626e7ec864eb706046a87f66a0a7fbd241 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Thu, 11 Jun 2026 11:38:12 -0700 Subject: [PATCH 09/10] Point Python and JS guides at new hello-iroh-ffi examples hello-iroh-ffi now ships Python and Node console readers for the dot demo. Link them from the Python and JavaScript quick references and next-steps cards, mention them on the examples page, and add a hello-iroh-ffi card to the languages overview. Co-Authored-By: Claude Fable 5 --- examples.mdx | 2 +- languages/index.mdx | 4 ++++ languages/javascript.mdx | 6 +++++- languages/python.mdx | 6 +++++- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/examples.mdx b/examples.mdx index 5b2bbc3..cf2a611 100644 --- a/examples.mdx +++ b/examples.mdx @@ -33,7 +33,7 @@ Example code repositories demonstrating iroh usage. icon="mobile" href="https://github.com/n0-computer/hello-iroh-ffi" > - Example iOS, macOS, and Android presence demo built on the Swift and Kotlin bindings + Example presence demo with iOS, macOS, and Android apps plus Python and Node console readers, built on the iroh-ffi bindings + One demo, four languages: SwiftUI and Jetpack Compose apps plus Python and Node console readers, all speaking the same protocol over iroh. A good starting point for any of the bindings. + + ## Platform support The [n0.computer](https://n0.computer) team offers official support for the following platforms. diff --git a/languages/javascript.mdx b/languages/javascript.mdx index 03d5ca6..35d9a24 100644 --- a/languages/javascript.mdx +++ b/languages/javascript.mdx @@ -13,7 +13,7 @@ description: "Use iroh from Node.js via the iroh-ffi bindings." All platforms require Node.js 20.3.0 or newer. - **API reference:** [JavaScript API docs](https://n0-computer.github.io/iroh-ffi/js/) -- **Example code:** [iroh-js test scripts](https://github.com/n0-computer/iroh-ffi/tree/main/iroh-js/test), runnable scripts covering endpoints, keys, and relays +- **Example app:** [hello-iroh-ffi](https://github.com/n0-computer/hello-iroh-ffi/tree/main/js), a console reader for the cross-platform dot demo - **All languages:** [platform support matrix](/languages#platform-support) The JavaScript bindings to iroh are published on npm as [`@number0/iroh`](https://www.npmjs.com/package/@number0/iroh) and shipped from [iroh-ffi](https://github.com/n0-computer/iroh-ffi). They're built with [napi-rs](https://napi.rs/), so the package distributes prebuilt native binaries. No Rust toolchain or local compilation is needed. @@ -95,6 +95,10 @@ Start the receiver in one terminal, copy the printed ticket, and run the sender Walkthrough of endpoints, tickets, and ALPNs with code samples in every language. + + A headless Node peer for the cross-platform dot demo. It speaks the same wire format as the Swift and Kotlin apps and prints received positions to the console. + + Generated TypeDoc reference for `@number0/iroh`. diff --git a/languages/python.mdx b/languages/python.mdx index 2676c26..3bb718b 100644 --- a/languages/python.mdx +++ b/languages/python.mdx @@ -10,7 +10,7 @@ description: "Use iroh from Python via the iroh-ffi bindings." | Windows | x86_64 | - **API reference:** [Python API docs](https://n0-computer.github.io/iroh-ffi/python/) -- **Example app:** [main.py](https://github.com/n0-computer/iroh-ffi/blob/main/python/main.py), a two-peer echo over QUIC +- **Example app:** [hello-iroh-ffi](https://github.com/n0-computer/hello-iroh-ffi/tree/main/python), a console reader for the cross-platform dot demo - **All languages:** [platform support matrix](/languages#platform-support) The Python bindings to iroh are published as prebuilt wheels on [PyPI](https://pypi.org/project/iroh/) and shipped from [iroh-ffi](https://github.com/n0-computer/iroh-ffi). The bindings are generated by [uniffi-rs](https://github.com/mozilla/uniffi-rs), so every class, method, and enum carries a docstring you can introspect from a REPL. @@ -98,6 +98,10 @@ The client opens a bi-directional stream, sends `hello`, and prints what the oth Conceptual walkthrough of endpoints, tickets, and ALPNs. The code samples are Rust, but every API maps 1:1 to Python. + + A headless Python peer for the cross-platform dot demo. It speaks the same wire format as the Swift and Kotlin apps and prints received positions to the console. + + Full class, method, and enum reference generated from the uniffi-rs bindings. From 332cdab936859415842ac28411fb980d1013b30d Mon Sep 17 00:00:00 2001 From: Frando Date: Mon, 15 Jun 2026 09:08:41 +0200 Subject: [PATCH 10/10] fixup rebase --- snippets/relay-endpoint-config.mdx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/snippets/relay-endpoint-config.mdx b/snippets/relay-endpoint-config.mdx index d7bc76f..3025825 100644 --- a/snippets/relay-endpoint-config.mdx +++ b/snippets/relay-endpoint-config.mdx @@ -7,10 +7,12 @@ use iroh::{Endpoint, RelayMap, RelayMode, RelayUrl, endpoint::presets}; async fn main() -> anyhow::Result<()> { let relay_url1: RelayUrl = "YOUR_RELAY_URL_US".parse()?; let relay_url2: RelayUrl = "YOUR_RELAY_URL_EU".parse()?; - let relay_map = RelayMap::from_iter([relay_url1, relay_url2]); let endpoint = Endpoint::builder(presets::N0) - .relay_mode(RelayMode::Custom(relay_map)) + .relay_mode(RelayMode::Custom(RelayMap::from_iter([ + relay_url1, + relay_url2, + ]))) .bind() .await?;