How to Get Started with Iroh Examples: A Complete Guide for Beginners
To get started with Iroh examples, clone the n0-computer/iroh repository and run the echo, blob transfer, or gossip examples using cargo run -p iroh --example <name>, which demonstrate the core Endpoint and Router APIs for peer-to-peer QUIC connections.
Iroh is a Rust-first library that enables applications to dial peers by public key, automatically selecting the fastest path through direct connections, hole-punching, or public relays. This guide covers the essential beginner examples from the n0-computer/iroh repository, showing you how to establish connections, transfer content-addressed data, and build publish-subscribe networks.
Prerequisites and Repository Setup
Before running the examples, ensure you have Rust installed. Clone the repository and explore the available crates:
git clone https://github.com/n0-computer/iroh.git
cd iroh
The repository contains several key crates:
iroh– Core library withEndpointand connection establishment logic ([iroh/src/lib.rs](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs))iroh-relay– Relay server for NAT traversal ([iroh-relay/src/main.rs](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/main.rs))iroh-blobs– Content-addressed blob storage and transferiroh-gossip– Scalable publish-subscribe overlay network
Example 1: Simple Echo Protocol
The echo example demonstrates the fundamental pattern of connecting two peers over QUIC using Application-Layer Protocol Negotiation (ALPN). This example uses the Endpoint type from [iroh/src/lib.rs](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) to manage connections.
Client Implementation
Create a client that connects to a remote peer and sends a message:
use iroh::Endpoint;
use anyhow::Result;
use tokio::io::{AsyncWriteExt, AsyncReadExt};
const ALPN: &[u8] = b"iroh-example/echo/0";
#[tokio::main]
async fn main() -> Result<()> {
// Bind a local endpoint that manages connections
let endpoint = Endpoint::bind().await?;
// Connect to a remote peer using their address
let addr = "...remote endpoint address...".parse()?;
let conn = endpoint.connect(addr, ALPN).await?;
// Open a bidirectional QUIC stream
let (mut send, mut recv) = conn.open_bi().await?;
// Send payload and read response
send.write_all(b"Hello, iroh!").await?;
send.finish()?;
let mut response = Vec::new();
recv.read_to_end(&mut response).await?;
assert_eq!(response, b"Hello, iroh!");
// Cleanup
conn.close(0u32.into(), b"bye!");
endpoint.close().await;
Ok(())
}
The Endpoint::bind() method creates a local endpoint that owns a QUIC engine, while connect() handles DNS resolution, hole-punching, and relay fallback automatically.
Server Implementation
The server side implements the ProtocolHandler trait to accept incoming connections:
use iroh::Endpoint;
use iroh::protocol::ProtocolHandler;
use iroh::connection::Connection;
use anyhow::Result;
use std::sync::Arc;
const ALPN: &[u8] = b"iroh-example/echo/0";
#[derive(Debug, Clone)]
struct Echo;
#[async_trait::async_trait]
impl ProtocolHandler for Echo {
async fn accept(&self, conn: Connection) -> Result<()> {
let (mut send, mut recv) = conn.accept_bi().await?;
tokio::io::copy(&mut recv, &mut send).await?;
send.finish()?;
conn.closed().await;
Ok(())
}
}
#[tokio::main]
async fn main() -> Result<()> {
let endpoint = Endpoint::bind().await?;
let router = Router::builder(endpoint)
.accept(ALPN.to_vec(), Arc::new(Echo))
.spawn()
.await?;
// Router runs indefinitely
Ok(())
}
The Router::builder registers protocol handlers by ALPN identifier, allowing the same endpoint to serve multiple protocols.
Example 2: Blob Transfer with iroh-blobs
For transferring large files, iroh-blobs provides a content-addressed store using BLAKE3 hashes. This example demonstrates the high-level API similar to the echo pattern but with persistent storage.
use iroh::Endpoint;
use iroh_blobs::BlobStore;
use anyhow::Result;
use std::path::PathBuf;
const ALPN: &[u8] = b"iroh-blobs/transfer/0";
#[tokio::main]
async fn main() -> Result<()> {
// Initialize endpoint and on-disk blob store
let endpoint = Endpoint::bind().await?;
let store = BlobStore::open(PathBuf::from("./my_blobs")).await?;
// Sender: Add file to store and transmit
let cid = store.add_file("large_file.dat").await?;
let conn = endpoint.connect(remote_addr, ALPN).await?;
store.send(&conn, cid).await?;
// Receiver: Accept connections and automatically store blobs
let router = Router::builder(endpoint)
.accept(ALPN.to_vec(), Arc::new(store.clone()))
.spawn()
.await?;
Ok(())
}
Key implementation files include [iroh-blobs/src/lib.rs](https://github.com/n0-computer/iroh/blob/main/iroh-blobs/src/lib.rs) for the core store and iroh-blobs/examples/transfer.rs for the complete runnable demo.
Example 3: Publish-Subscribe with iroh-gossip
The gossip protocol builds a scalable overlay network on top of Iroh's connection layer, enabling message broadcasting to multiple peers.
Publisher Implementation
Create a publisher that sends messages to a topic:
use iroh::Endpoint;
use iroh_gossip::{Gossip, Topic};
use anyhow::Result;
use std::sync::Arc;
#[tokio::main]
async fn main() -> Result<()> {
let endpoint = Endpoint::bind().await?;
let gossip = Gossip::new(endpoint.clone()).await?;
// Create topic from arbitrary bytes
let topic = Topic::from(b"chat-room-1".as_ref());
// Publish messages periodically
let mut interval = tokio::time::interval(std::time::Duration::from_secs(1));
loop {
interval.tick().await;
let msg = format!("ping at {}", chrono::Utc::now());
gossip.publish(&topic, msg.into_bytes()).await?;
}
}
Subscriber Implementation
Subscribers register callbacks to receive messages:
use iroh::Endpoint;
use iroh_gossip::{Gossip, Topic, Message};
use anyhow::Result;
#[tokio::main]
async fn main() -> Result<()> {
let endpoint = Endpoint::bind().await?;
let gossip = Gossip::new(endpoint.clone()).await?;
let topic = Topic::from(b"chat-room-1".as_ref());
gossip.subscribe(&topic, |msg: Message| async move {
println!("Got: {}", String::from_utf8_lossy(&msg.payload));
}).await?;
// Keep alive indefinitely
futures::future::pending::<()>().await;
Ok(())
}
The high-level API is defined in [iroh-gossip/src/lib.rs](https://github.com/n0-computer/iroh/blob/main/iroh-gossip/src/lib.rs), with a complete example available at iroh-gossip/examples/pubsub.rs.
Running a Local Relay (Optional)
For testing NAT traversal without depending on public infrastructure, run a local relay server:
cargo run -p iroh-relay -- --listen 0.0.0.0:2345 --cert ./cert.pem --key ./key.pem
The relay server implementation resides in [iroh-relay/src/main.rs](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/main.rs), which parses CLI options and starts the QUIC listener, while [iroh-relay/src/lib.rs](https://github.com/n0-computer/iroh/blob/main/iroh-relay/src/lib.rs) contains the core client handling and connection pooling logic.
Key Source Files and Architecture
Understanding the repository structure helps when extending these examples:
- [
iroh/src/lib.rs](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs) – Public API exposingEndpoint,Router, and protocol registration - [
iroh-base/src/lib.rs](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/lib.rs) – Core data structures includingEndpointIdandRelayUrl - [
iroh-base/src/key.rs](https://github.com/n0-computer/iroh/blob/main/iroh-base/src/key.rs) – Public-key utilities and DER/PEM parsing - [
iroh-dns-server/src/main.rs](https://github.com/n0-computer/iroh/blob/main/iroh-dns-server/src/main.rs) – DNS/PKARR server for resolvingEndpointIdto addresses - [
iroh-dns/src/dns.rs](https://github.com/n0-computer/iroh/blob/main/iroh-dns/src/dns.rs) – DNS resolver client used byEndpointduring peer lookup
Summary
- Clone the repository from n0-computer/iroh to access official examples and source code
- Start with the echo example to understand
Endpoint::bind(),connect(), and theProtocolHandlertrait - Use iroh-blobs for content-addressed file transfer with automatic deduplication via BLAKE3 hashing
- Implement gossip protocols using
iroh-gossipfor scalable publish-subscribe messaging over the same QUIC backbone - Test locally with
iroh-relayto understand NAT traversal without external dependencies
Frequently Asked Questions
How do I run the Iroh examples without cloning the entire repository?
You can add the specific crates as dependencies in your Cargo.toml and copy the example code from the repository's examples/ directories. For instance, add iroh = "0.7" and iroh-blobs = "0.7" to your dependencies, then adapt the code from iroh-blobs/examples/transfer.rs to your project structure.
What is the difference between Endpoint and Router in Iroh?
The Endpoint struct, defined in [iroh/src/lib.rs](https://github.com/n0-computer/iroh/blob/main/iroh/src/lib.rs), manages the underlying QUIC connections and dialing logic. The Router allows you to register multiple ProtocolHandler implementations by ALPN identifier, enabling a single endpoint to serve different protocols simultaneously without manual connection dispatch.
Do I need to run my own relay server to use Iroh?
No, Iroh includes default public relays that handle NAT traversal automatically. However, for private deployments or testing offline scenarios, you can run your own relay using the iroh-relay crate by executing cargo run -p iroh-relay with appropriate certificate and listen address parameters.
Which Iroh example should I start with as a beginner?
Begin with the echo example because it demonstrates the fundamental connection lifecycle: binding an endpoint, connecting by address, opening bidirectional streams, and graceful shutdown. Once you understand this pattern, the blob transfer and gossip examples build upon the same Endpoint and Router APIs with additional protocol-specific logic.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →