How to Use Iroh as a Decentralized Storage Solution
Iroh is a Rust-first framework that implements fully decentralized, content-addressed storage over a peer-to-peer QUIC transport, allowing you to publish and retrieve BLAKE3-hashed blobs without centralized servers.
Iroh provides a complete peer-to-peer storage layer that eliminates the need for centralized cloud providers by combining content-addressed storage with public-key routing. According to the n0-computer/iroh source code, the framework exposes a high-level API that handles NAT traversal, encrypted transport, and data integrity automatically. This guide explains how to use iroh as a decentralized storage solution in your Rust applications, referencing the actual implementation files and methods.
Core Architecture Components
Three main building blocks work together to provide decentralized storage in iroh. Each component handles a specific layer of the networking and storage stack.
The Endpoint (Transport Layer)
The Endpoint type defined in iroh/src/endpoint.rs serves as the public-key-identified node that can dial and be dialed. When you call Endpoint::bind(), the framework generates a long-term Ed25519 key pair and creates a QUIC endpoint that hides complexity such as hole-punching, relay fallback, and DNS resolution.
The endpoint owns a QUIC socket and a router, managing connection lifecycles while authenticating all traffic via TLS-like encryption using the node's public key.
Content-Addressed Blob Storage
The iroh-blobs protocol implements BLAKE3-hash-addressed blob transfer. When you store data using BlobClient::put(), iroh computes the BLAKE3 hash and returns a Tag that uniquely identifies the blob. This content-addressed approach means the hash itself is the immutable identifier—no additional metadata is required to locate the data.
The blob client communicates over QUIC streams that are automatically multiplexed, prioritized, and encrypted. The type lives in the external iroh-blobs crate, which is pulled in automatically as a workspace dependency.
Relay and Discovery Services
Iroh includes infrastructure for peer discovery and connectivity:
- Relay servers (
iroh-relay/src/server.rs) provide stateless forwarding of encrypted QUIC packets when direct hole-punching fails - DNS/Pkarr resolution (
iroh-dns-server/src/dns.rs) maps human-readable names likemybox.pkarr.netto endpoint IDs defined iniroh-base/src/endpoint_addr.rs
These services ensure storage remains accessible even when peers are behind restrictive NATs.
Publishing and Retrieving Blobs
The following examples demonstrate the complete workflow for decentralized storage operations. These patterns are adapted from iroh/examples/transfer.rs.
Creating an Endpoint and Storing Data
To publish data, bind a local endpoint and use the blob client to compute the content hash:
use iroh::endpoint::Endpoint;
use iroh::blob::Client as BlobClient;
use tokio::fs;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Bind a local endpoint (generates a new key pair on first run)
let endpoint = Endpoint::bind().await?;
// Create a blob client that talks over this endpoint
let blobs = BlobClient::new(endpoint.clone());
// Read data you want to store
let data = fs::read("my_file.bin").await?;
// Put the data – iroh computes the BLAKE3 hash for you
let tag = blobs.put(data.into()).await?;
println!("Stored blob with tag {}", tag);
Ok(())
}
The BlobClient::put() method streams the data over a QUIC connection and returns a Tag containing the BLAKE3 hash. This tag serves as the permanent address for retrieving the blob from any peer in the network.
Retrieving Blobs by Hash
To download data, create an endpoint on the receiving side and request the blob by its tag:
use iroh::endpoint::Endpoint;
use iroh::blob::Client as BlobClient;
use tokio::fs;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let endpoint = Endpoint::bind().await?;
let blobs = BlobClient::new(endpoint.clone());
// The tag obtained from the publishing side
let tag = "b7c9f6a5…".parse()?; // Tag implements FromStr
// Request the blob – iroh finds the owning peer automatically
let data = blobs.get(tag).await?.into_bytes();
fs::write("downloaded.bin", data).await?;
println!("Blob downloaded");
Ok(())
}
When BlobClient::get() is called, iroh locates the owning peer via the public-key network, attempts a direct connection (hole-punch), and falls back to a public relay if needed. The blob streams back to the caller without requiring a central server.
Using Human-Readable Names with PKARR
Instead of sharing raw public keys or tags, you can use DNS/Pkarr resolution for friendly naming:
use iroh::endpoint::Endpoint;
use iroh::blob::Client as BlobClient;
use iroh::dns::Resolver;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let endpoint = Endpoint::bind().await?;
let blobs = BlobClient::new(endpoint.clone());
// Resolve a PKARR name to an endpoint address
let resolver = Resolver::default();
let remote = resolver.resolve("mybox.pkarr.net").await?;
endpoint.add_known_peer(remote).await?;
// Now request a blob by its tag as before
// ...
Ok(())
}
The Resolver type handles the PKARR DNS resolution logic, allowing peers to discover each other via memorable domain names rather than cryptographic identifiers.
Key Implementation Details
Understanding these specific implementation files helps when debugging or extending iroh's storage capabilities:
iroh/src/endpoint.rs– CoreEndpointtype that manages QUIC sockets, routing, and connection lifecyclesiroh-base/src/endpoint_addr.rs– Definition ofEndpointAddrandEndpointId, the public-key-based identifiers used throughout the systemiroh-relay/src/server.rs– Minimal relay implementation used when direct hole-punching failsiroh-dns/src/lib.rsandiroh-dns-server/src/dns.rs– DNS/Pkarr resolution logic for peer discovery
All traffic is authenticated by the endpoint's public key, enabling peers to verify that a blob originates from the claimed source. The protocol uses noq (a QUIC implementation) to provide low latency, stream-level flow control, and datagram support while avoiding head-of-line blocking.
Summary
- Iroh implements decentralized storage through a Rust-first framework combining QUIC transport with content-addressed blobs
- Content-addressing via BLAKE3 ensures data integrity—hashes serve as permanent tags that identify blobs without central indexing
- Automatic NAT traversal attempts direct hole-punching before falling back to stateless relay servers
- Public-key routing identifies endpoints via Ed25519 keys, with optional PKARR DNS resolution for human-readable names
- Simple API surface exposes
Endpoint::bind()andBlobClientmethods for publishing and retrieving data across peer-to-peer networks
Frequently Asked Questions
What makes iroh different from IPFS?
While both systems use content-addressing, iroh focuses specifically on efficient blob transfer over QUIC with built-in hole-punching and relay fallback. Unlike IPFS, iroh does not require a distributed hash table (DHT) for content routing—instead, it uses public-key-based endpoint identification and direct peer connections, resulting in lower latency and simpler deployment.
How does iroh handle NAT traversal?
Iroh first attempts to open a direct UDP path through hole-punching between endpoints. If NAT traversal fails, the connection automatically falls back to a public relay server (iroh-relay/src/server.rs). The relay is stateless and merely forwards encrypted QUIC packets, meaning it cannot inspect traffic content while enabling connectivity between restricted networks.
What is the relationship between Tags and BLAKE3 hashes?
A Tag is the high-level identifier returned by BlobClient::put() that wraps the BLAKE3 hash of the blob content. Because the hash is cryptographically unique to the data, the tag serves as an immutable, verifiable pointer to the blob. When you request data via BlobClient::get(), iroh verifies the received content against the hash embedded in the tag to ensure integrity.
Can I use iroh from languages other than Rust?
Yes, while iroh is Rust-first, the framework provides iroh-ffi bindings that expose the storage API to other languages. The core concepts—creating an endpoint, obtaining tags, and retrieving blobs—remain identical across language bindings, though the Rust implementation in iroh/src/endpoint.rs represents the reference implementation for the decentralized storage protocol.
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 →