# How to Open a QUIC Stream in Iroh: Methods and Examples

> Learn how to open QUIC streams in Iroh using open_uni() and open_bi() methods. Explore practical examples for unidirectional and bidirectional data transfer with AsyncWrite and AsyncRead.

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

---

**You open a QUIC stream in Iroh by calling `open_uni()` for unidirectional (send-only) streams or `open_bi()` for bidirectional streams on an established `Connection`, awaiting the resulting future to obtain the stream object, and using standard `AsyncWrite` or `AsyncRead` traits to transfer data.**

Iroh's networking layer is built on top of the **noq** QUIC implementation, providing async-first APIs for peer-to-peer communication in the `n0-computer/iroh` repository. After establishing a connection between endpoints, you must open a QUIC stream in Iroh to transmit application data, choosing between unidirectional or bidirectional channels based on your protocol requirements.

## Prerequisites: Obtaining a Connection

Before you can open a QUIC stream in Iroh, you need an active `Connection` handle. This is obtained either by connecting to a remote node or accepting an incoming connection handshake.

```rust
use iroh::Endpoint;

// Client side: connect to a remote server
let endpoint = Endpoint::client("0.0.0.0:0".parse()?)?;
let conn = endpoint.connect(server_addr, "example.com")
    .await?                 // performs the QUIC handshake
    .await?;                // yields a `Connection`

```

The `Connection` struct is defined in [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) and provides the methods for stream creation.

## Opening Outgoing Streams

The [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) file implements two primary methods for creating new outgoing streams. Both return futures that resolve when the remote peer acknowledges the stream creation.

### Unidirectional Streams with `open_uni()`

Use `open_uni()` to create a **send-only** stream where the peer can only read from the returned object. According to the source at lines 11211-11215, this method returns an `OpenUni` future that resolves to a stream implementing `AsyncWrite`.

```rust
use iroh::endpoint::ConnectionExt; // for `.anyerr()`
use tokio::io::AsyncWriteExt;

// Open a unidirectional stream
let mut send_stream = conn.open_uni().await.anyerr()?;

// Write data and close the sending side
send_stream.write_all(b"hello iroh").await?;
send_stream.finish().await?;

```

### Bidirectional Streams with `open_bi()`

Use `open_bi()` to create a **read-write** stream where you can both send and receive data. This returns an `OpenBi` future that resolves to a tuple of `(SendStream, RecvStream)`.

```rust
use tokio::io::{AsyncReadExt, AsyncWriteExt};

// Open a bidirectional stream
let (mut send, mut recv) = conn.open_bi().await.anyerr()?;

// Send request
send.write_all(b"ping").await?;
send.finish().await?;

// Read response
let mut buf = vec![0u8; 64];
let n = recv.read(&mut buf).await?;

```

## Accepting Incoming Streams (Server Side)

When acting as a server, you accept connections and then wait for the remote peer to open streams using the `accept_uni()` and `accept_bi()` methods. The implementation in [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) (around lines 11289-11295) provides these counterparts to the open methods.

```rust
// Accept an incoming connection
let conn = server_endpoint.accept().await
    .ok_or_else(|| anyhow::anyhow!("no incoming connection"))?
    .await?;

// Accept the next unidirectional stream opened by the client
let mut recv = conn.accept_uni().await.anyerr()?;

// Read the payload
let mut data = Vec::new();
recv.read_to_end(&mut data).await?;

```

## Error Handling and Stream Limits

When you open a QUIC stream in Iroh, the operation may fail if the peer has reached its stream limit or refuses new streams. The examples above use the `anyerr()` helper method, provided by `iroh::endpoint::ConnectionExt` and defined in [`iroh/src/util.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/util.rs), to convert `noq::Error` into a standard `Result` type.

Both `open_uni()` and `open_bi()` return futures that can fail with connection errors if the underlying QUIC connection closes before the stream is established. Always await these futures within your error handling context.

## Complete Working Example

The official Iroh example in [`iroh/examples/remote-info.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/remote-info.rs) demonstrates a server opening a unidirectional stream to send data. Here is the excerpt showing the key pattern:

```rust
// iroh/examples/remote-info.rs (lines 68-73)
let mut s = conn.open_uni().await.anyerr()?; // wait for stream to open
tokio::time::sleep(Duration::from_millis(500)).await;
s.write_all(b"hi").await.anyerr()?;           // write data
s.finish().anyerr()?;                        // close the sending side

```

When combined with connection setup, the full client workflow to open a QUIC stream in Iroh looks like this:

```rust
use iroh::Endpoint;
use iroh::endpoint::ConnectionExt;
use noq::VarInt;
use tokio::io::AsyncWriteExt;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Create endpoint and connect
    let client_ep = Endpoint::client("0.0.0.0:0".parse()?)?;
    let conn = client_ep.connect("127.0.0.1:9999".parse()?, "example.com")
        .await?.await?;

    // Open and use a unidirectional stream
    let mut stream = conn.open_uni().await.anyerr()?;
    stream.write_all(b"hello from iroh client").await?;
    stream.finish().await?;
    
    // Gracefully close connection
    conn.close(VarInt::from_u32(0), b"done");
    Ok(())
}

```

## Summary

- **Choose stream direction**: Use `open_uni()` for send-only streams and `open_bi()` for bidirectional communication.
- **Await the future**: Both methods return futures (`OpenUni` and `OpenBi`) that must be awaited to obtain the actual stream objects.
- **Source location**: The implementation lives in [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs) at lines 11211-11215.
- **Handle errors**: Use the `anyerr()` helper from `ConnectionExt` to map `noq` errors into standard Results, or handle `noq::Error` directly.
- **Server counterpart**: Use `accept_uni()` and `accept_bi()` to receive streams initiated by remote peers.

## Frequently Asked Questions

### What is the difference between unidirectional and bidirectional streams in Iroh?

**Unidirectional streams** (`open_uni()`) allow only the initiating peer to write data, while the receiving peer can only read. **Bidirectional streams** (`open_bi()`) provide two-way communication where both sides can read and write. Choose unidirectional for simple data pushes and bidirectional for request-response protocols.

### How do I handle errors when opening a QUIC stream in Iroh?

The `open_uni()` and `open_bi()` methods return futures that may resolve to errors if the connection is closed or stream limits are exceeded. Use the `.anyerr()` helper method from `iroh::endpoint::ConnectionExt` to convert `noq::Error` into a standard `Result` type, allowing you to use the `?` operator for ergonomic error propagation.

### Can I open multiple streams on a single connection?

Yes, Iroh supports multiplexing multiple streams over a single QUIC connection. You can call `open_uni()` or `open_bi()` multiple times on the same `Connection` handle to create concurrent streams. Each stream operates independently with its own flow control, though they share the underlying connection's congestion control and security context.

### Where can I find the implementation details for stream creation?

The primary implementation is in [`iroh/src/endpoint/connection.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/src/endpoint/connection.rs), specifically lines 11211-11215 for the `open_uni()` and `open_bi()` methods. For practical usage examples, see [`iroh/examples/remote-info.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/remote-info.rs) and [`iroh/examples/transfer.rs`](https://github.com/n0-computer/iroh/blob/main/iroh/examples/transfer.rs), which demonstrate both client and server patterns for stream handling.