# How to Manage iroh Node Connections: Complete Guide to Endpoints, Relays, and Monitoring

> Learn to manage iroh node connections by initializing endpoints, resolving addresses, and using connect() for authenticated QUIC connections with relay fallback. Your complete guide for n0-computer iroh.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: how-to-guide
- Published: 2026-07-04

---

**You manage iroh node connections by initializing an Endpoint, resolving EndpointAddr addresses, and invoking the async `connect()` method to establish authenticated QUIC connections with automatic relay fallback.**

Managing peer-to-peer connections in the n0-computer/iroh repository requires understanding how nodes discover and communicate through endpoints identified by public keys. The connection management API centers on the **Endpoint** struct in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs), which provides the primary interface for establishing secure QUIC connections between nodes.

## Establishing Connections with the Endpoint API

The core connection logic resides in **[`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)**. The public API exposes the async method **`Endpoint::connect(endpoint_addr, alpn).await`**, which internally delegates to **`Endpoint::connect_with_opts`** to resolve remote addresses, configure transport settings, and initiate the QUIC handshake.

### Building and Binding an Endpoint

Before connecting, you must bind an endpoint with appropriate ALPN protocols and relay configuration. The following example from [`iroh/examples/connect.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/connect.rs) demonstrates the setup:

```rust
let endpoint = Endpoint::builder(presets::N0)
    .secret_key(SecretKey::generate())
    .alpns(vec![EXAMPLE_ALPN.to_vec()])
    .relay_mode(RelayMode::Default)
    .bind()
    .await?;
endpoint.online().await; // Ensure relay connectivity before dialing

```

### The Connection Workflow

When you call `connect()`, iroh executes a four-step resolution and handshake process:

1. **Address resolution** – `Endpoint::connect_with_opts` resolves the supplied `EndpointAddr` (which may contain relay URLs, direct UDP addresses, or both) using the internal address-lookup service:
   ```rust
   let mapped_addr = self.inner.resolve_remote(endpoint_addr).await??;
   ```

   *(See line 27 in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs))*

2. **Transport configuration** – The method builds a QUIC client configuration with the requested ALPN and any extra ALPNs supplied via `ConnectOptions`:
   ```rust
   let client_config = self.inner.static_config.create_client_config(
       alpn_protocols, transport_config.clone());
   ```

   *(Lines 38-43)*

3. **QUIC handshake** – The *noq* endpoint creates the authenticated connection:
   ```rust
   let connect = self.inner.noq_endpoint()
       .connect_with(client_config, dest_addr, server_name)?;
   ```

   *(Lines 44-48)*

4. **Returning Connecting** – The function wraps the raw QUIC connection in a **`Connecting`** future that resolves to a fully-handshaked **`Connection`**.

## Connection Options and Advanced Configuration

### Using connect_with_opts for 0-RTT

For advanced control over the connection process, use **`Endpoint::connect_with_opts`** to enable 0-RTT handshakes or tweak QUIC settings:

```rust
let opts = ConnectOptions {
    transport_config: Some(custom_quic_config),
    additional_alpns: vec![b"my/extra".to_vec()],
    ..Default::default()
};
let connecting = endpoint.connect_with_opts(addr, ALPN, opts).await?;
let conn = connecting.into_0rtt().await?; // Zero-RTT handshake

```

*Source: [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) (lines 50-63)*

## Relay Handling and Fallback Mechanisms

When direct UDP paths cannot be established, iroh automatically falls back to the relay protocol. The relay logic is implemented in **[`iroh/src/socket/transports/relay/actor.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports/relay/actor.rs)**.

### ActiveRelayActor and Connection Resilience

The **`ActiveRelayActor`** maintains a QUIC session with a relay server, implementing automatic reconnection with exponential back-off when connections drop. Critical timing constants governing this behavior include:

- **`CONNECT_TIMEOUT`** – 10 seconds for establishing a relay session (line 84)
- **`PING_INTERVAL`** – 15-second periodic keep-alive pings (line 69)
- **`RELAY_INACTIVE_CLEANUP_TIME`** – 60 seconds idle time before closing the relay connection (line 65)

The actor receives **`CheckConnection`** messages (line 66) to verify relay link health when local network interfaces change.

## Monitoring Connection Lifecycle

### Implementing EndpointHooks

Applications can observe connection lifecycle events without blocking shutdown by implementing the **`EndpointHooks`** trait. The [`monitor-connections.rs`](https://github.com/n0-computer/iroh/blob/main/monitor-connections.rs) example demonstrates logging connection open and close events:

```rust
#[derive(Clone)]
struct Monitor { tx: UnboundedSender<MonitoredConnection> }

impl EndpointHooks for Monitor {
    async fn after_handshake(&self, conn: &Connection) -> AfterHandshakeOutcome {
        let info = MonitoredConnection {
            alpn: conn.alpn().to_vec(),
            remote_id: conn.remote_id(),
            handle: conn.weak_handle(),
        };
        let _ = self.tx.send(info);
        AfterHandshakeOutcome::Accept
    }
}

```

*Source: [`iroh/examples/monitor-connections.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/monitor-connections.rs) (lines 84-107)*

The hook receives a **`WeakConnectionHandle`** after the handshake, allowing asynchronous inspection of metrics like RTT and byte counts while permitting normal connection shutdown.

## Summary

- Use **`Endpoint::builder()`** to configure nodes with ALPNs, secret keys, and relay modes before binding
- Call **`endpoint.connect()`** to initiate QUIC connections through the resolution and handshake workflow in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs)
- Leverage **`connect_with_opts`** for 0-RTT handshakes and custom transport configurations
- Rely on **`ActiveRelayActor`** in [`iroh/src/socket/transports/relay/actor.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports/relay/actor.rs) for automatic relay fallback with exponential back-off
- Implement **`EndpointHooks`** to monitor connection lifecycle events without preventing graceful shutdown

## Frequently Asked Questions

### How does iroh resolve addresses when connecting to a node?

Iroh resolves **`EndpointAddr`** structures (which may contain relay URLs, direct UDP addresses, or both) through the internal address-lookup service. The `Endpoint::connect_with_opts` method calls `self.inner.resolve_remote(endpoint_addr)` at line 27 of [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) to map the logical address to physical socket addresses before attempting the QUIC handshake.

### What is the difference between connect and connect_with_opts?

**`Endpoint::connect`** is the high-level convenience method that uses default connection options. **`Endpoint::connect_with_opts`** exposes lower-level control, allowing you to specify custom `ConnectOptions` including additional ALPNs, transport configurations, and 0-RTT settings as implemented in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) lines 50-63.

### How does iroh handle connection failures when direct UDP is unavailable?

When direct UDP paths fail, iroh automatically falls back to the relay protocol. The **`ActiveRelayActor`** in [`iroh/src/socket/transports/relay/actor.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/socket/transports/relay/actor.rs) manages relay connections with a 10-second connection timeout, implements exponential back-off for reconnection attempts, and sends periodic 15-second keep-alive pings to maintain the relay session.

### Can I monitor active connections without blocking the connection lifecycle?

Yes, by implementing the **`EndpointHooks`** trait and specifically the `after_handshake` method. Your hook receives a **`WeakConnectionHandle`** that allows asynchronous inspection of connection metrics like RTT and byte counts without preventing the connection from closing normally, as demonstrated in [`iroh/examples/monitor-connections.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/monitor-connections.rs).