less net work for networks

Iroh is a library for building on direct connections between devices, putting more control in the hands of your users.

DIRECTDIRECTRELAYDIRECT

Connect any two devices on the planet

Iroh gives you an API for dialing by public key. You say “connect to that phone”, iroh will find & maintain the fastest connection for you, regardless of where it is.

“In stark contrast to other p2p & dweb technologies we've played with - which are exciting due to their implications for the future - Iroh brought instant gains in our present."

- weird.one

Compose your own tailor-made protocol stack

An ecosystem of ready-made, composable protocols are built on top of iroh.
Mix & match to get the feature set you need.

Build your own protocol

Don't see a protocol you need? Build your own! Iroh gives you a reliable foundation for building distributed systems that reach the edge. The rest is up to you.

Protocol Docs

Continuously Measured

All commits to iroh's main branch run through a growing set of simulations & tests

Iroh Perf Site

Real World Use

Iroh is running in production on hundreds of thousands of devices, on all major platforms.

Delta Chat

Delta Chat

Iroh powers multi-device backup & live connections for in-chat WebXDC apps

Build something amazing, today.

Start Building

main.rs

use std::{env, str::FromStr};

use iroh::base::ticket::BlobTicket;
use iroh::node::Node;

// serve & fetch data with iroh, from any two devices in the world
// run this example from any two computers, and iroh will connect them!
#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // create a new node with default protocols
    let node = Node::memory().spawn().await?;

    // Choose between client & server roles based on a download argument
    if let Some(ticket) = env::args().collect::<Vec<String>>().get(1) {
        // we have a download argument. Fetch some data!
        let ticket = BlobTicket::from_str(&ticket).expect("failed to parse blob ticket");

        let response = node
            .blobs()
            .download(ticket.hash(), ticket.node_addr().clone())
            .await?
            .await
            .expect("unable to download hash");

        println!(
            "downloaded {} bytes from node {}",
            response.downloaded_size,
            ticket.node_addr().node_id
        );

        let bytes = node.blobs().read_to_bytes(ticket.hash()).await?;
        let s = std::str::from_utf8(&bytes).expect("unable to parse blob as as utf-8 string");
        println!("{s}");
    } else {
        // no download argumnt, serve some data that says "Hello, world!"
        let res = node.blobs().add_bytes("Hello, world!").await?;

        // create a "ticket" for sharing data with a peer
        let ticket = node
            .blobs()
            .share(res.hash, res.format, Default::default())
            .await?;

        // print the ticket, containing all the above information
        println!("Serving data! In another terminal, run:");
        println!("cargo run --example hello {}", ticket);
        tokio::signal::ctrl_c().await?;
        node.shutdown().await?;
    }

    Ok(())
}

From the Blog

Update On FFI Bindings

We are pausing FFI releases until we figure out a better solution