From a746fdd0ee5f4b82b3c9f9f9b6c04d47f7895d60 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Mon, 27 Jul 2026 18:49:07 -0700 Subject: [PATCH 01/18] docs: add enterprise network deployment guide Corporate networks are where iroh deployments hit friction, and the answers have lived in the source tree rather than the docs: which ports and hostnames an endpoint actually touches, that the relay path is a WebSocket over TLS on 443, that certificate verification ignores the OS trust store, and that the relay ships an access control config. The guide is organized around what a network administrator has to decide rather than around iroh's internals. It leads with the fact that iroh works with UDP blocked entirely, since that is the question enterprise reviews open with, and treats allowing UDP as an optimization to argue for rather than a requirement to concede. Co-Authored-By: Claude Opus 5 (1M context) --- deployment/enterprise-networks.mdx | 569 +++++++++++++++++++++++++++++ docs.json | 1 + troubleshooting.mdx | 7 + 3 files changed, 577 insertions(+) create mode 100644 deployment/enterprise-networks.mdx diff --git a/deployment/enterprise-networks.mdx b/deployment/enterprise-networks.mdx new file mode 100644 index 0000000..ab9f2d8 --- /dev/null +++ b/deployment/enterprise-networks.mdx @@ -0,0 +1,569 @@ +--- +title: "Enterprise Networks" +description: "Firewall, proxy, DNS, and Wi-Fi configuration for running iroh on managed corporate networks" +--- + +Running iroh on a managed corporate network takes one firewall rule: outbound +TCP 443 to your relay hostnames. Allowing UDP as well is what turns most +connections direct. There are no inbound rules, no port forwarding, no static +IPs, and no listening ports to expose. + +Everything below is the detail behind those three sentences: every flow an +endpoint opens, what happens when each one is blocked, and how to configure iroh +around the restrictions a corporate network typically imposes. + +## Two levels of configuration + +| | What you allow | What you get | +|---|---|---| +| **Minimum** | Outbound TCP 443 to your relay hostnames | Every connection works. All traffic is relayed. | +| **Recommended** | The above, plus outbound UDP | Roughly nine connections in ten become direct: lower latency, higher throughput, and no relay egress | + +Both are supported configurations. iroh detects which one it is on and adapts at +runtime. You do not configure a mode, and connections do not fail if UDP is +unavailable. + +## Traffic reference + +All flows are **outbound-initiated**. Return traffic arrives on the same 5-tuple +and is permitted by any stateful firewall. + +| Traffic | Transport | Destination | Required | +|---|---|---|---| +| Relay connection | TCP 443 (TLS, WebSocket upgrade at `/relay`) | Your relay hostnames | **Yes** | +| Relay latency probe | TCP 443 (HTTPS `GET /ping`) | Every configured relay hostname | **Yes** | +| Address publishing | TCP 443 (HTTPS `PUT`, pkarr) | `dns.iroh.link` | Default preset only | +| Address resolution | UDP/TCP 53 via your own resolver | TXT records under `dns.iroh.link` | Default preset only | +| QUIC address discovery | UDP 7842 | Your relay hostnames | Recommended | +| Direct QUIC and hole punching | UDP, ephemeral source port to arbitrary destination port | Any peer | Recommended | +| Port mapping | UDP 1900 (SSDP), UDP 5351 (PCP/NAT-PMP) | Local gateway only | Optional | +| mDNS address lookup | UDP 5353 to `224.0.0.251`, `ff02::fb` | Local segment only | Off by default | + +By default an endpoint binds `0.0.0.0:0` and `[::]:0`, an OS-assigned ephemeral +UDP port on all interfaces. To pin it to a fixed port for a narrower egress +rule: + +```rust +use iroh::{Endpoint, endpoint::presets}; + +let endpoint = Endpoint::builder(presets::N0) + .clear_ip_transports() + .bind_addr("0.0.0.0:49555")? + .bind() + .await?; +``` + +Iroh Services features, [metrics](/iroh-services/metrics/index) and +[network diagnostics](/iroh-services/net-diagnostics/usage), travel over your +existing iroh connections. They need no additional firewall rules. + +## Hostnames to allowlist + +With the default `presets::N0` configuration: + +``` +use1-1.relay.n0.iroh.link +usw1-1.relay.n0.iroh.link +euc1-1.relay.n0.iroh.link +aps1-1.relay.n0.iroh.link +dns.iroh.link +``` + + +iroh releases before 1.0 used `use1-1.relay.iroh.network`, +`euw1-1.relay.iroh.network`, and `aps1-1.relay.iroh.network`. Include those too +if you have older clients in the fleet. + + +If you deploy [dedicated relays](/deployment/dedicated-infrastructure), your +allowlist becomes a short list of hostnames you own, and endpoints stop +contacting n0 infrastructure entirely. This is the recommended posture for +production on a managed network. See +[Running entirely inside your perimeter](#running-entirely-inside-your-perimeter). + +## Working with a UDP-restricted network + +Many enterprise networks restrict UDP, on the reasoning that it is harder to +track and that older firewalls cannot inspect it statefully. iroh is built to +work either way, so this does not need to be a blocker. + +### iroh works with UDP fully blocked + +The relay path is a WebSocket over TLS on TCP 443. To a firewall it is an +ordinary long-lived HTTPS connection, and it is subject to the same policy, +logging, and allowlisting as any other HTTPS destination. If your network passes +HTTPS, it passes iroh. + +### What blocking UDP costs + +Every byte between two peers takes a round trip through a relay. Expect added +latency proportional to the distance to the nearest relay, throughput bounded by +the relay rather than by the peers' links, and relay egress you either pay for +or contend for on shared public infrastructure. Nothing breaks; it is slower and +more expensive. + +### Why allowing UDP is usually reasonable + +- **It is QUIC, not raw datagrams.** Connection-oriented, congestion-controlled, + TLS 1.3 handshake, retransmission, and flow control. It behaves like a TCP + flow that happens to ride UDP. +- **Your network probably already carries it.** Chrome, Edge, and Safari use + HTTP/3 over UDP 443 by default for a large share of the web. iroh's traffic is + the same protocol family. +- **Flows are always outbound-first.** No inbound rule, no port forward, and no + exposed listener. A stateful firewall sees an outbound flow and its replies. +- **Idle mappings do not linger.** iroh sends a heartbeat every 5 seconds on an + active path and tears a path down after 15 seconds without traffic. A UDP idle + timeout of 30 seconds or more on your firewall is comfortable, and abandoned + state ages out quickly. +- **Inspection is not the tradeoff.** All iroh traffic is end-to-end encrypted + regardless of path. The TCP 443 relay path is no more inspectable than the UDP + path: a middlebox sees ciphertext either way. If your policy requires + visibility into the payload, the answer is to run relays inside your perimeter + and inspect at the application, not to block UDP. + +### Partial UDP allowances do not help + +Allowing UDP only to port 443 buys nothing. Direct paths use ephemeral ports on +both ends, and QUIC address discovery targets port 7842 on the relay. It is +effectively all-or-relay for internet peers. + + +The exception worth knowing: **two peers on the same corporate network connect +directly over the LAN**, governed by your internal segmentation policy rather +than the perimeter firewall. Branch-office and same-site traffic can stay +direct and never touch a relay even when the perimeter blocks UDP outbound. + + +### Forcing relay-only + +On a network where UDP is blackholed, you can skip UDP entirely. This removes +probe traffic that shows up as noise in IDS logs and speeds up startup, since +iroh no longer waits for probes that will never be answered. + +```rust +use iroh::endpoint::{presets, PortmapperConfig}; +use iroh::Endpoint; + +let endpoint = Endpoint::builder(presets::N0) + // No UDP sockets at all: relay transport only. + .clear_ip_transports() + // Skips SSDP multicast gateway discovery. + .portmapper_config(PortmapperConfig::Disabled) + .bind() + .await?; +``` + +`PortmapperConfig::Disabled` is worth setting on its own even when UDP is +allowed. The UPnP discovery step multicasts SSDP to `239.255.255.250:1900`, +which is a common IDS signature and raises firewall prompts on some client +operating systems. Disabling it costs you gateway-assisted port mapping, which +most enterprise gateways do not offer anyway. + +## HTTP proxies + +If your network requires egress through an HTTP proxy, iroh can tunnel its relay +and HTTPS traffic through it with `CONNECT`: + +```rust +let endpoint = Endpoint::builder(presets::N0) + // Reads HTTP_PROXY, http_proxy, HTTPS_PROXY, https_proxy, in that order. + .proxy_from_env() + .bind() + .await?; +``` + +Use `.proxy_url(url)` to set it explicitly instead. The proxy applies to relay +and HTTPS traffic only; UDP paths are unaffected, and a proxied deployment +typically ends up relay-only in practice. + +## TLS interception + +By default iroh verifies relay certificates against a **compiled-in copy of the +Mozilla root store**. It does not consult the operating system's trust store, so +a TLS-intercepting proxy presenting a corporate CA will cause relay connections +to fail with a certificate error even though the CA is installed on the machine. + +Switch to the platform verifier so the CA your MDM distributed is honored: + +```rust +use iroh::tls::CaTlsConfig; + +let endpoint = Endpoint::builder(presets::N0) + .ca_tls_config(CaTlsConfig::system()) + .bind() + .await?; +``` + +`CaTlsConfig::custom_roots(certs)` trusts only the roots you supply, and +`CaTlsConfig::system().with_extra_roots(certs)` adds to the OS set. + + +This affects the relay and HTTPS paths only. Peer-to-peer QUIC connections +authenticate with raw public keys bound to endpoint IDs and cannot be +intercepted: a middlebox that substitutes a certificate only causes the +handshake to be rejected. In practice no proxy sits in a peer-to-peer path. + + +## DNS filtering + +With the default preset, resolving a peer by its endpoint ID means a TXT lookup +for `_iroh..dns.iroh.link` through your normal resolver. Two +enterprise patterns break this: + +- **TXT-record filtering.** DNS security products that block or strip TXT + responses as a DNS-tunneling heuristic will silently break address lookup. +- **Split-horizon resolvers** that do not forward queries for `iroh.link`. + +The symptom is distinctive: connections by explicit address succeed, but +connecting by endpoint ID fails to resolve. Allowlist `dns.iroh.link` in your +resolver policy, or drop DNS-based lookup and distribute addresses out of band. + +## Running entirely inside your perimeter + +For deployments where no iroh traffic may leave the network, replace all three +pieces of shared infrastructure: + + + +Run the [relay binary](https://github.com/n0-computer/iroh/tree/main/iroh-relay) +on hosts your endpoints can reach, and point clients at them with +`RelayMode::Custom`. See [Dedicated +Infrastructure](/deployment/dedicated-infrastructure). + + +Either run your own [iroh-dns-server](https://github.com/n0-computer/iroh/tree/main/iroh-dns-server) +on an internal zone, or drop address lookup entirely and hand out +`EndpointAddr` values through your existing service discovery. + + +`presets::N0` configures n0's relays and DNS. Build from +`presets::Empty` and add only what you run. + + + +```rust +use iroh::{Endpoint, RelayMap, RelayMode, RelayUrl, endpoint::presets}; + +let relay: RelayUrl = "https://relay.internal.example.com".parse()?; + +let endpoint = Endpoint::builder(presets::Empty) + .relay_mode(RelayMode::Custom(RelayMap::from_iter([relay]))) + .bind() + .await?; +``` + +For an air-gapped LAN with no relay at all, combine +[mDNS address lookup](/connecting/local-address-lookup) with +`RelayMode::Disabled`. Peers discover each other by multicast on +`224.0.0.251:5353` and connect directly. This requires that your switches +forward multicast on the segment and that client isolation is off. + +## Never sending data over a relay + +The request usually arrives as a policy statement: no application data may +transit a server we do not control. It is worth separating what is being asked +for, because iroh answers the confidentiality version and the control version +differently. + +### What a relay can and cannot see + +All iroh traffic is end-to-end encrypted between endpoints. A relay forwards +ciphertext and holds no key that can open it, whether the relay is ours, yours, +or a third party's. What a relay does observe is metadata: which endpoint IDs +are talking to each other, when, and how much. That is a real disclosure and a +reasonable thing to care about, but it is not the same as reading your data. + +If the concern is confidentiality, it is already addressed. If the concern is +metadata, jurisdiction, or data residency, the fix is to run the relay yourself, +as described in the previous section. Your relay, your network, your logs, and +nothing leaves the perimeter. + +### Turning relay data off entirely + +If you still need endpoints that will never send a byte through a relay: + +```rust +let endpoint = Endpoint::builder(presets::Empty) + .relay_mode(RelayMode::Disabled) + .bind() + .await?; +``` + +`clear_relay_transports()` does the same to a builder that already has relays +configured. + +### Why this is usually the wrong lever + +Disabling relays removes the fallback, but it also removes the machinery that +makes direct connections happen in the first place. + +- **Roughly one connection in ten stops working.** Symmetric NATs and strict + corporate firewalls do not permit hole punching. Those connections relay + today. Without a relay they fail outright, with no fallback and no error a + network administrator can act on. +- **You lose the rendezvous, not just the fallback.** The relay is how two peers + first reach each other and coordinate the simultaneous send that opens the + hole. With relays off, peers need another way to find each other: mDNS on the + same segment, or addresses you distribute yourself. An endpoint behind a NAT + with no other signaling channel cannot be dialed at all. +- **Connections get slower to establish.** Normally iroh sends the first bytes + over the relay immediately and upgrades to a direct path once hole punching + completes. Without a relay, nothing flows until hole punching succeeds or + fails. +- **Network changes become outages.** Moving between Wi-Fi and cellular, or a + DHCP lease change, invalidates direct paths. The relay normally carries + traffic during the seconds it takes to establish a new one. + +The cost is not evenly distributed either. It falls on whichever users happen to +sit behind the most restrictive networks, which is often the ones you can +least afford to break. + +### A relay that cannot carry payload + +If what you want is a server that assists NAT traversal but is architecturally +incapable of forwarding data, run the relay with relaying switched off: + +```toml +enable_relay = false +enable_quic_addr_discovery = true +``` + +The server then starts only the QUIC address discovery listener on UDP 7842. It +reports the reflexive address it observes and runs no relay endpoint at all, so +no configuration mistake on a client can cause data to flow through it. + + +This is an advanced configuration and it is not a drop-in replacement for a +relay. Clients still list the server in their relay map to use it for address +discovery, but they will not obtain a home relay from it and get no fallback +path, so every tradeoff in the previous section still applies. Test it against +your own fleet before committing to it. + + +## Locking down an internal relay + +### Firewall rules for the relay host + +These are **inbound** rules on the relay itself. Ports are derived from +`http_bind_addr` (default `[::]:80`) unless you set them explicitly. + +| Port | Transport | Purpose | Needed when | +|---|---|---|---| +| 443 | TCP | Relay WebSocket at `/relay`, latency probe at `/ping` | Always | +| 7842 | UDP | QUIC address discovery | `enable_quic_addr_discovery = true` | +| 80 | TCP | Let's Encrypt HTTP-01 challenge, captive-portal check | `cert_mode = "LetsEncrypt"` | +| 9090 | TCP | Prometheus metrics | Scraping only. Never expose this. | + + +The metrics server is **on by default** and binds `[::]:9090`, on all +interfaces. Set `metrics_bind_addr` to a loopback or management address, or set +`enable_metrics = false`. + + +An internal relay usually cannot complete a Let's Encrypt HTTP-01 challenge, +because that requires the hostname to be publicly resolvable and port 80 +reachable from the internet. Issue a certificate from your internal PKI instead: + +```toml +# relay.toml +enable_relay = true +enable_quic_addr_discovery = true +http_bind_addr = "[::]:80" +metrics_bind_addr = "127.0.0.1:9090" + +[tls] +https_bind_addr = "[::]:443" +quic_bind_addr = "[::]:7842" +cert_mode = "Manual" +manual_cert_path = "/etc/iroh-relay/relay.crt" +manual_key_path = "/etc/iroh-relay/relay.key" +``` + +Clients must then trust the issuing CA. iroh does **not** use the OS trust store +by default, so pair this with `CaTlsConfig::system()` or +`CaTlsConfig::custom_roots(...)`. See [TLS interception](#tls-interception). + +### Restricting who may use the relay + +Firewall rules control which *hosts* reach the relay. The relay's own `access` +config controls which *endpoints* it admits, and it is checked after the relay +handshake has cryptographically authenticated the endpoint ID, so an allowlist +entry cannot be spoofed by another endpoint. + +```toml +# Default: no restriction. +access = "everyone" + +# Fixed fleet: admit only these endpoint IDs (lowercase hex, as printed by +# `endpoint.id()`). +access.allowlist = [ + "523c7996bad77424cbdfd0f0f0b8b5d0d9f9c1b2a3e4f5061718293a4b5c6d7e", + "8f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0", +] + +# Or the inverse. +access.denylist = ["..."] + +# Shared secret: admit anyone presenting one of these bearer tokens. +# Overridable at runtime with IROH_RELAY_ACCESS_TOKEN. +access.shared_token = ["token-a", "token-b"] + +# Delegate to your own service. +access.http = { url = "https://inventory.internal.example.com/iroh-access", bearer_token = "..." } +``` + +The `http` mode sends a `POST` with the connecting endpoint's ID in an +`X-Iroh-Endpoint-Id` header, and admits the endpoint only on a `200` whose body +is `true`. Anything else is a denial. Its own bearer token can come from +`IROH_RELAY_HTTP_BEARER_TOKEN`. + +| Mode | Use when | +|---|---| +| `allowlist` | Small fixed fleet whose endpoint IDs you already manage | +| `shared_token` | Dynamic fleet; you want one rotatable secret, not per-device enrollment | +| `http` | You already have an inventory or device-management service to ask | +| `denylist` | You need to evict specific endpoints from an otherwise open relay | + +Clients present a token by attaching it to the relay map: + +```rust +use iroh::{Endpoint, RelayMap, RelayMode, RelayUrl, endpoint::presets}; + +let relay: RelayUrl = "https://relay.internal.example.com".parse()?; +let relay_map = RelayMap::from_iter([relay]).with_auth_token("token-a"); + +let endpoint = Endpoint::builder(presets::Empty) + .relay_mode(RelayMode::Custom(relay_map)) + .bind() + .await?; +``` + + +Tokens are looked up **per relay URL from the local relay map**. An endpoint +will dial whatever relay a peer publishes as its home relay, including one +absent from its own map, but it will present no token and be refused. Ship the +same relay map, with the same tokens, to every site. + + +## Split networks and multiple sites + +### How reachability actually works + +An endpoint picks one **home relay**, the lowest-latency reachable relay from +its configured map, and publishes it through address lookup. To reach a peer, +your endpoint connects to **that peer's home relay**, not to your own. It keeps +its home relay connection open permanently and closes other relay connections +after 60 seconds idle. + +So the requirement is not "both sites configured with the same relay." It is: + +> Each side must be able to reach the relay the *other* side selected. + +### Failure modes across two LANs + +| Situation | What happens | Fix | +|---|---|---| +| Site A reaches only relay-A, Site B only relay-B | Home relay selection converges per site. Peers publish relays the other cannot reach, and never connect. | Deploy a relay both sites reach, or make relay-A and relay-B mutually reachable and list both in both maps. | +| Both relays reachable, but Site B's map omits relay-A | Site B dials relay-A with no auth token and is refused at the handshake. Often looks direction-dependent or intermittent, since it depends on which relay each peer selected. | Ship an identical `RelayMap`, with the same URLs and tokens, to every site. | +| Self-hosted address lookup on Site A only | Site B cannot resolve or publish endpoint IDs. Connecting by explicit address still works. | Make the DNS server reachable from both sites, or distribute `EndpointAddr` out of band. | +| WAN allows TCP 443 but not UDP between sites | Everything relays across the WAN, including site-to-site traffic that could have been direct. | Allow UDP between site subnets. This is internal segmentation policy, not the perimeter firewall. | + +### What survives a partition + +Direct QUIC paths are independent of the relay once established. If the relay +becomes unreachable but a direct path is alive, **the connection keeps working**: +heartbeats continue every 5 seconds, and a path is torn down only after 15 +seconds of silence. + +What stops working during a partition is anything that needs the relay: +establishing *new* connections, re-dialing after a drop, and address publishing. + +For a site that must keep working through a total uplink loss, give endpoints a +path that never leaves the LAN: + +```rust +use iroh::{Endpoint, RelayMode, endpoint::presets}; +use iroh_mdns_address_lookup::MdnsAddressLookup; + +// Local-only: multicast discovery, direct LAN connections, no relay. +let endpoint = Endpoint::builder(presets::Empty) + .address_lookup(MdnsAddressLookup::builder()) + .relay_mode(RelayMode::Disabled) + .bind() + .await?; +``` + +Run mDNS *alongside* your normal relay configuration rather than instead of it, +and same-segment peers keep connecting while the uplink is down, without losing +cross-site connectivity when it returns. + +### Recommended topologies + +| Topology | Deploy | +|---|---| +| Single site | One internal relay. Add a second for redundancy. | +| Several sites on a corporate WAN | Two relays at well-connected sites, both in every endpoint's relay map, both reachable from every site. Home relay selection sorts out which is closer. | +| Sites with no WAN path between them | Accept the partition: each site is its own iroh network. Cross-site traffic needs a relay in a location both can reach, typically a DMZ or cloud host. | +| Site that must survive uplink loss | Relay config plus mDNS address lookup, so same-segment peers stay connected. | + +## Wireless networks + +Two access-point settings commonly defeat direct connections between devices on +the same SSID: + +- **Client isolation** (also "AP isolation", "station isolation", or + "peer-to-peer blocking") prevents wireless clients from addressing each other. + Direct LAN connections fail and everything relays. It is on by default on most + guest networks. +- **Multicast filtering** blocks mDNS address lookup. If you rely on local + discovery, enable multicast forwarding, or multicast-to-unicast conversion on + larger deployments. + +Neither breaks iroh. Both push traffic onto the relay path that could have +stayed on your own LAN. + +## Verifying the configuration + +Run [`iroh-doctor`](https://github.com/n0-computer/iroh-doctor) from a machine +on the network in question: + +```shell +cargo install iroh-doctor +iroh-doctor report +``` + +| Report field | What to look for | +|---|---| +| `udp` | `false` means UDP is blocked or filtered. Everything will relay. | +| `ipv4` / `ipv6` | Which families reached a relay over UDP. | +| `mapping_varies_by_dest_ip` | `true` indicates a symmetric NAT. Direct connections will mostly fail even with UDP open. | +| `preferred_relay` | Which relay was selected. Absent means no relay was reachable at all. Check TCP 443 and DNS first. | +| `relay_latency` | Per-relay latency. Missing entries are relays your firewall blocked. | +| `captive_portal` | A non-null value means an intercepting portal is in the path. | + +For endpoints already deployed on customer or branch networks you cannot run a +CLI on, use [Network Diagnostics](/iroh-services/net-diagnostics/usage). It runs +the same probes on demand from the Iroh Services dashboard against any online +endpoint in your project and returns the report to the dashboard. + +## Checklist for your network team + +- [ ] Allow outbound TCP 443 to your relay hostnames (required) +- [ ] Allow outbound HTTPS to `dns.iroh.link`, or replace address lookup +- [ ] Confirm your resolver returns TXT records for `dns.iroh.link` +- [ ] Allow outbound UDP 7842 to your relay hostnames (recommended) +- [ ] Allow outbound UDP to arbitrary destination ports (recommended) +- [ ] Set the UDP idle timeout to 30 seconds or more +- [ ] Add the corporate CA via `CaTlsConfig::system()` if TLS is intercepted +- [ ] Set the proxy with `proxy_from_env()` if egress requires one +- [ ] Turn off client isolation on wireless segments where peers should connect directly +- [ ] Verify with `iroh-doctor report` from inside the network + +If you run your own relay: + +- [ ] Open inbound TCP 443, and UDP 7842 if QUIC address discovery is enabled +- [ ] Bind the metrics server to loopback, or disable it +- [ ] Issue the relay certificate from your internal PKI and set `CaTlsConfig::system()` on clients +- [ ] Set `access` to something other than `everyone` +- [ ] Ship an identical relay map, with tokens, to every site diff --git a/docs.json b/docs.json index 4f5e079..6f0d201 100644 --- a/docs.json +++ b/docs.json @@ -142,6 +142,7 @@ ] }, "examples", + "deployment/enterprise-networks", "troubleshooting", "iroh-services/support", "about/faq" diff --git a/troubleshooting.mdx b/troubleshooting.mdx index 3fca523..3030271 100644 --- a/troubleshooting.mdx +++ b/troubleshooting.mdx @@ -143,3 +143,10 @@ The above output shows that the endpoint is using the `use1-1.relay.iroh.network To diagnose connectivity issues on your users' endpoints in production, use [Network Diagnostics](/iroh-services/net-diagnostics/usage). It runs the same kinds of probes as `iroh-doctor` (UDP connectivity, NAT type, relay latency, port mapping) but you trigger them from the Iroh Services dashboard against any of your project's online endpoints. The report comes back to the dashboard, so you can see what your user is experiencing without asking them to run a CLI tool. To enable diagnostics on an endpoint, grant the `NetDiagnosticsCap::GetAny` capability and run a `ClientHost`. See the [Network Diagnostics integration guide](/iroh-services/net-diagnostics/usage) for the full setup. + +## Corporate and managed networks + +If the network is one you or your customer administers, and the problem looks +like blocked UDP, an HTTPS proxy, TLS interception, or DNS filtering, see +[Enterprise Networks](/deployment/enterprise-networks) for the full list of +flows to allow and how to configure iroh around each restriction. From 084c164012dfb04ed63f7199498cd85a867c3b60 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Mon, 27 Jul 2026 19:09:05 -0700 Subject: [PATCH 02/18] docs: rewrite enterprise guide for non-specialist administrators The first draft assumed a network engineer at a large company. The real reader is closer to an IT manager at a small or medium business who has been handed a piece of software to approve, is wary of it, and does not spend their day writing firewall rules. Reorganized around what that reader has to decide rather than around iroh's internals: a one-minute summary, a direct answer to whether this defeats their firewall, a security and privacy FAQ, and troubleshooting organized by symptom. Network changes are now separated from application changes, since only the first kind is theirs to make. Terms are defined on first use and anchored to STUN and TURN, which anyone who has set up a conferencing system has already reasoned about once. Also drops the hole-punching-only relay server configuration. It is a sharp edge that does not belong on a page aimed at this reader. Co-Authored-By: Claude Opus 5 (1M context) --- deployment/enterprise-networks.mdx | 857 ++++++++++++++++------------- 1 file changed, 468 insertions(+), 389 deletions(-) diff --git a/deployment/enterprise-networks.mdx b/deployment/enterprise-networks.mdx index ab9f2d8..5bdad54 100644 --- a/deployment/enterprise-networks.mdx +++ b/deployment/enterprise-networks.mdx @@ -1,65 +1,125 @@ --- title: "Enterprise Networks" -description: "Firewall, proxy, DNS, and Wi-Fi configuration for running iroh on managed corporate networks" +description: "What iroh does on your network, what to allow through your firewall, and how to keep it under your control" --- -Running iroh on a managed corporate network takes one firewall rule: outbound -TCP 443 to your relay hostnames. Allowing UDP as well is what turns most -connections direct. There are no inbound rules, no port forwarding, no static -IPs, and no listening ports to expose. +If a team at your company wants to ship software built on iroh, this page tells +you what it will do on your network, what you need to allow, and what you can +control. + +## The one-minute version + +- **One firewall rule is required:** outbound HTTPS (TCP 443) to a short list of + hostnames. Nothing else is mandatory. +- **Nothing listens for inbound connections.** You do not open any ports inward, + forward any ports, or assign any static IP addresses. +- **Allowing outbound UDP is optional.** It makes connections faster and + cheaper. iroh works without it. +- **All traffic is encrypted end to end**, including traffic that passes through + a relay server. The relay operator cannot read it. +- **You can run all of the infrastructure yourself** if you do not want traffic + leaving your network. +- **You can block it.** There is no fallback that evades a firewall rule. + +If you have set up a video conferencing or VoIP system, iroh's requirements will +look familiar. It needs the same two things Teams, Zoom, and WebEx need: a way +for a device to learn its own public address, and a server that can pass traffic +along when two devices cannot reach each other directly. -Everything below is the detail behind those three sentences: every flow an -endpoint opens, what happens when each one is blocked, and how to configure iroh -around the restrictions a corporate network typically imposes. + +Some of the changes on this page are made in the application, not on your +network. Those are marked **For your developers** and include the code to hand +over. + + +## What iroh does on your network + +iroh is a networking library that your developers build into an application. Its +job is to connect two devices directly, so data moves between them instead of +through a central server. + +A device running iroh produces three kinds of traffic. + +**Relay connections.** Each device opens one long-lived encrypted connection to +a *relay server* over HTTPS on port 443. This is the role a TURN server plays in +a VoIP system. The relay introduces two devices to each other and carries their +traffic if they cannot reach each other directly. It is always used, at minimum +for the introduction. + +**Address lookup.** To find another device, iroh looks up a DNS record for it. +This is an ordinary DNS query through your own resolver. + +**Direct connections.** Once introduced, the two devices try to connect straight +to each other over UDP. This part is optional. If you block it, they keep using +the relay. -## Two levels of configuration +## Does this punch holes in my firewall? -| | What you allow | What you get | +No. The technique has an alarming name, so it is worth being precise about what +actually happens. + +Two devices, each behind its own firewall, send an outbound packet to each other +at roughly the same moment. Each firewall sees an outbound connection from a +device on its own network and allows the reply, under the same rule that lets a +browser load a website. Neither firewall is tricked, and neither is +reconfigured. + +In practical terms: + +- iroh never accepts unsolicited inbound connections. No port is exposed to the + internet. +- Every connection begins as outbound traffic from a device you control. +- If your firewall does not allow the reply, nothing is bypassed. The connection + falls back to the relay. +- If you block the relay hostnames too, iroh stops working. That is the intended + behavior, not something to defend against. + +One caveat on that last point. Blocking the hostnames listed below stops iroh in +its default configuration, which is what almost every application uses. But the +hostnames are a setting in the application, and iroh also supports other ways of +finding peers that your developers could enable. If you need a hard guarantee +rather than a sensible default, block outbound UDP as well, and ask your +developers to confirm what the application is actually configured with. + +## What to allow + +### Required + +| Allow | To | Why | |---|---|---| -| **Minimum** | Outbound TCP 443 to your relay hostnames | Every connection works. All traffic is relayed. | -| **Recommended** | The above, plus outbound UDP | Roughly nine connections in ten become direct: lower latency, higher throughput, and no relay egress | +| Outbound TCP 443 | Your relay hostnames | The relay connection itself, and the latency checks that pick the closest relay | +| Outbound TCP 443 | `dns.iroh.link` | Publishing the device's address so others can find it | +| Normal DNS (UDP/TCP 53) | Your own resolver | Looking up other devices | -Both are supported configurations. iroh detects which one it is on and adapts at -runtime. You do not configure a mode, and connections do not fail if UDP is -unavailable. +The relay connection is a standard HTTPS connection that then upgrades to a +WebSocket, which is the same thing a chat or trading application does. If you +run an application-aware firewall configured to permit only ordinary web +browsing on port 443, it may block the upgrade. Allow the relay hostnames as a +WebSocket destination. -## Traffic reference +### Recommended -All flows are **outbound-initiated**. Return traffic arrives on the same 5-tuple -and is permitted by any stateful firewall. +Allowing these lets devices connect directly instead of relaying everything. -| Traffic | Transport | Destination | Required | -|---|---|---|---| -| Relay connection | TCP 443 (TLS, WebSocket upgrade at `/relay`) | Your relay hostnames | **Yes** | -| Relay latency probe | TCP 443 (HTTPS `GET /ping`) | Every configured relay hostname | **Yes** | -| Address publishing | TCP 443 (HTTPS `PUT`, pkarr) | `dns.iroh.link` | Default preset only | -| Address resolution | UDP/TCP 53 via your own resolver | TXT records under `dns.iroh.link` | Default preset only | -| QUIC address discovery | UDP 7842 | Your relay hostnames | Recommended | -| Direct QUIC and hole punching | UDP, ephemeral source port to arbitrary destination port | Any peer | Recommended | -| Port mapping | UDP 1900 (SSDP), UDP 5351 (PCP/NAT-PMP) | Local gateway only | Optional | -| mDNS address lookup | UDP 5353 to `224.0.0.251`, `ff02::fb` | Local segment only | Off by default | - -By default an endpoint binds `0.0.0.0:0` and `[::]:0`, an OS-assigned ephemeral -UDP port on all interfaces. To pin it to a fixed port for a narrower egress -rule: +| Allow | To | Why | +|---|---|---| +| Outbound UDP 7842 | Your relay hostnames | Lets a device learn its own public address. This is the job STUN does in a VoIP system. | +| Outbound UDP, any destination port | Any address | The direct connection between two devices | -```rust -use iroh::{Endpoint, endpoint::presets}; +### Optional -let endpoint = Endpoint::builder(presets::N0) - .clear_ip_transports() - .bind_addr("0.0.0.0:49555")? - .bind() - .await?; -``` +| Traffic | What it is | Default | +|---|---|---| +| UDP 1900 and UDP 5351 on your local network | Asking the router to open a port automatically (UPnP, NAT-PMP) | On, and safe to turn off | +| UDP 5353 multicast on the local network | Finding devices on the same office network | Off unless your developers enable it | -Iroh Services features, [metrics](/iroh-services/metrics/index) and -[network diagnostics](/iroh-services/net-diagnostics/usage), travel over your -existing iroh connections. They need no additional firewall rules. +### Hostnames to allow -## Hostnames to allowlist +The list depends on which version of iroh the application is built with. Ask +your developers, because the two sets are on **different domains** and a rule +written for one will not match the other. -With the default `presets::N0` configuration: +**iroh 1.0 and later**, with the default configuration: ``` use1-1.relay.n0.iroh.link @@ -69,219 +129,305 @@ aps1-1.relay.n0.iroh.link dns.iroh.link ``` - -iroh releases before 1.0 used `use1-1.relay.iroh.network`, -`euw1-1.relay.iroh.network`, and `aps1-1.relay.iroh.network`. Include those too -if you have older clients in the fleet. - +**Pre-1.0 versions of iroh** use `iroh.network` rather than `iroh.link`: + +``` +use1-1.relay.iroh.network +euw1-1.relay.iroh.network +aps1-1.relay.iroh.network +``` + +If you are not certain which version is in use, allow both lists. + +These servers are run by [n0](https://n0.computer), the company that builds +iroh. If you run your own relay, this list becomes hostnames you control +instead. See [Keeping traffic inside your network](#keeping-traffic-inside-your-network). -If you deploy [dedicated relays](/deployment/dedicated-infrastructure), your -allowlist becomes a short list of hostnames you own, and endpoints stop -contacting n0 infrastructure entirely. This is the recommended posture for -production on a managed network. See -[Running entirely inside your perimeter](#running-entirely-inside-your-perimeter). - -## Working with a UDP-restricted network - -Many enterprise networks restrict UDP, on the reasoning that it is harder to -track and that older firewalls cannot inspect it statefully. iroh is built to -work either way, so this does not need to be a blocker. - -### iroh works with UDP fully blocked - -The relay path is a WebSocket over TLS on TCP 443. To a firewall it is an -ordinary long-lived HTTPS connection, and it is subject to the same policy, -logging, and allowlisting as any other HTTPS destination. If your network passes -HTTPS, it passes iroh. - -### What blocking UDP costs - -Every byte between two peers takes a round trip through a relay. Expect added -latency proportional to the distance to the nearest relay, throughput bounded by -the relay rather than by the peers' links, and relay egress you either pay for -or contend for on shared public infrastructure. Nothing breaks; it is slower and -more expensive. - -### Why allowing UDP is usually reasonable - -- **It is QUIC, not raw datagrams.** Connection-oriented, congestion-controlled, - TLS 1.3 handshake, retransmission, and flow control. It behaves like a TCP - flow that happens to ride UDP. -- **Your network probably already carries it.** Chrome, Edge, and Safari use - HTTP/3 over UDP 443 by default for a large share of the web. iroh's traffic is - the same protocol family. -- **Flows are always outbound-first.** No inbound rule, no port forward, and no - exposed listener. A stateful firewall sees an outbound flow and its replies. -- **Idle mappings do not linger.** iroh sends a heartbeat every 5 seconds on an - active path and tears a path down after 15 seconds without traffic. A UDP idle - timeout of 30 seconds or more on your firewall is comfortable, and abandoned - state ages out quickly. -- **Inspection is not the tradeoff.** All iroh traffic is end-to-end encrypted - regardless of path. The TCP 443 relay path is no more inspectable than the UDP - path: a middlebox sees ciphertext either way. If your policy requires - visibility into the payload, the answer is to run relays inside your perimeter - and inspect at the application, not to block UDP. - -### Partial UDP allowances do not help - -Allowing UDP only to port 443 buys nothing. Direct paths use ephemeral ports on -both ends, and QUIC address discovery targets port 7842 on the relay. It is -effectively all-or-relay for internet peers. +If your developers use the Iroh Services dashboard for monitoring or +diagnostics, that traffic travels over the connections above and needs no extra +firewall rules. + +## Security and privacy questions + +### Can the relay operator read our data? + +No. Traffic is encrypted end to end between the two devices. A relay forwards +encrypted bytes and holds no key that can decrypt them. This is true of the +public relays run by n0 and of any relay you run yourself. + +### What can a relay see? + +The identities of the devices connected to it, which pairs are exchanging +traffic, when, and how much. It cannot see file names, message contents, or +anything else inside the connection. + +That connection metadata is a real disclosure, and it is a fair thing to care +about. If it matters to you, run your own relay and it stays with you. + +### Can we inspect or log the contents? + +No, and this is not a property of the UDP path specifically. The relay +connection on port 443 is also encrypted end to end, so a TLS inspection +appliance sees encrypted data either way. + +If your policy requires visibility into content, the place to get it is the +application, not the network. Your developers can log what the application sends +and receives. Running your own relay gives you full control of the path and the +connection metadata, but not the contents. + +### What leaves our network with the default configuration? + +Four things: + +1. An encrypted connection to one of the n0 relay servers listed above. +2. A small signed record published to `dns.iroh.link`, containing the device's + public key and which relay it is using. **Internal IP addresses are not + published by default.** iroh deliberately filters them out to avoid leaking + them to a public server. +3. DNS lookups for the devices you connect to. +4. If direct connections are allowed, UDP traffic to the other device, which + reveals your public IP address to that device. This is true of any direct + connection between two computers. + +### Can we control which of our devices are allowed to use it? + +Yes, if you run your own relay. You can allow a specific list of devices, or +require a shared password. See +[Controlling who can use your relay](#controlling-who-can-use-your-relay). + +On the public n0 relays, no. Any application with the relay URL can connect to +them. + +### Can we turn parts of it off? + +Yes. Direct connections, automatic router port opening, local network discovery, +and even relaying itself can each be disabled independently. Each one is a +change your developers make in the application. The relevant sections below have +the details. + +## Common situations + +### We block outbound UDP + +**What you will see:** everything works, but more slowly, and all traffic goes +through the relay. + +**What is happening:** devices cannot reach each other directly, so every byte +takes a round trip through a relay server. + +**What to do:** nothing, if you are willing to accept the cost. It is worth +understanding the trade-off before you decide. + +The cost of blocking UDP is added delay proportional to how far away the nearest +relay is, transfer speeds limited by the relay rather than by your own internet +connection, and bandwidth charges on the relay that you either pay for directly +or share with other users of the public relays. + +Why allowing it is usually reasonable: + +- **This is QUIC, not loose datagrams.** QUIC is a connection-oriented, + congestion-controlled protocol with a TLS 1.3 handshake. It behaves like a TCP + connection that happens to run over UDP. +- **Your network almost certainly carries it already.** Chrome, Edge, and Safari + use HTTP/3 over UDP port 443 for a large share of the web, and every major + conferencing product uses UDP for media. +- **It is always outbound-first.** No inbound rule, no port forward, no exposed + service. +- **Idle entries clear quickly.** iroh sends a keepalive every 5 seconds while a + connection is active and gives up on an idle path after 15 seconds. A UDP idle + timeout of 30 seconds or more on your firewall is comfortable. + +One thing that does not work: allowing UDP only to port 443. Direct connections +use randomly assigned high-numbered ports on both ends, so a rule scoped to +port 443 gains you nothing. The destination port cannot be narrowed. If you need +a tighter rule than "any destination port", your developers can pin the +application to a fixed source port with `.bind_addr("0.0.0.0:49555")`, and you +can scope the rule to that source port instead. -The exception worth knowing: **two peers on the same corporate network connect -directly over the LAN**, governed by your internal segmentation policy rather -than the perimeter firewall. Branch-office and same-site traffic can stay -direct and never touch a relay even when the perimeter blocks UDP outbound. +Two devices on the same office network connect directly to each other regardless +of whether your perimeter firewall allows UDP. That traffic is governed by your +internal network policy, so desk-to-desk transfers can stay local even when +outbound UDP is blocked. -### Forcing relay-only - -On a network where UDP is blackholed, you can skip UDP entirely. This removes -probe traffic that shows up as noise in IDS logs and speeds up startup, since -iroh no longer waits for probes that will never be answered. +**For your developers.** On a network where UDP is silently dropped, the +application can skip trying, which removes probe traffic that shows up in +intrusion detection logs and speeds up startup: ```rust use iroh::endpoint::{presets, PortmapperConfig}; use iroh::Endpoint; let endpoint = Endpoint::builder(presets::N0) - // No UDP sockets at all: relay transport only. + // No UDP at all: relay connections only. .clear_ip_transports() - // Skips SSDP multicast gateway discovery. + // Do not ask the router to open ports. .portmapper_config(PortmapperConfig::Disabled) .bind() .await?; ``` -`PortmapperConfig::Disabled` is worth setting on its own even when UDP is -allowed. The UPnP discovery step multicasts SSDP to `239.255.255.250:1900`, -which is a common IDS signature and raises firewall prompts on some client -operating systems. Disabling it costs you gateway-assisted port mapping, which -most enterprise gateways do not offer anyway. +### We inspect TLS traffic -## HTTP proxies +**What you will see:** the application cannot connect at all, and logs show a +certificate error, even though your inspection appliance's certificate authority +is installed on the machine. -If your network requires egress through an HTTP proxy, iroh can tunnel its relay -and HTTPS traffic through it with `CONNECT`: +**What is happening:** iroh checks relay certificates against a list of +authorities built into the application. It does not read the operating system's +certificate store, so the certificate authority your device management tool +installed is not trusted. + +**What to do:** this is an application change, not a network one. + +**For your developers.** Switch iroh to the operating system's certificate +store, which contains the corporate certificate authority: ```rust +use iroh::tls::CaTlsConfig; + let endpoint = Endpoint::builder(presets::N0) - // Reads HTTP_PROXY, http_proxy, HTTPS_PROXY, https_proxy, in that order. - .proxy_from_env() + .ca_tls_config(CaTlsConfig::system()) .bind() .await?; ``` -Use `.proxy_url(url)` to set it explicitly instead. The proxy applies to relay -and HTTPS traffic only; UDP paths are unaffected, and a proxied deployment -typically ends up relay-only in practice. +This affects the relay connection only. Direct connections between two devices +identify each other by their own cryptographic keys rather than by website +certificates, so an inspection appliance cannot sit in the middle of one. In +practice it would never be in that path anyway. -## TLS interception +### All traffic must go through a proxy -By default iroh verifies relay certificates against a **compiled-in copy of the -Mozilla root store**. It does not consult the operating system's trust store, so -a TLS-intercepting proxy presenting a corporate CA will cause relay connections -to fail with a certificate error even though the CA is installed on the machine. +**What you will see:** the application cannot reach a relay at all, because it +tries to connect directly rather than through the proxy. -Switch to the platform verifier so the CA your MDM distributed is honored: +**What to do:** iroh can send its relay traffic through an HTTP proxy using +`CONNECT`, the same way a browser does. This is an application change. -```rust -use iroh::tls::CaTlsConfig; +**For your developers:** +```rust let endpoint = Endpoint::builder(presets::N0) - .ca_tls_config(CaTlsConfig::system()) + // Reads HTTP_PROXY, http_proxy, HTTPS_PROXY, https_proxy, in that order. + .proxy_from_env() .bind() .await?; ``` -`CaTlsConfig::custom_roots(certs)` trusts only the roots you supply, and -`CaTlsConfig::system().with_extra_roots(certs)` adds to the OS set. +Use `.proxy_url(url)` to set the address explicitly. A proxy only carries the +relay connection, so a proxied deployment usually ends up relaying everything. - -This affects the relay and HTTPS paths only. Peer-to-peer QUIC connections -authenticate with raw public keys bound to endpoint IDs and cannot be -intercepted: a middlebox that substitutes a certificate only causes the -handshake to be rejected. In practice no proxy sits in a peer-to-peer path. - +### Our DNS filtering blocks it + +**What you will see:** connections work when the application is given an address +directly, but fail when it tries to find a device by its identity. That split is +a good sign the problem is DNS rather than the firewall. + +**What is happening:** iroh finds devices by looking up a DNS TXT record under +`dns.iroh.link`. Two common configurations break this. Some DNS security +products block or strip TXT records because attackers sometimes use them to +smuggle data. Some internal DNS servers do not forward lookups for outside +domains such as `iroh.link`. + +**What to do:** allow `dns.iroh.link` in your DNS filtering policy. If you would +rather not allow it at all, your developers can turn off DNS-based lookup and +distribute addresses through whatever system you already use. + +### Guest Wi-Fi or wireless client isolation -## DNS filtering +**What you will see:** devices on the same wireless network relay their traffic +instead of connecting directly. -With the default preset, resolving a peer by its endpoint ID means a TXT lookup -for `_iroh..dns.iroh.link` through your normal resolver. Two -enterprise patterns break this: +**What is happening:** two access point settings prevent local connections. +*Client isolation* (also called AP isolation, station isolation, or peer-to-peer +blocking) stops wireless devices from addressing each other, and is on by +default on most guest networks. *Multicast filtering* prevents devices from +finding each other on the local network. -- **TXT-record filtering.** DNS security products that block or strip TXT - responses as a DNS-tunneling heuristic will silently break address lookup. -- **Split-horizon resolvers** that do not forward queries for `iroh.link`. +**What to do:** turn client isolation off on segments where your devices should +be able to reach each other. Neither setting breaks iroh. Both just push traffic +onto the relay that could have stayed on your own network. -The symptom is distinctive: connections by explicit address succeed, but -connecting by endpoint ID fails to resolve. Allowlist `dns.iroh.link` in your -resolver policy, or drop DNS-based lookup and distribute addresses out of band. +### Automatic port opening -## Running entirely inside your perimeter +By default, iroh asks the local router to open a port using UPnP and NAT-PMP. +Most business-grade gateways do not offer this and ignore the request. The +discovery step sends a multicast message to `239.255.255.250:1900`, which is a +common intrusion detection signature and can trigger firewall prompts on some +computers. -For deployments where no iroh traffic may leave the network, replace all three -pieces of shared infrastructure: +Turning it off is a reasonable default on a managed network. It costs you +nothing that your gateway was going to provide. + +**For your developers:** `.portmapper_config(PortmapperConfig::Disabled)` on the +endpoint builder, as in the UDP example above. + +## Keeping traffic inside your network + +If you would rather no iroh traffic left your network at all, you can replace +every piece of shared infrastructure with your own. This is the right answer to +most concerns about relays, third-party servers, and data residency. + +There are three pieces, and you can replace them independently. - -Run the [relay binary](https://github.com/n0-computer/iroh/tree/main/iroh-relay) -on hosts your endpoints can reach, and point clients at them with -`RelayMode::Custom`. See [Dedicated -Infrastructure](/deployment/dedicated-infrastructure). + +Run the [relay software](https://github.com/n0-computer/iroh/tree/main/iroh-relay) +on servers your devices can reach, and have your developers point the +application at them. See [Dedicated +Infrastructure](/deployment/dedicated-infrastructure) for the deployment guide, +and the sections below for firewall rules and access control. - -Either run your own [iroh-dns-server](https://github.com/n0-computer/iroh/tree/main/iroh-dns-server) -on an internal zone, or drop address lookup entirely and hand out -`EndpointAddr` values through your existing service discovery. + +Either run an internal +[iroh-dns-server](https://github.com/n0-computer/iroh/tree/main/iroh-dns-server), +or drop address lookup entirely and distribute device addresses through a system +you already have. - -`presets::N0` configures n0's relays and DNS. Build from -`presets::Empty` and add only what you run. + +This is a one-line change for your developers. It removes the n0 relays and DNS +server from the application's configuration. +**For your developers:** + ```rust use iroh::{Endpoint, RelayMap, RelayMode, RelayUrl, endpoint::presets}; let relay: RelayUrl = "https://relay.internal.example.com".parse()?; +// presets::Empty starts with no n0 infrastructure configured. let endpoint = Endpoint::builder(presets::Empty) .relay_mode(RelayMode::Custom(RelayMap::from_iter([relay]))) .bind() .await?; ``` -For an air-gapped LAN with no relay at all, combine -[mDNS address lookup](/connecting/local-address-lookup) with -`RelayMode::Disabled`. Peers discover each other by multicast on -`224.0.0.251:5353` and connect directly. This requires that your switches -forward multicast on the segment and that client isolation is off. - -## Never sending data over a relay +For an isolated network with no internet access at all, devices can find each +other by multicast on the local network and connect directly, with no relay +involved. This requires your switches to forward multicast and client isolation +to be off. Your developers will find the setup in +[mDNS address lookup](/connecting/local-address-lookup). -The request usually arrives as a policy statement: no application data may -transit a server we do not control. It is worth separating what is being asked -for, because iroh answers the confidentiality version and the control version -differently. +### Can we stop data going through a relay entirely? -### What a relay can and cannot see +This request usually arrives as a policy statement: no company data may pass +through a server we do not control. It is worth separating the two things that +might mean, because they have different answers. -All iroh traffic is end-to-end encrypted between endpoints. A relay forwards -ciphertext and holds no key that can open it, whether the relay is ours, yours, -or a third party's. What a relay does observe is metadata: which endpoint IDs -are talking to each other, when, and how much. That is a real disclosure and a -reasonable thing to care about, but it is not the same as reading your data. +**If the concern is that someone could read the data,** it is already addressed. +A relay forwards encrypted bytes and cannot decrypt them. Nobody operating a +relay, including n0, can see inside your traffic. -If the concern is confidentiality, it is already addressed. If the concern is -metadata, jurisdiction, or data residency, the fix is to run the relay yourself, -as described in the previous section. Your relay, your network, your logs, and -nothing leaves the perimeter. +**If the concern is control, jurisdiction, or data residency,** the answer is to +run the relay yourself, as described above. Your server, your network, your +logs, and nothing leaves your perimeter. -### Turning relay data off entirely - -If you still need endpoints that will never send a byte through a relay: +Switching relaying off entirely is a third option. It is a one-line change for +your developers: ```rust let endpoint = Endpoint::builder(presets::Empty) @@ -290,84 +436,57 @@ let endpoint = Endpoint::builder(presets::Empty) .await?; ``` -`clear_relay_transports()` does the same to a builder that already has relays -configured. - -### Why this is usually the wrong lever +It is usually the wrong choice. It removes the fallback, but it also removes the +machinery that makes direct connections work in the first place. -Disabling relays removes the fallback, but it also removes the machinery that -makes direct connections happen in the first place. +- **Roughly one connection in ten stops working.** Stricter firewalls and + certain router types do not allow two devices to connect directly. Those + connections use a relay today. Without one they simply fail, with no fallback + and no error you can fix from the network side. +- **You lose the introduction, not just the fallback.** The relay is how two + devices first find each other and coordinate the simultaneous connection + attempt. Without it, a device behind a firewall cannot be reached at all + unless you provide another way for peers to find each other. +- **Connections take longer to start.** Normally the first data flows over the + relay immediately and moves to a direct connection a moment later. Without a + relay, nothing flows until the direct connection succeeds or fails. +- **Network changes turn into outages.** Switching from Wi-Fi to a mobile + connection, or a DHCP lease change, breaks the direct path. The relay normally + carries traffic during the seconds it takes to rebuild one. -- **Roughly one connection in ten stops working.** Symmetric NATs and strict - corporate firewalls do not permit hole punching. Those connections relay - today. Without a relay they fail outright, with no fallback and no error a - network administrator can act on. -- **You lose the rendezvous, not just the fallback.** The relay is how two peers - first reach each other and coordinate the simultaneous send that opens the - hole. With relays off, peers need another way to find each other: mDNS on the - same segment, or addresses you distribute yourself. An endpoint behind a NAT - with no other signaling channel cannot be dialed at all. -- **Connections get slower to establish.** Normally iroh sends the first bytes - over the relay immediately and upgrades to a direct path once hole punching - completes. Without a relay, nothing flows until hole punching succeeds or - fails. -- **Network changes become outages.** Moving between Wi-Fi and cellular, or a - DHCP lease change, invalidates direct paths. The relay normally carries - traffic during the seconds it takes to establish a new one. +The cost also falls unevenly. It lands on whoever is behind the most restrictive +network, which is often a customer or a remote worker rather than someone you +can help. -The cost is not evenly distributed either. It falls on whichever users happen to -sit behind the most restrictive networks, which is often the ones you can -least afford to break. +For nearly every deployment, running your own relay is the better answer. You +get the control you were after, and your users keep the connectivity. -### A relay that cannot carry payload +## Running your own relay -If what you want is a server that assists NAT traversal but is architecturally -incapable of forwarding data, run the relay with relaying switched off: +### Firewall rules for the relay server -```toml -enable_relay = false -enable_quic_addr_discovery = true -``` - -The server then starts only the QUIC address discovery listener on UDP 7842. It -reports the reflexive address it observes and runs no relay endpoint at all, so -no configuration mistake on a client can cause data to flow through it. - - -This is an advanced configuration and it is not a drop-in replacement for a -relay. Clients still list the server in their relay map to use it for address -discovery, but they will not obtain a home relay from it and get no fallback -path, so every tradeoff in the previous section still applies. Test it against -your own fleet before committing to it. - +These are **inbound** rules on the relay server itself. -## Locking down an internal relay - -### Firewall rules for the relay host - -These are **inbound** rules on the relay itself. Ports are derived from -`http_bind_addr` (default `[::]:80`) unless you set them explicitly. - -| Port | Transport | Purpose | Needed when | +| Port | Protocol | Purpose | Needed when | |---|---|---|---| -| 443 | TCP | Relay WebSocket at `/relay`, latency probe at `/ping` | Always | -| 7842 | UDP | QUIC address discovery | `enable_quic_addr_discovery = true` | -| 80 | TCP | Let's Encrypt HTTP-01 challenge, captive-portal check | `cert_mode = "LetsEncrypt"` | -| 9090 | TCP | Prometheus metrics | Scraping only. Never expose this. | +| 443 | TCP | The relay service and its latency checks | Always | +| 7842 | UDP | Telling devices their public address | Address discovery is enabled | +| 80 | TCP | Let's Encrypt certificate validation | You use Let's Encrypt for the certificate | +| 9090 | TCP | Prometheus monitoring | Your monitoring system only. Never expose this. | -The metrics server is **on by default** and binds `[::]:9090`, on all -interfaces. Set `metrics_bind_addr` to a loopback or management address, or set +The monitoring port is **enabled by default and listens on every network +interface**. Set `metrics_bind_addr` to a loopback or management address, or set `enable_metrics = false`. -An internal relay usually cannot complete a Let's Encrypt HTTP-01 challenge, -because that requires the hostname to be publicly resolvable and port 80 -reachable from the internet. Issue a certificate from your internal PKI instead: +An internal relay usually cannot use Let's Encrypt, because that requires the +hostname to be resolvable from the public internet and port 80 reachable from +outside. Issue a certificate from your own internal certificate authority +instead: ```toml # relay.toml -enable_relay = true enable_quic_addr_discovery = true http_bind_addr = "[::]:80" metrics_bind_addr = "127.0.0.1:9090" @@ -380,52 +499,50 @@ manual_cert_path = "/etc/iroh-relay/relay.crt" manual_key_path = "/etc/iroh-relay/relay.key" ``` -Clients must then trust the issuing CA. iroh does **not** use the OS trust store -by default, so pair this with `CaTlsConfig::system()` or -`CaTlsConfig::custom_roots(...)`. See [TLS interception](#tls-interception). +Because iroh does not read the operating system's certificate store by default, +your developers also need to point the application at the right authority. See +[We inspect TLS traffic](#we-inspect-tls-traffic) for the code. -### Restricting who may use the relay +### Controlling who can use your relay -Firewall rules control which *hosts* reach the relay. The relay's own `access` -config controls which *endpoints* it admits, and it is checked after the relay -handshake has cryptographically authenticated the endpoint ID, so an allowlist -entry cannot be spoofed by another endpoint. +Firewall rules control which *machines* can reach the relay. The relay's own +`access` setting controls which *devices* it will serve. The check happens after +the relay has cryptographically verified the device's identity, so an entry on +the allow list cannot be impersonated. ```toml -# Default: no restriction. +# Default: anyone who can reach it. access = "everyone" -# Fixed fleet: admit only these endpoint IDs (lowercase hex, as printed by -# `endpoint.id()`). +# Allow a fixed list of devices, identified by public key. access.allowlist = [ "523c7996bad77424cbdfd0f0f0b8b5d0d9f9c1b2a3e4f5061718293a4b5c6d7e", "8f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0", ] -# Or the inverse. +# Or block specific devices and allow the rest. access.denylist = ["..."] -# Shared secret: admit anyone presenting one of these bearer tokens. -# Overridable at runtime with IROH_RELAY_ACCESS_TOKEN. +# Require a shared password. Can also be set with the +# IROH_RELAY_ACCESS_TOKEN environment variable. access.shared_token = ["token-a", "token-b"] -# Delegate to your own service. +# Ask your own service for each device. access.http = { url = "https://inventory.internal.example.com/iroh-access", bearer_token = "..." } ``` -The `http` mode sends a `POST` with the connecting endpoint's ID in an -`X-Iroh-Endpoint-Id` header, and admits the endpoint only on a `200` whose body -is `true`. Anything else is a denial. Its own bearer token can come from -`IROH_RELAY_HTTP_BEARER_TOKEN`. - -| Mode | Use when | +| Option | Use when | |---|---| -| `allowlist` | Small fixed fleet whose endpoint IDs you already manage | -| `shared_token` | Dynamic fleet; you want one rotatable secret, not per-device enrollment | -| `http` | You already have an inventory or device-management service to ask | -| `denylist` | You need to evict specific endpoints from an otherwise open relay | +| `allowlist` | You have a small fixed set of devices and already track their identities | +| `shared_token` | Devices come and go, and you want one password you can rotate | +| `http` | You already run an inventory or device management system that can answer | +| `denylist` | You need to remove specific devices from an otherwise open relay | -Clients present a token by attaching it to the relay map: +The `http` option sends a `POST` to your service with the device's identity in +an `X-Iroh-Endpoint-Id` header, and admits the device only if you reply `200` +with a body of `true`. + +**For your developers.** A device presents the shared password like this: ```rust use iroh::{Endpoint, RelayMap, RelayMode, RelayUrl, endpoint::presets}; @@ -439,131 +556,93 @@ let endpoint = Endpoint::builder(presets::Empty) .await?; ``` - -Tokens are looked up **per relay URL from the local relay map**. An endpoint -will dial whatever relay a peer publishes as its home relay, including one -absent from its own map, but it will present no token and be refused. Ship the -same relay map, with the same tokens, to every site. - - -## Split networks and multiple sites +## Multiple offices and sites -### How reachability actually works +Skip this section if everything runs at one site. -An endpoint picks one **home relay**, the lowest-latency reachable relay from -its configured map, and publishes it through address lookup. To reach a peer, -your endpoint connects to **that peer's home relay**, not to your own. It keeps -its home relay connection open permanently and closes other relay connections -after 60 seconds idle. +Each device picks a *home relay*, the one that answers fastest, and publishes +which one it chose. To reach a device, another device connects to **that +device's** home relay, not its own. So the requirement is not that both sites +use the same relay. It is that each side can reach the relay the other side +picked. -So the requirement is not "both sites configured with the same relay." It is: +That distinction causes most multi-site problems. -> Each side must be able to reach the relay the *other* side selected. - -### Failure modes across two LANs - -| Situation | What happens | Fix | +| What you set up | What goes wrong | What to do | |---|---|---| -| Site A reaches only relay-A, Site B only relay-B | Home relay selection converges per site. Peers publish relays the other cannot reach, and never connect. | Deploy a relay both sites reach, or make relay-A and relay-B mutually reachable and list both in both maps. | -| Both relays reachable, but Site B's map omits relay-A | Site B dials relay-A with no auth token and is refused at the handshake. Often looks direction-dependent or intermittent, since it depends on which relay each peer selected. | Ship an identical `RelayMap`, with the same URLs and tokens, to every site. | -| Self-hosted address lookup on Site A only | Site B cannot resolve or publish endpoint IDs. Connecting by explicit address still works. | Make the DNS server reachable from both sites, or distribute `EndpointAddr` out of band. | -| WAN allows TCP 443 but not UDP between sites | Everything relays across the WAN, including site-to-site traffic that could have been direct. | Allow UDP between site subnets. This is internal segmentation policy, not the perimeter firewall. | - -### What survives a partition - -Direct QUIC paths are independent of the relay once established. If the relay -becomes unreachable but a direct path is alive, **the connection keeps working**: -heartbeats continue every 5 seconds, and a path is torn down only after 15 -seconds of silence. - -What stops working during a partition is anything that needs the relay: -establishing *new* connections, re-dialing after a drop, and address publishing. - -For a site that must keep working through a total uplink loss, give endpoints a -path that never leaves the LAN: - -```rust -use iroh::{Endpoint, RelayMode, endpoint::presets}; -use iroh_mdns_address_lookup::MdnsAddressLookup; - -// Local-only: multicast discovery, direct LAN connections, no relay. -let endpoint = Endpoint::builder(presets::Empty) - .address_lookup(MdnsAddressLookup::builder()) - .relay_mode(RelayMode::Disabled) - .bind() - .await?; -``` - -Run mDNS *alongside* your normal relay configuration rather than instead of it, -and same-segment peers keep connecting while the uplink is down, without losing -cross-site connectivity when it returns. - -### Recommended topologies - -| Topology | Deploy | +| A relay at each site, neither reachable from the other | Devices at each site pick their local relay. Neither site can reach the other's, so cross-site connections never work. | Deploy one relay both sites can reach, or make both relays reachable from both sites and list both in the application configuration. | +| Both relays reachable, but the application at one site is only configured with one of them | Devices dial the other relay without the password and get refused. Often looks intermittent or one-directional, because it depends which relay each device happened to pick. | Ship the same relay list and the same password to every site. | +| An internal address lookup server at one site only | The other site cannot find devices by identity. Connections by explicit address still work. | Make the lookup server reachable from both sites, or distribute addresses another way. | +| Site-to-site links that allow TCP 443 but not UDP | Traffic between two of your own offices goes out to a relay and back. | Allow UDP between your site subnets. This is an internal routing policy question, not a perimeter one. | + +**What survives a network outage.** Connections that are already direct keep +working when the relay becomes unreachable, because they no longer depend on it. +What stops is anything needing the relay: starting new connections, reconnecting +after a drop, and publishing addresses. + +If a site has to keep working through a total internet outage, ask your +developers to enable local network discovery alongside the normal relay +configuration. Devices on the same network segment will keep finding each other +while the link is down, and cross-site connections resume when it returns. + +| Your situation | What to deploy | |---|---| -| Single site | One internal relay. Add a second for redundancy. | -| Several sites on a corporate WAN | Two relays at well-connected sites, both in every endpoint's relay map, both reachable from every site. Home relay selection sorts out which is closer. | -| Sites with no WAN path between them | Accept the partition: each site is its own iroh network. Cross-site traffic needs a relay in a location both can reach, typically a DMZ or cloud host. | -| Site that must survive uplink loss | Relay config plus mDNS address lookup, so same-segment peers stay connected. | - -## Wireless networks - -Two access-point settings commonly defeat direct connections between devices on -the same SSID: +| One site | One internal relay. Add a second for redundancy. | +| Several sites on a company WAN | Two relays at well-connected sites, both reachable from everywhere, both in the application configuration. Devices sort out which is closer. | +| Sites with no network path between them | Each site is its own separate network. Connecting them needs a relay somewhere both can reach, such as a DMZ or a cloud server. | +| A site that must survive losing its internet link | Normal relay configuration plus local network discovery. | -- **Client isolation** (also "AP isolation", "station isolation", or - "peer-to-peer blocking") prevents wireless clients from addressing each other. - Direct LAN connections fail and everything relays. It is on by default on most - guest networks. -- **Multicast filtering** blocks mDNS address lookup. If you rely on local - discovery, enable multicast forwarding, or multicast-to-unicast conversion on - larger deployments. +## Testing your configuration -Neither breaks iroh. Both push traffic onto the relay path that could have -stayed on your own LAN. - -## Verifying the configuration - -Run [`iroh-doctor`](https://github.com/n0-computer/iroh-doctor) from a machine -on the network in question: +Run the diagnostic tool on a machine on the network you want to test. It needs +[Rust](https://rustup.rs) installed, so you may prefer to ask your developers to +run it and send you the output. ```shell cargo install iroh-doctor iroh-doctor report ``` -| Report field | What to look for | +The output is long. These are the lines that matter: + +| Line | What it tells you | |---|---| -| `udp` | `false` means UDP is blocked or filtered. Everything will relay. | -| `ipv4` / `ipv6` | Which families reached a relay over UDP. | -| `mapping_varies_by_dest_ip` | `true` indicates a symmetric NAT. Direct connections will mostly fail even with UDP open. | -| `preferred_relay` | Which relay was selected. Absent means no relay was reachable at all. Check TCP 443 and DNS first. | -| `relay_latency` | Per-relay latency. Missing entries are relays your firewall blocked. | -| `captive_portal` | A non-null value means an intercepting portal is in the path. | - -For endpoints already deployed on customer or branch networks you cannot run a -CLI on, use [Network Diagnostics](/iroh-services/net-diagnostics/usage). It runs -the same probes on demand from the Iroh Services dashboard against any online -endpoint in your project and returns the report to the dashboard. - -## Checklist for your network team - -- [ ] Allow outbound TCP 443 to your relay hostnames (required) -- [ ] Allow outbound HTTPS to `dns.iroh.link`, or replace address lookup -- [ ] Confirm your resolver returns TXT records for `dns.iroh.link` -- [ ] Allow outbound UDP 7842 to your relay hostnames (recommended) -- [ ] Allow outbound UDP to arbitrary destination ports (recommended) -- [ ] Set the UDP idle timeout to 30 seconds or more -- [ ] Add the corporate CA via `CaTlsConfig::system()` if TLS is intercepted -- [ ] Set the proxy with `proxy_from_env()` if egress requires one -- [ ] Turn off client isolation on wireless segments where peers should connect directly -- [ ] Verify with `iroh-doctor report` from inside the network +| `udp` | `false` means UDP is blocked or filtered, and everything will relay. | +| `ipv4` / `ipv6` | Which of the two reached a relay successfully. | +| `mapping_varies_by_dest_ip` | `true` means your router type will prevent most direct connections, even with UDP allowed. | +| `preferred_relay` | Which relay was chosen. If this is empty, no relay was reachable at all. Check TCP 443 and DNS first. | +| `relay_latency` | Response time per relay. Missing entries are relays your firewall blocked. | +| `captive_portal` | Anything other than empty means a captive portal or intercepting device is in the way. | + +For devices already deployed at customer sites or branch offices where you +cannot run a command line tool, your developers can use +[Network Diagnostics](/iroh-services/net-diagnostics/usage). It runs the same +checks on demand from a web dashboard against any device that is online. + +## Checklist + +Firewall and DNS: + +- [ ] Allow outbound TCP 443 to the relay hostnames (required) +- [ ] Allow outbound TCP 443 to `dns.iroh.link`, or plan to replace address lookup +- [ ] Confirm your DNS filtering does not strip TXT records for `dns.iroh.link` +- [ ] Allow outbound UDP 7842 to the relay hostnames (recommended) +- [ ] Allow outbound UDP to any destination port (recommended) +- [ ] Set your firewall's UDP idle timeout to 30 seconds or more +- [ ] Turn off client isolation on wireless segments where devices should reach each other +- [ ] Test with `iroh-doctor report` from inside the network + +Ask your developers to: + +- [ ] Use the operating system certificate store if you inspect TLS traffic +- [ ] Configure the proxy address if outbound traffic must be proxied +- [ ] Disable automatic router port opening +- [ ] Tell you which relay hostnames the application is actually configured with If you run your own relay: -- [ ] Open inbound TCP 443, and UDP 7842 if QUIC address discovery is enabled -- [ ] Bind the metrics server to loopback, or disable it -- [ ] Issue the relay certificate from your internal PKI and set `CaTlsConfig::system()` on clients +- [ ] Open inbound TCP 443, and UDP 7842 if address discovery is enabled +- [ ] Bind the monitoring port to loopback, or disable it +- [ ] Issue the certificate from your internal certificate authority - [ ] Set `access` to something other than `everyone` -- [ ] Ship an identical relay map, with tokens, to every site +- [ ] Give every site the same relay list and password From c438d9f753ae6ee5ce4cded65432e810e9b12079 Mon Sep 17 00:00:00 2001 From: rae <633012+okdistribute@users.noreply.github.com> Date: Mon, 27 Jul 2026 19:11:27 -0700 Subject: [PATCH 03/18] Update enterprise-networks.mdx --- deployment/enterprise-networks.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/enterprise-networks.mdx b/deployment/enterprise-networks.mdx index 5bdad54..c280600 100644 --- a/deployment/enterprise-networks.mdx +++ b/deployment/enterprise-networks.mdx @@ -92,7 +92,7 @@ developers to confirm what the application is actually configured with. | Normal DNS (UDP/TCP 53) | Your own resolver | Looking up other devices | The relay connection is a standard HTTPS connection that then upgrades to a -WebSocket, which is the same thing a chat or trading application does. If you +WebSocket, which is the same thing a chat application does. If you run an application-aware firewall configured to permit only ordinary web browsing on port 443, it may block the upgrade. Allow the relay hostnames as a WebSocket destination. From ad8e2be4f245d181070b7783c7b5cc4e32bd4321 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Mon, 27 Jul 2026 19:13:16 -0700 Subject: [PATCH 04/18] docs: drop pre-1.0 relay hostnames from the allowlist Listing two sets of hostnames made the reader stop and work out which one applied to them, at the point in the page where they just want something to paste into a firewall rule. Anyone still on a pre-1.0 release has a version question to answer anyway, and the page already tells them to confirm the configured hostnames with their developers. Co-Authored-By: Claude Opus 5 (1M context) --- deployment/enterprise-networks.mdx | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/deployment/enterprise-networks.mdx b/deployment/enterprise-networks.mdx index c280600..afefaae 100644 --- a/deployment/enterprise-networks.mdx +++ b/deployment/enterprise-networks.mdx @@ -115,11 +115,7 @@ Allowing these lets devices connect directly instead of relaying everything. ### Hostnames to allow -The list depends on which version of iroh the application is built with. Ask -your developers, because the two sets are on **different domains** and a rule -written for one will not match the other. - -**iroh 1.0 and later**, with the default configuration: +With iroh's default configuration: ``` use1-1.relay.n0.iroh.link @@ -129,16 +125,6 @@ aps1-1.relay.n0.iroh.link dns.iroh.link ``` -**Pre-1.0 versions of iroh** use `iroh.network` rather than `iroh.link`: - -``` -use1-1.relay.iroh.network -euw1-1.relay.iroh.network -aps1-1.relay.iroh.network -``` - -If you are not certain which version is in use, allow both lists. - These servers are run by [n0](https://n0.computer), the company that builds iroh. If you run your own relay, this list becomes hostnames you control instead. See [Keeping traffic inside your network](#keeping-traffic-inside-your-network). From 236f4d3bad3dc5cfb4a6ac5218bf50d6381b36a3 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Mon, 27 Jul 2026 19:16:04 -0700 Subject: [PATCH 05/18] docs: stop centering the shared public relays The page read as though every deployment uses the public relays, when most production users either self-host or buy dedicated relays. That framing sends the reader off to allowlist four hostnames that are probably not the ones their application uses. The hostname section now starts from the question the reader has to answer, which relays the application is pointed at, and treats the public defaults as the third case and a sign the application has not been configured yet. The privacy answers are phrased for relays in general, including the point that running the relay yourself buys control of the path but still not visibility into the contents. Co-Authored-By: Claude Opus 5 (1M context) --- deployment/enterprise-networks.mdx | 60 +++++++++++++++++++----------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/deployment/enterprise-networks.mdx b/deployment/enterprise-networks.mdx index afefaae..b05aa95 100644 --- a/deployment/enterprise-networks.mdx +++ b/deployment/enterprise-networks.mdx @@ -115,19 +115,31 @@ Allowing these lets devices connect directly instead of relaying everything. ### Hostnames to allow -With iroh's default configuration: +Which relay hostnames to allow depends on how the application is configured. Ask +your developers, and expect one of three answers. + +**Relays you run yourself.** The hostnames are ones you already control, on your +own network or in your own cloud account. See +[Running your own relay](#running-your-own-relay). + +**Dedicated relays.** Servers provisioned for your project through Iroh Services +and used by nobody else, on hostnames given to you when they are set up. + +**The shared public relays.** Run by [n0](https://n0.computer), the company that +builds iroh, and shared with every other iroh developer worldwide. They are +meant for development and testing rather than production, so treat this as a +sign the application has not been configured yet. The defaults are: ``` use1-1.relay.n0.iroh.link usw1-1.relay.n0.iroh.link euc1-1.relay.n0.iroh.link aps1-1.relay.n0.iroh.link -dns.iroh.link ``` -These servers are run by [n0](https://n0.computer), the company that builds -iroh. If you run your own relay, this list becomes hostnames you control -instead. See [Keeping traffic inside your network](#keeping-traffic-inside-your-network). +Separately from the relays, allow `dns.iroh.link`. That one is used no matter +which relays the application points at, unless your developers have replaced +address lookup with something you run. If your developers use the Iroh Services dashboard for monitoring or diagnostics, that traffic travels over the connections above and needs no extra @@ -138,8 +150,11 @@ firewall rules. ### Can the relay operator read our data? No. Traffic is encrypted end to end between the two devices. A relay forwards -encrypted bytes and holds no key that can decrypt them. This is true of the -public relays run by n0 and of any relay you run yourself. +encrypted bytes and holds no key that can decrypt them. + +This holds for every relay: ones you run, dedicated ones from Iroh Services, and +the shared public ones. It also means running the relay yourself does not give +you visibility into the contents. What it gives you is control of the path. ### What can a relay see? @@ -161,11 +176,12 @@ application, not the network. Your developers can log what the application sends and receives. Running your own relay gives you full control of the path and the connection metadata, but not the contents. -### What leaves our network with the default configuration? +### What actually leaves our network? Four things: -1. An encrypted connection to one of the n0 relay servers listed above. +1. An encrypted connection to the relay servers the application is configured + with. 2. A small signed record published to `dns.iroh.link`, containing the device's public key and which relay it is using. **Internal IP addresses are not published by default.** iroh deliberately filters them out to avoid leaking @@ -177,12 +193,13 @@ Four things: ### Can we control which of our devices are allowed to use it? -Yes, if you run your own relay. You can allow a specific list of devices, or +Yes, on relays you run yourself. You can admit a specific list of devices or require a shared password. See [Controlling who can use your relay](#controlling-who-can-use-your-relay). +Dedicated relays from Iroh Services do this for you, and only admit devices +belonging to your project. -On the public n0 relays, no. Any application with the relay URL can connect to -them. +On the shared public relays, no. Any application that knows the URL can connect. ### Can we turn parts of it off? @@ -353,8 +370,8 @@ endpoint builder, as in the UDP example above. ## Keeping traffic inside your network If you would rather no iroh traffic left your network at all, you can replace -every piece of shared infrastructure with your own. This is the right answer to -most concerns about relays, third-party servers, and data residency. +every piece of infrastructure outside it with your own. This is the right answer +to most concerns about relays, third-party servers, and data residency. There are three pieces, and you can replace them independently. @@ -372,9 +389,9 @@ Either run an internal or drop address lookup entirely and distribute device addresses through a system you already have. - -This is a one-line change for your developers. It removes the n0 relays and DNS -server from the application's configuration. + +This is a one-line change for your developers. It removes the shared public +relays and DNS server from the application's configuration. @@ -385,7 +402,7 @@ use iroh::{Endpoint, RelayMap, RelayMode, RelayUrl, endpoint::presets}; let relay: RelayUrl = "https://relay.internal.example.com".parse()?; -// presets::Empty starts with no n0 infrastructure configured. +// presets::Empty starts with no external infrastructure configured. let endpoint = Endpoint::builder(presets::Empty) .relay_mode(RelayMode::Custom(RelayMap::from_iter([relay]))) .bind() @@ -405,8 +422,8 @@ through a server we do not control. It is worth separating the two things that might mean, because they have different answers. **If the concern is that someone could read the data,** it is already addressed. -A relay forwards encrypted bytes and cannot decrypt them. Nobody operating a -relay, including n0, can see inside your traffic. +A relay forwards encrypted bytes and cannot decrypt them. No relay operator can +see inside your traffic. **If the concern is control, jurisdiction, or data residency,** the answer is to run the relay yourself, as described above. Your server, your network, your @@ -620,10 +637,11 @@ Firewall and DNS: Ask your developers to: +- [ ] Tell you which relay hostnames the application is configured with, before + you write any of the rules above - [ ] Use the operating system certificate store if you inspect TLS traffic - [ ] Configure the proxy address if outbound traffic must be proxied - [ ] Disable automatic router port opening -- [ ] Tell you which relay hostnames the application is actually configured with If you run your own relay: From 595c4bd99031b1f84ceb2cc83d4071b3ea2f0a39 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Mon, 27 Jul 2026 19:19:01 -0700 Subject: [PATCH 06/18] docs: cross-link the FAQ to the enterprise network guide The two pages answer to different readers, so the network material stays in one place and the FAQ points at it rather than restating it. Four entries now link across: relay confidentiality, self-hosting, limiting which endpoints connect, and ports. "What ports does iroh use?" did not name a port a firewall administrator could act on, which is the one question they are most likely to search for, so it now lists 443, the two local UDP sockets, and 7842. Also adds an entry for the case where a security team rather than a developer is the one asking. Corrects the relay count, which said three in one answer and four in another. Four is right. Co-Authored-By: Claude Opus 5 (1M context) --- about/faq.mdx | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/about/faq.mdx b/about/faq.mdx index 3969d97..64ee4bc 100644 --- a/about/faq.mdx +++ b/about/faq.mdx @@ -46,6 +46,8 @@ From the perspective of our QUIC implementation, the relay is "just another UDP Because the relays are relaying traffic, they theoretically know that Endpoint ID X talks to Endpoint ID Y and how many bytes are sent this way, but only for as long as these endpoints haven't established a direct connection yet. However, we don't record this data on our relays. +If you're answering this question for a security review rather than for yourself, [Enterprise Networks](/deployment/enterprise-networks#security-and-privacy-questions) covers it alongside what a relay can see, what leaves your network, and what running your own relay does and doesn't change. + ## How secure is iroh's end-to-end encryption? @@ -67,6 +69,8 @@ The easiest path is a [dedicated relay via Iroh Services](/concepts/relays#dedic Running your own relay doesn't affect interoperability. Your endpoints can still connect to peers using other relay servers, and since relays are stateless and logic lives at the client, can be swapped independently. +For firewall rules, certificates, and access control on a relay you host yourself, see [Running your own relay](/deployment/enterprise-networks#running-your-own-relay). + ## What are the risks of running a public relay? @@ -85,7 +89,7 @@ If you're in a local network together you can enable [local network address look ## 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). +By default iroh is configured with four relay servers from the [default `RelayMap`](https://docs.rs/iroh/latest/iroh/defaults/prod/index.html). 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. @@ -108,6 +112,8 @@ 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. +That controls who your application accepts. If you run your own relay, you can separately control which endpoints the relay itself will carry traffic for, by endpoint ID, a shared token, or a callback to your own service. See [Controlling who can use your relay](/deployment/enterprise-networks#controlling-who-can-use-your-relay). + ## What is "Address Lookup" in iroh and which one should I enable? @@ -128,14 +134,25 @@ First, peer-to-peer [address lookup over the Mainline DHT](/connecting/dht-addre When you do a P2P address lookup, any Mainline server node could respond: each [BEP 44](https://www.bittorrent.org/beps/bep_0044.html) record is stored on 20 random Mainline nodes. So yes, a BitTorrent client that participates in the DHT as a server and has been running long enough to be in the routing tables will respond. +## Our IT or security team needs to approve iroh. What do they need? + +One outbound firewall rule: HTTPS on TCP 443 to your relay servers. Nothing listens for inbound connections, no ports are forwarded, and all traffic is end-to-end encrypted whether it goes direct or through a relay. Allowing outbound UDP as well is optional, and it is what lets most connections go direct. + +[Enterprise Networks](/deployment/enterprise-networks) is written for that reader rather than for a developer: what iroh does on a managed network, what to allow, the security and privacy questions that come up in review, and how to run every part of it on infrastructure you control. + + ## What ports does iroh use? -Iroh listens on **Two UDP ports**: one for IPv4 and one for IPv6, used for direct P2P connections; configurable via [`endpoint::Builder`](https://docs.rs/iroh/latest/iroh/endpoint/struct.Builder.html) +**Outbound TCP 443** to your relay servers. This is the only connection iroh strictly requires, and it's an HTTPS connection that upgrades to a WebSocket. + +**Outbound UDP** for direct connections. Iroh binds **two UDP ports**, one for IPv4 and one for IPv6, assigned by the operating system unless you set them via [`endpoint::Builder`](https://docs.rs/iroh/latest/iroh/endpoint/struct.Builder.html). Direct connections go to arbitrary destination ports on the far side, and **UDP 7842** on the relay is used to learn your own public address. Iroh will work behind firewalls that only allow TCP outbound, but direct connections won't be possible in that case; all traffic will fall back to the relay. It's totally possible that you maintain connections to multiple relays at a time, if you're connected to iroh endpoints that have another home relay than yours. Each of these connections will use another TCP socket. +For the full list of flows, hostnames, and firewall rules to hand to a network administrator, see [Enterprise Networks](/deployment/enterprise-networks). + ## How would onion-routing work with iroh? From 8446e0f27a65b078878468df41676ee96e394b51 Mon Sep 17 00:00:00 2001 From: rae <633012+okdistribute@users.noreply.github.com> Date: Tue, 28 Jul 2026 12:19:44 -0700 Subject: [PATCH 07/18] Update deployment/enterprise-networks.mdx Co-authored-by: Franz Heinzmann --- deployment/enterprise-networks.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/enterprise-networks.mdx b/deployment/enterprise-networks.mdx index b05aa95..f8f821d 100644 --- a/deployment/enterprise-networks.mdx +++ b/deployment/enterprise-networks.mdx @@ -196,7 +196,7 @@ Four things: Yes, on relays you run yourself. You can admit a specific list of devices or require a shared password. See [Controlling who can use your relay](#controlling-who-can-use-your-relay). -Dedicated relays from Iroh Services do this for you, and only admit devices +Dedicated relays from Iroh Services with authentication enabled do this for you, and only admit devices belonging to your project. On the shared public relays, no. Any application that knows the URL can connect. From 880d3560c2a0631be7f6ef5f237cd63ac4347926 Mon Sep 17 00:00:00 2001 From: rae <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 09:15:28 -0700 Subject: [PATCH 08/18] Update faq.mdx --- about/faq.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/about/faq.mdx b/about/faq.mdx index 64ee4bc..4a28ba5 100644 --- a/about/faq.mdx +++ b/about/faq.mdx @@ -35,7 +35,7 @@ Relays serve two roles: they assist with [NAT Traversal](/concepts/nat-traversal Because relays are **stateless** (they route encrypted packets but store nothing), they're cheap to run and easy to scale. There's no database to sync, no state to migrate, and automatic failover across relay instances is built in. -By default iroh is configured with four public relay servers run by number 0 (two in the US, one in Europe, and one in Asia), free to use for development and testing. To prevent abuse, throughput through public relays is rate-limited. For production workloads, you can run a [dedicated relay](/concepts/relays#dedicated-relays) or [self-host your own](/concepts/relays#deploy-your-own-relay). +By default iroh is configured with several public rate-limited relay servers run by number 0 (two in the US, one in Europe, and one in Asia), free to use for development and testing. To prevent abuse, throughput through public relays is rate-limited. For production workloads, you can run a [dedicated relay](/concepts/relays#dedicated-relays) or [self-host your own](/concepts/relays#deploy-your-own-relay). ## Can relays read the traffic they route? @@ -89,7 +89,7 @@ If you're in a local network together you can enable [local network address look ## 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 four relay servers from the [default `RelayMap`](https://docs.rs/iroh/latest/iroh/defaults/prod/index.html). +By default iroh is configured with several public rate-limited relay servers from the [default `RelayMap`](https://docs.rs/iroh/latest/iroh/defaults/prod/index.html). 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. From e58488107236aead0c2509e4d1f86a1799a11645 Mon Sep 17 00:00:00 2001 From: rae <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 09:33:50 -0700 Subject: [PATCH 09/18] Update faq.mdx --- about/faq.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/about/faq.mdx b/about/faq.mdx index 4a28ba5..69c30ff 100644 --- a/about/faq.mdx +++ b/about/faq.mdx @@ -136,7 +136,7 @@ When you do a P2P address lookup, any Mainline server node could respond: each [ ## Our IT or security team needs to approve iroh. What do they need? -One outbound firewall rule: HTTPS on TCP 443 to your relay servers. Nothing listens for inbound connections, no ports are forwarded, and all traffic is end-to-end encrypted whether it goes direct or through a relay. Allowing outbound UDP as well is optional, and it is what lets most connections go direct. +Iroh operates on open standards: HTTPS on TCP 443, QUIC HTTP/3, mTLS, and industry grade end to end encryption with elliptic curve keys. Nothing listens for inbound connections, no ports are forwarded, and all traffic is end-to-end encrypted whether it goes direct or through a relay. Allowing outbound UDP is optional, but it is what lets most connections go direct. [Enterprise Networks](/deployment/enterprise-networks) is written for that reader rather than for a developer: what iroh does on a managed network, what to allow, the security and privacy questions that come up in review, and how to run every part of it on infrastructure you control. From 505f68ddfa1678fd8d7780bdcaf6b40c87d78217 Mon Sep 17 00:00:00 2001 From: rae <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:08:33 -0700 Subject: [PATCH 10/18] Update deployment/enterprise-networks.mdx Co-authored-by: Franz Heinzmann --- deployment/enterprise-networks.mdx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deployment/enterprise-networks.mdx b/deployment/enterprise-networks.mdx index f8f821d..7b4edcf 100644 --- a/deployment/enterprise-networks.mdx +++ b/deployment/enterprise-networks.mdx @@ -40,7 +40,7 @@ through a central server. A device running iroh produces three kinds of traffic. -**Relay connections.** Each device opens one long-lived encrypted connection to +**Relay connections.** Each device opens one or a few long-lived encrypted connections to a *relay server* over HTTPS on port 443. This is the role a TURN server plays in a VoIP system. The relay introduces two devices to each other and carries their traffic if they cannot reach each other directly. It is always used, at minimum From 296864d6bd9be4188d2a19df8de4b6a9a386d4d1 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 13:03:48 -0700 Subject: [PATCH 11/18] remove unnecessary you can block it line --- deployment/enterprise-networks.mdx | 1 - 1 file changed, 1 deletion(-) diff --git a/deployment/enterprise-networks.mdx b/deployment/enterprise-networks.mdx index 7b4edcf..68484a4 100644 --- a/deployment/enterprise-networks.mdx +++ b/deployment/enterprise-networks.mdx @@ -19,7 +19,6 @@ control. a relay server. The relay operator cannot read it. - **You can run all of the infrastructure yourself** if you do not want traffic leaving your network. -- **You can block it.** There is no fallback that evades a firewall rule. If you have set up a video conferencing or VoIP system, iroh's requirements will look familiar. It needs the same two things Teams, Zoom, and WebEx need: a way From a8ebc9d8a1881ed6474bca0549fafbad1adedfce Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:19:06 -0700 Subject: [PATCH 12/18] docs: make the WebSocket upgrade explicit in the firewall rules "Allow outbound TCP 443" reads as satisfied by an ordinary HTTPS rule, which is how someone running a web filter or application-aware firewall ends up with a relay that never connects. The required-traffic table now carries a protocol column naming the upgrade, with a warning saying plainly that opening 443 is not always enough, and the relay connection is described as an upgraded HTTPS request where the reader first meets it. Also completes the page move to configuring-networks so the nav entry resolves, and repoints the cross-links from the FAQ and the troubleshooting page. Co-Authored-By: Claude Opus 5 (1M context) --- about/faq.mdx | 38 +++++++-- ...e-networks.mdx => configuring-networks.mdx | 80 ++++++------------- docs.json | 4 +- troubleshooting.mdx | 2 +- 4 files changed, 61 insertions(+), 63 deletions(-) rename deployment/enterprise-networks.mdx => configuring-networks.mdx (90%) diff --git a/about/faq.mdx b/about/faq.mdx index 69c30ff..a4596c4 100644 --- a/about/faq.mdx +++ b/about/faq.mdx @@ -46,7 +46,7 @@ From the perspective of our QUIC implementation, the relay is "just another UDP Because the relays are relaying traffic, they theoretically know that Endpoint ID X talks to Endpoint ID Y and how many bytes are sent this way, but only for as long as these endpoints haven't established a direct connection yet. However, we don't record this data on our relays. -If you're answering this question for a security review rather than for yourself, [Enterprise Networks](/deployment/enterprise-networks#security-and-privacy-questions) covers it alongside what a relay can see, what leaves your network, and what running your own relay does and doesn't change. +If you're answering this question for a security review rather than for yourself, [Enterprise Networks](/configuring-networks#security-and-privacy-questions) covers it alongside what a relay can see, what leaves your network, and what running your own relay does and doesn't change. ## How secure is iroh's end-to-end encryption? @@ -69,7 +69,7 @@ The easiest path is a [dedicated relay via Iroh Services](/concepts/relays#dedic Running your own relay doesn't affect interoperability. Your endpoints can still connect to peers using other relay servers, and since relays are stateless and logic lives at the client, can be swapped independently. -For firewall rules, certificates, and access control on a relay you host yourself, see [Running your own relay](/deployment/enterprise-networks#running-your-own-relay). +For firewall rules, certificates, and access control on a relay you host yourself, see [Running your own relay](/configuring-networks#running-your-own-relay). ## What are the risks of running a public relay? @@ -112,7 +112,7 @@ 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. -That controls who your application accepts. If you run your own relay, you can separately control which endpoints the relay itself will carry traffic for, by endpoint ID, a shared token, or a callback to your own service. See [Controlling who can use your relay](/deployment/enterprise-networks#controlling-who-can-use-your-relay). +That controls who your application accepts. If you run your own relay, you can separately control which endpoints the relay itself will carry traffic for, by endpoint ID, a shared token, or a callback to your own service. See [Controlling who can use your relay](/configuring-networks#controlling-who-can-use-your-relay). ## What is "Address Lookup" in iroh and which one should I enable? @@ -138,7 +138,7 @@ When you do a P2P address lookup, any Mainline server node could respond: each [ Iroh operates on open standards: HTTPS on TCP 443, QUIC HTTP/3, mTLS, and industry grade end to end encryption with elliptic curve keys. Nothing listens for inbound connections, no ports are forwarded, and all traffic is end-to-end encrypted whether it goes direct or through a relay. Allowing outbound UDP is optional, but it is what lets most connections go direct. -[Enterprise Networks](/deployment/enterprise-networks) is written for that reader rather than for a developer: what iroh does on a managed network, what to allow, the security and privacy questions that come up in review, and how to run every part of it on infrastructure you control. +[Enterprise Networks](/configuring-networks) is written for that reader rather than for a developer: what iroh does on a managed network, what to allow, the security and privacy questions that come up in review, and how to run every part of it on infrastructure you control. ## What ports does iroh use? @@ -151,7 +151,7 @@ Iroh will work behind firewalls that only allow TCP outbound, but direct connect It's totally possible that you maintain connections to multiple relays at a time, if you're connected to iroh endpoints that have another home relay than yours. Each of these connections will use another TCP socket. -For the full list of flows, hostnames, and firewall rules to hand to a network administrator, see [Enterprise Networks](/deployment/enterprise-networks). +For the full list of flows, hostnames, and firewall rules to hand to a network administrator, see [Enterprise Networks](/configuring-networks). ## How would onion-routing work with iroh? @@ -272,3 +272,31 @@ 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*. We fully believe the work on post-quantum-cryptography is good and important and follow developments closely. + +## Does this punch holes in my firewall? + +No. The technique has an alarming name, so it is worth being precise about what +actually happens. + +Two devices, each behind its own firewall, send an outbound packet to each other +at roughly the same moment. Each firewall sees an outbound connection from a +device on its own network and allows the reply, under the same rule that lets a +browser load a website. Neither firewall is tricked, and neither is +reconfigured. + +In practical terms: + +- iroh never accepts unsolicited inbound connections. No port is exposed to the + internet. +- Every connection begins as outbound traffic from a device you control. +- If your firewall does not allow the reply, nothing is bypassed. The connection + falls back to the relay. +- If you block the relay hostnames too, iroh stops working. That is the intended + behavior, not something to defend against. + +One caveat on that last point. Blocking the hostnames listed below stops iroh in +its default configuration, which is what almost every application uses. But the +hostnames are a setting in the application, and iroh also supports other ways of +finding peers that your developers could enable. If you need a hard guarantee +rather than a sensible default, block outbound UDP as well, and ask your +developers to confirm what the application is actually configured with. diff --git a/deployment/enterprise-networks.mdx b/configuring-networks.mdx similarity index 90% rename from deployment/enterprise-networks.mdx rename to configuring-networks.mdx index 68484a4..6f62912 100644 --- a/deployment/enterprise-networks.mdx +++ b/configuring-networks.mdx @@ -1,5 +1,5 @@ --- -title: "Enterprise Networks" +title: "Deploying to enterprise networks" description: "What iroh does on your network, what to allow through your firewall, and how to keep it under your control" --- @@ -9,12 +9,10 @@ control. ## The one-minute version -- **One firewall rule is required:** outbound HTTPS (TCP 443) to a short list of - hostnames. Nothing else is mandatory. - **Nothing listens for inbound connections.** You do not open any ports inward, forward any ports, or assign any static IP addresses. - **Allowing outbound UDP is optional.** It makes connections faster and - cheaper. iroh works without it. + cheaper, by being direct, but iroh works without it. - **All traffic is encrypted end to end**, including traffic that passes through a relay server. The relay operator cannot read it. - **You can run all of the infrastructure yourself** if you do not want traffic @@ -25,11 +23,6 @@ look familiar. It needs the same two things Teams, Zoom, and WebEx need: a way for a device to learn its own public address, and a server that can pass traffic along when two devices cannot reach each other directly. - -Some of the changes on this page are made in the application, not on your -network. Those are marked **For your developers** and include the code to hand -over. - ## What iroh does on your network @@ -39,62 +32,39 @@ through a central server. A device running iroh produces three kinds of traffic. -**Relay connections.** Each device opens one or a few long-lived encrypted connections to -a *relay server* over HTTPS on port 443. This is the role a TURN server plays in -a VoIP system. The relay introduces two devices to each other and carries their -traffic if they cannot reach each other directly. It is always used, at minimum -for the introduction. +**Relay connections.** Each device opens one or a few long-lived encrypted +connections to *relay servers* on TCP port 443, as HTTPS requests that upgrade to +a WebSocket. This is the role a TURN server plays in a VoIP system. The relay +introduces two devices to each other and carries their traffic if they cannot +reach each other directly. It is always used, at minimum for the introduction. **Address lookup.** To find another device, iroh looks up a DNS record for it. This is an ordinary DNS query through your own resolver. **Direct connections.** Once introduced, the two devices try to connect straight -to each other over UDP. This part is optional. If you block it, they keep using -the relay. - -## Does this punch holes in my firewall? - -No. The technique has an alarming name, so it is worth being precise about what -actually happens. - -Two devices, each behind its own firewall, send an outbound packet to each other -at roughly the same moment. Each firewall sees an outbound connection from a -device on its own network and allows the reply, under the same rule that lets a -browser load a website. Neither firewall is tricked, and neither is -reconfigured. - -In practical terms: - -- iroh never accepts unsolicited inbound connections. No port is exposed to the - internet. -- Every connection begins as outbound traffic from a device you control. -- If your firewall does not allow the reply, nothing is bypassed. The connection - falls back to the relay. -- If you block the relay hostnames too, iroh stops working. That is the intended - behavior, not something to defend against. - -One caveat on that last point. Blocking the hostnames listed below stops iroh in -its default configuration, which is what almost every application uses. But the -hostnames are a setting in the application, and iroh also supports other ways of -finding peers that your developers could enable. If you need a hard guarantee -rather than a sensible default, block outbound UDP as well, and ask your -developers to confirm what the application is actually configured with. +to each other over UDP. This part is optional. If a direct connection is not +possible for some reason, the bi-directional connection continues to send data +using the relay. ## What to allow ### Required -| Allow | To | Why | -|---|---|---| -| Outbound TCP 443 | Your relay hostnames | The relay connection itself, and the latency checks that pick the closest relay | -| Outbound TCP 443 | `dns.iroh.link` | Publishing the device's address so others can find it | -| Normal DNS (UDP/TCP 53) | Your own resolver | Looking up other devices | - -The relay connection is a standard HTTPS connection that then upgrades to a -WebSocket, which is the same thing a chat application does. If you -run an application-aware firewall configured to permit only ordinary web -browsing on port 443, it may block the upgrade. Allow the relay hostnames as a -WebSocket destination. +| Allow | To | Protocol | Why | +|---|---|---|---| +| Outbound TCP 443 | Your relay hostnames | **HTTPS, upgraded to WebSocket** | The relay connection itself, and the latency checks that pick the closest relay | +| Outbound TCP 443 | `dns.iroh.link` | HTTPS | Publishing the device's address so others can find it | +| Normal DNS (UDP/TCP 53) | Your own resolver | DNS | Looking up other devices | + + +**Opening TCP 443 is not always enough on its own.** The relay connection starts +as a standard HTTPS request and then upgrades to a WebSocket, which is the same +thing a chat application does. If you run an application-aware firewall, a web +filter, or a proxy that permits ordinary web browsing on port 443 but blocks +WebSocket upgrades, iroh will not connect at all. + +Allow the relay hostnames as a WebSocket destination, not just as an HTTPS one. + ### Recommended diff --git a/docs.json b/docs.json index 6f0d201..ddee18f 100644 --- a/docs.json +++ b/docs.json @@ -40,7 +40,8 @@ "iroh-services/net-diagnostics/quickstart", "protocols/using-quic", "examples/chat", - "add-a-relay" + "add-a-relay", + "configuring-networks" ] }, { @@ -142,7 +143,6 @@ ] }, "examples", - "deployment/enterprise-networks", "troubleshooting", "iroh-services/support", "about/faq" diff --git a/troubleshooting.mdx b/troubleshooting.mdx index 3030271..db7b352 100644 --- a/troubleshooting.mdx +++ b/troubleshooting.mdx @@ -148,5 +148,5 @@ To enable diagnostics on an endpoint, grant the `NetDiagnosticsCap::GetAny` capa If the network is one you or your customer administers, and the problem looks like blocked UDP, an HTTPS proxy, TLS interception, or DNS filtering, see -[Enterprise Networks](/deployment/enterprise-networks) for the full list of +[Enterprise Networks](/configuring-networks) for the full list of flows to allow and how to configure iroh around each restriction. From 195038fa49fe9308130fe14599b2c9f21f0389a2 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:23:59 -0700 Subject: [PATCH 13/18] docs: move the security review questions to the concepts page They answer questions about iroh rather than about a firewall, so they belong with the rest of the security and privacy material where a developer fielding a review will look for them. The network guide keeps the heading and points across, so the administrator reading that page still lands on the answers. Two edits the move required. The relay access answer linked to an anchor that only existed on the network page, so it now links across. The "what leaves our network" answer deferred its IP disclosure point to prose that is now above it on the same page. Also notes that relay visibility ends once a connection goes direct, which the FAQ already said and this section had dropped. Co-Authored-By: Claude Opus 5 (1M context) --- about/faq.mdx | 2 +- concepts/security-privacy.mdx | 74 +++++++++++++++++++++++++++++++++++ configuring-networks.mdx | 64 ++---------------------------- 3 files changed, 79 insertions(+), 61 deletions(-) diff --git a/about/faq.mdx b/about/faq.mdx index a4596c4..0c27b51 100644 --- a/about/faq.mdx +++ b/about/faq.mdx @@ -46,7 +46,7 @@ From the perspective of our QUIC implementation, the relay is "just another UDP Because the relays are relaying traffic, they theoretically know that Endpoint ID X talks to Endpoint ID Y and how many bytes are sent this way, but only for as long as these endpoints haven't established a direct connection yet. However, we don't record this data on our relays. -If you're answering this question for a security review rather than for yourself, [Enterprise Networks](/configuring-networks#security-and-privacy-questions) covers it alongside what a relay can see, what leaves your network, and what running your own relay does and doesn't change. +If you're answering this question for a security review rather than for yourself, [Enterprise Networks](/concepts/security-privacy#questions-from-security-reviews) covers it alongside what a relay can see, what leaves your network, and what running your own relay does and doesn't change. ## How secure is iroh's end-to-end encryption? diff --git a/concepts/security-privacy.mdx b/concepts/security-privacy.mdx index fb27610..414901a 100644 --- a/concepts/security-privacy.mdx +++ b/concepts/security-privacy.mdx @@ -104,3 +104,77 @@ for each relay in the route, which would require significant protocol changes. If these features are interesting to you, please [contact us](https://n0.computer/) to discuss your specific requirements. + + +## Questions from security reviews + +These are the questions that come up when a network or security team reviews an +application built on iroh. For the firewall side of that review, see +[Deploying to enterprise networks](/configuring-networks). + +### Can the relay operator read our data? + +No. Traffic is encrypted end to end between the two devices. A relay forwards +encrypted bytes and holds no key that can decrypt them. + +This holds for every relay: ones you run, dedicated ones from Iroh Services, and +the shared public ones. It also means running the relay yourself does not give +you visibility into the contents. What it gives you is control of the path. + +### What can a relay see? + +The identities of the devices connected to it, which pairs are exchanging +traffic, when, and how much. It cannot see file names, message contents, or +anything else inside the connection. + +Once two devices establish a direct connection, their traffic stops flowing +through the relay, so this visibility covers the period before that plus any +connection that never manages to go direct. + +That connection metadata is a real disclosure, and it is a fair thing to care +about. If it matters to you, run your own relay and it stays with you. + +### Can we inspect or log the contents? + +No, and this is not a property of the UDP path specifically. The relay +connection on port 443 is also encrypted end to end, so a TLS inspection +appliance sees encrypted data either way. + +If your policy requires visibility into content, the place to get it is the +application, not the network. Your developers can log what the application sends +and receives. Running your own relay gives you full control of the path and the +connection metadata, but not the contents. + +### What actually leaves our network? + +Four things: + +1. An encrypted connection to the relay servers the application is configured + with. +2. A small signed record published to `dns.iroh.link`, containing the device's + public key and which relay it is using. **Internal IP addresses are not + published by default.** iroh deliberately filters them out to avoid leaking + them to a public server. +3. DNS lookups for the devices you connect to. +4. If direct connections are allowed, UDP traffic to the other device, which + reveals your public IP address to that device. This is true of any direct + connection between two computers, and is covered in more depth in + [Protecting leakage of IP Addresses](#protecting-leakage-of-ip-addresses). + +### Can we control which of our devices are allowed to use it? + +Yes, on relays you run yourself. You can admit a specific list of devices or +require a shared password. See +[Controlling who can use your relay](/configuring-networks#controlling-who-can-use-your-relay). +Dedicated relays from Iroh Services with authentication enabled do this for you, +and only admit devices belonging to your project. + +On the shared public relays, no. Any application that knows the URL can connect. + +### Can we turn parts of it off? + +Yes. Direct connections, automatic router port opening, local network discovery, +and even relaying itself can each be disabled independently. Each one is a change +your developers make in the application, and +[Deploying to enterprise networks](/configuring-networks) has the specifics for +each. diff --git a/configuring-networks.mdx b/configuring-networks.mdx index 6f62912..fcbb231 100644 --- a/configuring-networks.mdx +++ b/configuring-networks.mdx @@ -116,66 +116,10 @@ firewall rules. ## Security and privacy questions -### Can the relay operator read our data? - -No. Traffic is encrypted end to end between the two devices. A relay forwards -encrypted bytes and holds no key that can decrypt them. - -This holds for every relay: ones you run, dedicated ones from Iroh Services, and -the shared public ones. It also means running the relay yourself does not give -you visibility into the contents. What it gives you is control of the path. - -### What can a relay see? - -The identities of the devices connected to it, which pairs are exchanging -traffic, when, and how much. It cannot see file names, message contents, or -anything else inside the connection. - -That connection metadata is a real disclosure, and it is a fair thing to care -about. If it matters to you, run your own relay and it stays with you. - -### Can we inspect or log the contents? - -No, and this is not a property of the UDP path specifically. The relay -connection on port 443 is also encrypted end to end, so a TLS inspection -appliance sees encrypted data either way. - -If your policy requires visibility into content, the place to get it is the -application, not the network. Your developers can log what the application sends -and receives. Running your own relay gives you full control of the path and the -connection metadata, but not the contents. - -### What actually leaves our network? - -Four things: - -1. An encrypted connection to the relay servers the application is configured - with. -2. A small signed record published to `dns.iroh.link`, containing the device's - public key and which relay it is using. **Internal IP addresses are not - published by default.** iroh deliberately filters them out to avoid leaking - them to a public server. -3. DNS lookups for the devices you connect to. -4. If direct connections are allowed, UDP traffic to the other device, which - reveals your public IP address to that device. This is true of any direct - connection between two computers. - -### Can we control which of our devices are allowed to use it? - -Yes, on relays you run yourself. You can admit a specific list of devices or -require a shared password. See -[Controlling who can use your relay](#controlling-who-can-use-your-relay). -Dedicated relays from Iroh Services with authentication enabled do this for you, and only admit devices -belonging to your project. - -On the shared public relays, no. Any application that knows the URL can connect. - -### Can we turn parts of it off? - -Yes. Direct connections, automatic router port opening, local network discovery, -and even relaying itself can each be disabled independently. Each one is a -change your developers make in the application. The relevant sections below have -the details. +The questions a security or network review raises, including what a relay can +see, what actually leaves your network, and what running your own relay does and +does not change, are answered in +[Security & Privacy](/concepts/security-privacy#questions-from-security-reviews). ## Common situations From 50e47aba490e5d7b3b16af94a15324bcf4e8f96e Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:31:07 -0700 Subject: [PATCH 14/18] docs: name the peer's relay in what leaves the network A device does not only connect to the relays it was configured with. To reach a peer it dials whichever relay that peer selected as its home, which need not be one of yours. Listing only the configured relays understated the egress. Co-Authored-By: Claude Opus 5 (1M context) --- concepts/security-privacy.mdx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/concepts/security-privacy.mdx b/concepts/security-privacy.mdx index 414901a..76680e9 100644 --- a/concepts/security-privacy.mdx +++ b/concepts/security-privacy.mdx @@ -149,8 +149,9 @@ connection metadata, but not the contents. Four things: -1. An encrypted connection to the relay servers the application is configured - with. +1. Encrypted connections to relay servers: the ones your application is + configured with, plus the relay that any device you connect to has chosen for + itself. 2. A small signed record published to `dns.iroh.link`, containing the device's public key and which relay it is using. **Internal IP addresses are not published by default.** iroh deliberately filters them out to avoid leaking From a6619c0c86743e8d9413d766f377681edbd88689 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:34:57 -0700 Subject: [PATCH 15/18] docs: correct the claims about listening and port opening Two claims were wrong. Endpoints do listen for incoming connections on the UDP sockets they bind, and port mapping is a default feature with PortmapperConfig::Enabled as its default, so on a gateway that honors UPnP, NAT-PMP or PCP iroh will get an external port opened. Telling a network administrator that nothing listens and no port is ever forwarded is the kind of thing that destroys trust in the rest of the page when they find out otherwise. What is true is narrower: there is no inbound rule for you to write, because return traffic arrives on a flow already allowed outbound. The port mapping exception is now stated in the summary, in the reasons for allowing UDP, and in its own section, which leads with the perimeter change rather than with the IDS noise it makes. Also notes that a relay is never told when a pair goes direct, so it cannot tell a pair still talking directly from one that disconnected, which limits what its metadata supports inferring. Co-Authored-By: Claude Opus 5 (1M context) --- concepts/security-privacy.mdx | 6 ++ configuring-networks.mdx | 192 ++++++++++++---------------------- 2 files changed, 75 insertions(+), 123 deletions(-) diff --git a/concepts/security-privacy.mdx b/concepts/security-privacy.mdx index 76680e9..81b6086 100644 --- a/concepts/security-privacy.mdx +++ b/concepts/security-privacy.mdx @@ -131,6 +131,12 @@ Once two devices establish a direct connection, their traffic stops flowing through the relay, so this visibility covers the period before that plus any connection that never manages to go direct. +A relay is also not told when a pair goes direct. From its point of view traffic +simply stops, and it cannot distinguish a pair that is still talking over a direct +path from one that finished and disconnected. So a relay cannot reliably infer how +long two devices communicated or how much they exchanged in total, only what it +carried itself. + That connection metadata is a real disclosure, and it is a fair thing to care about. If it matters to you, run your own relay and it stays with you. diff --git a/configuring-networks.mdx b/configuring-networks.mdx index fcbb231..c46be15 100644 --- a/configuring-networks.mdx +++ b/configuring-networks.mdx @@ -9,12 +9,15 @@ control. ## The one-minute version -- **Nothing listens for inbound connections.** You do not open any ports inward, - forward any ports, or assign any static IP addresses. -- **Allowing outbound UDP is optional.** It makes connections faster and - cheaper, by being direct, but iroh works without it. - **All traffic is encrypted end to end**, including traffic that passes through a relay server. The relay operator cannot read it. +- **You do not have to open anything inward.** There is no inbound rule to write, + no port to forward, and no static IP to assign. Devices do listen on the UDP + sockets they bind, and by default iroh asks your gateway for an external port + over UPnP or NAT-PMP, which you can switch off. See + [Automatic port opening](#automatic-port-opening). +- **Allowing outbound UDP is optional, but recommended.** It makes connections faster and + cheaper, by being direct, but iroh works without it. - **You can run all of the infrastructure yourself** if you do not want traffic leaving your network. @@ -23,6 +26,27 @@ look familiar. It needs the same two things Teams, Zoom, and WebEx need: a way for a device to learn its own public address, and a server that can pass traffic along when two devices cannot reach each other directly. +### Checklist + +Firewall and DNS: + +- Allow outbound TCP 443 to the relay hostnames with WebSocket support +- Allow outbound TCP 443 to `dns.iroh.link`, or plan to replace address lookup +- Confirm your DNS filtering does not strip TXT records for `dns.iroh.link` +- Allow outbound UDP 7842 to the relay hostnames +- Allow outbound UDP to any destination port +- Set your firewall's UDP idle timeout to 30 seconds or more +- Turn off client isolation on wireless segments where devices should reach each other +- Test with `iroh-doctor report` from inside the network + +Ask your developers to: + +- Tell you which relay hostnames the application is configured with, before + you write any of the rules above +- Use the operating system certificate store if you inspect TLS traffic +- Configure the proxy address if outbound traffic must be proxied +- Disable automatic port opening, or confirm your gateway has UPnP and NAT-PMP + turned off already ## What iroh does on your network @@ -79,7 +103,7 @@ Allowing these lets devices connect directly instead of relaying everything. | Traffic | What it is | Default | |---|---|---| -| UDP 1900 and UDP 5351 on your local network | Asking the router to open a port automatically (UPnP, NAT-PMP) | On, and safe to turn off | +| UDP 1900 and UDP 5351 on your local network | Asking the gateway to open an external port (UPnP, NAT-PMP, PCP). Opens an inbound path if your gateway honors it. | On by default, and safe to turn off | | UDP 5353 multicast on the local network | Finding devices on the same office network | Off unless your developers enable it | ### Hostnames to allow @@ -114,7 +138,7 @@ If your developers use the Iroh Services dashboard for monitoring or diagnostics, that traffic travels over the connections above and needs no extra firewall rules. -## Security and privacy questions +### Security and privacy The questions a security or network review raises, including what a relay can see, what actually leaves your network, and what running your own relay does and @@ -147,11 +171,13 @@ Why allowing it is usually reasonable: - **Your network almost certainly carries it already.** Chrome, Edge, and Safari use HTTP/3 over UDP port 443 for a large share of the web, and every major conferencing product uses UDP for media. -- **It is always outbound-first.** No inbound rule, no port forward, no exposed - service. -- **Idle entries clear quickly.** iroh sends a keepalive every 5 seconds while a - connection is active and gives up on an idle path after 15 seconds. A UDP idle - timeout of 30 seconds or more on your firewall is comfortable. +- **It is outbound-first.** You write no inbound rule, because the packets coming + back arrive on a flow your firewall already allowed out. The one exception is + automatic port opening, which is + [separately controllable](#automatic-port-opening). +- **Idle entries clear quickly.** iroh sends a keepalive while a connection is +active and gives up on an idle path after a specified timeout. A UDP idle +timeout of 30 seconds or more on your firewall is recommended. One thing that does not work: allowing UDP only to port 443. Direct connections use randomly assigned high-numbered ports on both ends, so a rule scoped to @@ -268,14 +294,34 @@ onto the relay that could have stayed on your own network. ### Automatic port opening -By default, iroh asks the local router to open a port using UPnP and NAT-PMP. -Most business-grade gateways do not offer this and ignore the request. The -discovery step sends a multicast message to `239.255.255.250:1900`, which is a -common intrusion detection signature and can trigger firewall prompts on some -computers. +**What is happening:** by default iroh asks the local gateway to open an external +port for the device, using UPnP, NAT-PMP, or PCP. If your gateway implements one +of those and is configured to honor requests, iroh gets a port mapped and peers +can reach the device directly through it. + +This is the one thing iroh does that changes your perimeter without anyone asking +you, so it is worth being explicit about. It is on in a default build. Two things +soften it in practice: + +- Most business-grade gateways either do not implement these protocols or have + them turned off, in which case the request is ignored and nothing changes. +- Connectivity does not depend on it. Without a port mapping, iroh falls back to + hole punching and then to the relay. + +The UPnP discovery step also multicasts to `239.255.255.250:1900`, which is a +common intrusion detection signature and can raise firewall prompts on some +client operating systems. + +**What to do:** if your gateway has UPnP or NAT-PMP enabled and you would rather +iroh did not use it, you can turn those protocols off at the gateway, which +affects everything on the network, or have your developers disable it in the +application, which is the narrower fix. On a managed network, disabling it in the +application is the reasonable default. -Turning it off is a reasonable default on a managed network. It costs you -nothing that your gateway was going to provide. +To check whether your gateway is answering these requests at all, run the +connectivity report described in +[Testing your configuration](#testing-your-configuration). It includes a port +mapping probe. **For your developers:** `.portmapper_config(PortmapperConfig::Disabled)` on the endpoint builder, as in the UDP example above. @@ -419,58 +465,6 @@ Because iroh does not read the operating system's certificate store by default, your developers also need to point the application at the right authority. See [We inspect TLS traffic](#we-inspect-tls-traffic) for the code. -### Controlling who can use your relay - -Firewall rules control which *machines* can reach the relay. The relay's own -`access` setting controls which *devices* it will serve. The check happens after -the relay has cryptographically verified the device's identity, so an entry on -the allow list cannot be impersonated. - -```toml -# Default: anyone who can reach it. -access = "everyone" - -# Allow a fixed list of devices, identified by public key. -access.allowlist = [ - "523c7996bad77424cbdfd0f0f0b8b5d0d9f9c1b2a3e4f5061718293a4b5c6d7e", - "8f1e2d3c4b5a69788796a5b4c3d2e1f00f1e2d3c4b5a69788796a5b4c3d2e1f0", -] - -# Or block specific devices and allow the rest. -access.denylist = ["..."] - -# Require a shared password. Can also be set with the -# IROH_RELAY_ACCESS_TOKEN environment variable. -access.shared_token = ["token-a", "token-b"] - -# Ask your own service for each device. -access.http = { url = "https://inventory.internal.example.com/iroh-access", bearer_token = "..." } -``` - -| Option | Use when | -|---|---| -| `allowlist` | You have a small fixed set of devices and already track their identities | -| `shared_token` | Devices come and go, and you want one password you can rotate | -| `http` | You already run an inventory or device management system that can answer | -| `denylist` | You need to remove specific devices from an otherwise open relay | - -The `http` option sends a `POST` to your service with the device's identity in -an `X-Iroh-Endpoint-Id` header, and admits the device only if you reply `200` -with a body of `true`. - -**For your developers.** A device presents the shared password like this: - -```rust -use iroh::{Endpoint, RelayMap, RelayMode, RelayUrl, endpoint::presets}; - -let relay: RelayUrl = "https://relay.internal.example.com".parse()?; -let relay_map = RelayMap::from_iter([relay]).with_auth_token("token-a"); - -let endpoint = Endpoint::builder(presets::Empty) - .relay_mode(RelayMode::Custom(relay_map)) - .bind() - .await?; -``` ## Multiple offices and sites @@ -510,56 +504,8 @@ while the link is down, and cross-site connections resume when it returns. ## Testing your configuration -Run the diagnostic tool on a machine on the network you want to test. It needs -[Rust](https://rustup.rs) installed, so you may prefer to ask your developers to -run it and send you the output. - -```shell -cargo install iroh-doctor -iroh-doctor report -``` - -The output is long. These are the lines that matter: - -| Line | What it tells you | -|---|---| -| `udp` | `false` means UDP is blocked or filtered, and everything will relay. | -| `ipv4` / `ipv6` | Which of the two reached a relay successfully. | -| `mapping_varies_by_dest_ip` | `true` means your router type will prevent most direct connections, even with UDP allowed. | -| `preferred_relay` | Which relay was chosen. If this is empty, no relay was reachable at all. Check TCP 443 and DNS first. | -| `relay_latency` | Response time per relay. Missing entries are relays your firewall blocked. | -| `captive_portal` | Anything other than empty means a captive portal or intercepting device is in the way. | - -For devices already deployed at customer sites or branch offices where you -cannot run a command line tool, your developers can use -[Network Diagnostics](/iroh-services/net-diagnostics/usage). It runs the same -checks on demand from a web dashboard against any device that is online. - -## Checklist - -Firewall and DNS: - -- [ ] Allow outbound TCP 443 to the relay hostnames (required) -- [ ] Allow outbound TCP 443 to `dns.iroh.link`, or plan to replace address lookup -- [ ] Confirm your DNS filtering does not strip TXT records for `dns.iroh.link` -- [ ] Allow outbound UDP 7842 to the relay hostnames (recommended) -- [ ] Allow outbound UDP to any destination port (recommended) -- [ ] Set your firewall's UDP idle timeout to 30 seconds or more -- [ ] Turn off client isolation on wireless segments where devices should reach each other -- [ ] Test with `iroh-doctor report` from inside the network - -Ask your developers to: - -- [ ] Tell you which relay hostnames the application is configured with, before - you write any of the rules above -- [ ] Use the operating system certificate store if you inspect TLS traffic -- [ ] Configure the proxy address if outbound traffic must be proxied -- [ ] Disable automatic router port opening - -If you run your own relay: - -- [ ] Open inbound TCP 443, and UDP 7842 if address discovery is enabled -- [ ] Bind the monitoring port to loopback, or disable it -- [ ] Issue the certificate from your internal certificate authority -- [ ] Set `access` to something other than `everyone` -- [ ] Give every site the same relay list and password +See [Diagnose a connectivity issue](/iroh-services/net-diagnostics/quickstart) +for a script you can run from inside your network to confirm the application can +reach the relays and that direct connections work. It is a quick way to check +that your firewall rules are correct, and it produces a report you can send to +your developers to see that everything is working. From 112af4cc2657f0e817477db882c47cdc14366f48 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 19:19:28 -0700 Subject: [PATCH 16/18] update after review --- about/faq.mdx | 49 ++++++++++++++++++++++++++++++ configuring-networks.mdx | 65 +++++----------------------------------- 2 files changed, 57 insertions(+), 57 deletions(-) diff --git a/about/faq.mdx b/about/faq.mdx index 0c27b51..e31a65c 100644 --- a/about/faq.mdx +++ b/about/faq.mdx @@ -300,3 +300,52 @@ hostnames are a setting in the application, and iroh also supports other ways of finding peers that your developers could enable. If you need a hard guarantee rather than a sensible default, block outbound UDP as well, and ask your developers to confirm what the application is actually configured with. + +## Can we stop data going through a relay entirely? + +This request usually arrives as a policy statement: no company data may pass +through a server we do not control. It is worth separating the two things that +might mean, because they have different answers. + +**If the concern is that someone could read the data,** it is already addressed. +A relay forwards encrypted bytes and cannot decrypt them. No relay operator can +see inside your traffic. + +**If the concern is control, jurisdiction, or data residency,** the answer is to +run the relay yourself, as described above. Your server, your network, your +logs, and nothing leaves your perimeter. + +Switching relaying off entirely is a third option. It is a one-line change for +your developers: + +```rust +let endpoint = Endpoint::builder(presets::Empty) + .relay_mode(RelayMode::Disabled) + .bind() + .await?; +``` + +It is usually the wrong choice. It removes the fallback, but it also removes the +machinery that makes direct connections work in the first place. + +- **Roughly one connection in ten stops working.** Stricter firewalls and + certain router types do not allow two devices to connect directly. Those + connections use a relay today. Without one they simply fail, with no fallback + and no error you can fix from the network side. +- **You lose the introduction, not just the fallback.** The relay is how two + devices first find each other and coordinate the simultaneous connection + attempt. Without it, a device behind a firewall cannot be reached at all + unless you provide another way for peers to find each other. +- **Connections take longer to start.** Normally the first data flows over the + relay immediately and moves to a direct connection a moment later. Without a + relay, nothing flows until the direct connection succeeds or fails. +- **Network changes turn into outages.** Switching from Wi-Fi to a mobile + connection, or a DHCP lease change, breaks the direct path. The relay normally + carries traffic during the seconds it takes to rebuild one. + +The cost also falls unevenly. It lands on whoever is behind the most restrictive +network, which is often a customer or a remote worker rather than someone you +can help. + +For nearly every deployment, running your own relay is the better answer. You +get the control you were after, and your users keep the connectivity. diff --git a/configuring-networks.mdx b/configuring-networks.mdx index c46be15..d76702d 100644 --- a/configuring-networks.mdx +++ b/configuring-networks.mdx @@ -138,13 +138,6 @@ If your developers use the Iroh Services dashboard for monitoring or diagnostics, that traffic travels over the connections above and needs no extra firewall rules. -### Security and privacy - -The questions a security or network review raises, including what a relay can -see, what actually leaves your network, and what running your own relay does and -does not change, are answered in -[Security & Privacy](/concepts/security-privacy#questions-from-security-reviews). - ## Common situations ### We block outbound UDP @@ -193,7 +186,7 @@ internal network policy, so desk-to-desk transfers can stay local even when outbound UDP is blocked. -**For your developers.** On a network where UDP is silently dropped, the +On a network where UDP is silently dropped, the application can skip trying, which removes probe traffic that shows up in intrusion detection logs and speeds up startup: @@ -374,55 +367,6 @@ involved. This requires your switches to forward multicast and client isolation to be off. Your developers will find the setup in [mDNS address lookup](/connecting/local-address-lookup). -### Can we stop data going through a relay entirely? - -This request usually arrives as a policy statement: no company data may pass -through a server we do not control. It is worth separating the two things that -might mean, because they have different answers. - -**If the concern is that someone could read the data,** it is already addressed. -A relay forwards encrypted bytes and cannot decrypt them. No relay operator can -see inside your traffic. - -**If the concern is control, jurisdiction, or data residency,** the answer is to -run the relay yourself, as described above. Your server, your network, your -logs, and nothing leaves your perimeter. - -Switching relaying off entirely is a third option. It is a one-line change for -your developers: - -```rust -let endpoint = Endpoint::builder(presets::Empty) - .relay_mode(RelayMode::Disabled) - .bind() - .await?; -``` - -It is usually the wrong choice. It removes the fallback, but it also removes the -machinery that makes direct connections work in the first place. - -- **Roughly one connection in ten stops working.** Stricter firewalls and - certain router types do not allow two devices to connect directly. Those - connections use a relay today. Without one they simply fail, with no fallback - and no error you can fix from the network side. -- **You lose the introduction, not just the fallback.** The relay is how two - devices first find each other and coordinate the simultaneous connection - attempt. Without it, a device behind a firewall cannot be reached at all - unless you provide another way for peers to find each other. -- **Connections take longer to start.** Normally the first data flows over the - relay immediately and moves to a direct connection a moment later. Without a - relay, nothing flows until the direct connection succeeds or fails. -- **Network changes turn into outages.** Switching from Wi-Fi to a mobile - connection, or a DHCP lease change, breaks the direct path. The relay normally - carries traffic during the seconds it takes to rebuild one. - -The cost also falls unevenly. It lands on whoever is behind the most restrictive -network, which is often a customer or a remote worker rather than someone you -can help. - -For nearly every deployment, running your own relay is the better answer. You -get the control you were after, and your users keep the connectivity. - ## Running your own relay ### Firewall rules for the relay server @@ -509,3 +453,10 @@ for a script you can run from inside your network to confirm the application can reach the relays and that direct connections work. It is a quick way to check that your firewall rules are correct, and it produces a report you can send to your developers to see that everything is working. + +## Security and privacy + +The questions a security or network review raises, including what a relay can +see, what actually leaves your network, and what running your own relay does and +does not change, are answered in +[Security & Privacy](/concepts/security-privacy#questions-from-security-reviews). From da98ba50f0bb02319d64527f72576bb9278796ae Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 19:21:12 -0700 Subject: [PATCH 17/18] dedicated relay link --- configuring-networks.mdx | 45 ++++------------------------------------ 1 file changed, 4 insertions(+), 41 deletions(-) diff --git a/configuring-networks.mdx b/configuring-networks.mdx index d76702d..dfc4eab 100644 --- a/configuring-networks.mdx +++ b/configuring-networks.mdx @@ -367,48 +367,11 @@ involved. This requires your switches to forward multicast and client isolation to be off. Your developers will find the setup in [mDNS address lookup](/connecting/local-address-lookup). -## Running your own relay - -### Firewall rules for the relay server - -These are **inbound** rules on the relay server itself. - -| Port | Protocol | Purpose | Needed when | -|---|---|---|---| -| 443 | TCP | The relay service and its latency checks | Always | -| 7842 | UDP | Telling devices their public address | Address discovery is enabled | -| 80 | TCP | Let's Encrypt certificate validation | You use Let's Encrypt for the certificate | -| 9090 | TCP | Prometheus monitoring | Your monitoring system only. Never expose this. | - - -The monitoring port is **enabled by default and listens on every network -interface**. Set `metrics_bind_addr` to a loopback or management address, or set -`enable_metrics = false`. - - -An internal relay usually cannot use Let's Encrypt, because that requires the -hostname to be resolvable from the public internet and port 80 reachable from -outside. Issue a certificate from your own internal certificate authority -instead: - -```toml -# relay.toml -enable_quic_addr_discovery = true -http_bind_addr = "[::]:80" -metrics_bind_addr = "127.0.0.1:9090" - -[tls] -https_bind_addr = "[::]:443" -quic_bind_addr = "[::]:7842" -cert_mode = "Manual" -manual_cert_path = "/etc/iroh-relay/relay.crt" -manual_key_path = "/etc/iroh-relay/relay.key" -``` - -Because iroh does not read the operating system's certificate store by default, -your developers also need to point the application at the right authority. See -[We inspect TLS traffic](#we-inspect-tls-traffic) for the code. +## Help running your own relay +If you want to run your own relay servers, see [Dedicated +Infrastructure](/deployment/dedicated-infrastructure) for a deployment guide and +[contact us](https://n0.computer/) if you want help with the setup. ## Multiple offices and sites From ac8f608aea2697150db511f722992b1c7e02ed81 Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Wed, 29 Jul 2026 19:23:48 -0700 Subject: [PATCH 18/18] an iroh relay deployment guide doesnt actually exist --- configuring-networks.mdx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/configuring-networks.mdx b/configuring-networks.mdx index dfc4eab..b2dd41a 100644 --- a/configuring-networks.mdx +++ b/configuring-networks.mdx @@ -331,9 +331,8 @@ There are three pieces, and you can replace them independently. Run the [relay software](https://github.com/n0-computer/iroh/tree/main/iroh-relay) on servers your devices can reach, and have your developers point the -application at them. See [Dedicated -Infrastructure](/deployment/dedicated-infrastructure) for the deployment guide, -and the sections below for firewall rules and access control. +application at them. + Either run an internal @@ -369,8 +368,8 @@ to be off. Your developers will find the setup in ## Help running your own relay -If you want to run your own relay servers, see [Dedicated -Infrastructure](/deployment/dedicated-infrastructure) for a deployment guide and +If you want to run your own relay servers, you can! Everything is open source. See [Dedicated +Infrastructure](/deployment/dedicated-infrastructure) and [contact us](https://n0.computer/) if you want help with the setup. ## Multiple offices and sites