# What Are QUIC Streams in Iroh? Peer-to-Peer Multiplexing Explained

> Explore QUIC streams in Iroh, the peer to peer multiplexing solution. Learn how they enable reliable, ordered delivery for concurrent operations without multiple connections.

- Repository: [number zero/iroh](https://github.com/n0-computer/iroh)
- Tags: deep-dive
- Published: 2026-07-15

---

**QUIC streams in Iroh are lightweight, bidirectional byte streams multiplexed over a single QUIC connection, providing reliable, ordered delivery for concurrent peer-to-peer operations without the overhead of maintaining multiple connections.**

The n0-computer/iroh repository builds its networking stack directly on the QUIC transport protocol. After a QUIC handshake establishes a single connection between peers, that connection can carry dozens or hundreds of independent streams, each identified by a numeric stream ID. This architecture allows applications to spin up new streams for every logical operation—whether sending file chunks, control messages, or request-response exchanges—while the underlying QUIC connection handles encryption, congestion control, and packet loss recovery.

## How QUIC Streams Work in Iroh

Iroh treats QUIC streams as the primary abstraction for data exchange between peers. Unlike traditional TCP where each logical channel requires a separate connection, Iroh leverages QUIC's native multiplexing capabilities to keep operational overhead minimal.

### Multiplexing Over a Single Connection

A single QUIC connection in Iroh can host numerous bidirectional streams simultaneously. According to the documentation in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs), streams are "very cheap to create so can be used for many concurrent operations." Each stream operates independently with its own flow control and ordering guarantees, identified internally by a numeric stream ID. This design allows applications to open a fresh stream for each discrete task rather than managing complex multiplexing logic over a shared byte pipe.

The core implementation resides in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs), where the `Endpoint` type provides methods for establishing connections and managing the lifecycle of their associated streams.

### Reliability and Ordering Guarantees

While UDP serves as the underlying transport, QUIC streams provide **reliable, in-order delivery** of data. The QUIC implementation handles:

- Packet loss detection and retransmission
- Congestion control across all streams sharing the connection
- Encryption via TLS 1.3 (mandatory for all QUIC traffic)

Individual streams may close or encounter errors without affecting other active streams on the same connection. This isolation prevents head-of-line blocking between unrelated data flows, a critical feature for peer-to-peer applications handling multiple concurrent transfers.

## Performance Features of Iroh QUIC Streams

Beyond basic multiplexing, Iroh exposes advanced QUIC features that optimize latency and connectivity in real-world network conditions.

### Zero-RTT Data Transmission

Iroh's QUIC endpoints support **0-RTT data**, allowing the first bytes of a stream to be transmitted before the cryptographic handshake completes. When reconnecting to a previously known peer, this eliminates the round-trip delay typically required to establish security parameters, significantly reducing latency for repeated connections.

### Multipath Extension Support

For enhanced resilience, Iroh can enable the **QUIC Multipath Extension**, allowing a single logical connection to utilize several network paths simultaneously. This feature is configurable in [`src/endpoint/quic.rs`](https://github.com/n0-computer/iroh/blob/main/src/endpoint/quic.rs), where transport parameters specify multipath behavior. When enabled, a connection can migrate between WiFi and cellular interfaces or aggregate bandwidth across multiple routes without application-level intervention.

The connection migration logic and path validation probes are handled in [`src/net_report/probes.rs`](https://github.com/n0-computer/iroh/blob/main/src/net_report/probes.rs), which implements QUIC address-discovery mechanisms to locate optimal relay paths, while [`src/socket/transports/relay/actor.rs`](https://github.com/n0-computer/iroh/blob/main/src/socket/transports/relay/actor.rs) manages QUIC-specific timeout and congestion settings for relayed connections.

## Working with QUIC Streams in the Iroh API

The public API exposes streams through the `Endpoint` and `Connection` types defined in [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) and [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs). Applications interact with streams using async methods that return bidirectional send and receive halves.

### Opening Outgoing Streams

To initiate communication, establish a connection to a remote peer and open a bidirectional stream:

```rust
use iroh::Endpoint;

// Create an endpoint (client or server)
let ep = Endpoint::new_default().await?;

// Establish a QUIC connection to a remote peer
let conn = ep.connect(remote_peer_id).await?;

// Open a bi‑directional stream
let mut stream = conn.open_bi().await?;

// Send a request
stream.write_all(b"GET /resource\r\n").await?;

// Receive a response
let mut buf = Vec::new();
stream.read_to_end(&mut buf).await?;
println!("Response: {}", String::from_utf8_lossy(&buf));

```

The `open_bi()` method allocates a new stream ID and returns a handle that implements the async `AsyncRead` and `AsyncWrite` traits, allowing standard Rust io patterns.

### Accepting Incoming Streams

Server-side code accepts connections and handles individual streams in isolated tasks:

```rust
// Accepting a connection and handling streams
let mut incoming = ep.accept().await?;
while let Some(conn) = incoming.next().await {
    // Spawn a task for each incoming connection
    tokio::spawn(async move {
        while let Ok(mut stream) = conn.accept_bi().await {
            // Echo back whatever the client sent
            let mut data = Vec::new();
            stream.read_to_end(&mut data).await?;
            stream.write_all(&data).await?;
        }
    });
}

```

The `accept_bi()` method listens for new streams initiated by the remote peer. Because streams are cheap to create, this pattern scales to thousands of concurrent logical channels per connection.

For a complete minimal example, see [`iroh/examples/screening-connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/screening-connection.rs), which demonstrates endpoint initialization and bidirectional stream handshakes.

## Summary

- **QUIC streams in Iroh** are independent, bidirectional byte channels multiplexed over a single QUIC connection, defined in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs).
- Streams provide **reliable, ordered delivery** with per-stream flow control, while the connection handles TLS 1.3 encryption and congestion control.
- **0-RTT support** allows data transmission to begin before handshake completion, reducing latency for repeated peers.
- The **Multipath Extension** in [`src/endpoint/quic.rs`](https://github.com/n0-computer/iroh/blob/main/src/endpoint/quic.rs) enables connections to utilize multiple network paths simultaneously for improved resilience.
- Use `Connection::open_bi()` to initiate streams and `Connection::accept_bi()` to handle incoming streams, as shown in [`iroh/examples/screening-connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/screening-connection.rs).

## Frequently Asked Questions

### What is the difference between a QUIC connection and a QUIC stream in Iroh?

A **QUIC connection** represents the overall security association between two endpoints, established via the QUIC handshake and managed in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs). It handles encryption, congestion control, and path management. A **QUIC stream** is a lightweight, logical byte stream multiplexed *inside* that connection. While a connection requires cryptographic setup and network path validation, streams can be created and destroyed instantly with minimal overhead, allowing applications to dedicate separate streams to independent operations.

### How do I open a bidirectional QUIC stream in Iroh?

First, obtain a `Connection` by calling `Endpoint::connect()` with the remote peer's ID. Then invoke `connection.open_bi().await`, which returns a bidirectional stream implementor. This method is defined in the public API surface of [`iroh/src/lib.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) and allocates a new stream ID within the existing connection. The returned handle supports both reading and writing, enabling full-duplex communication over a single logical channel.

### Are QUIC streams in Iroh encrypted?

Yes. All QUIC streams inherit the **TLS 1.3 encryption** established at the connection level. While each stream maintains independent ordering and flow control, the actual bytes transmitted are encrypted and authenticated by the underlying QUIC connection. This means applications do not implement their own encryption logic; data written to any stream in [`iroh/src/endpoint.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint.rs) is automatically secured by the transport layer.

### What is QUIC stream multiplexing and why does it matter for peer-to-peer networking?

**Multiplexing** allows multiple independent streams to share a single UDP socket and QUIC connection. In Iroh's peer-to-peer context, this matters because it eliminates the need to establish new TCP connections (or QUIC handshakes) for every concurrent operation. As noted in the codebase documentation, streams are "very cheap to create," enabling applications to isolate different data flows—such as file transfers, control signals, and discovery messages—without head-of-line blocking or connection setup latency.