A libp2p transport using iroh QUIC that works behind any NAT without port forwarding, UPnP, or manual configuration.
Add to your Cargo.toml:
[dependencies]
libp2p-iroh = "0.1"Replace your existing transport with libp2p-iroh. That's it. Here's a minimal example using libp2p-kad with libp2p-iroh transport:
use libp2p::Multiaddr;
use libp2p::StreamProtocol;
use libp2p_kad::store::MemoryStore;
use libp2p_swarm::NetworkBehaviour;
use libp2p_swarm::Swarm;
use libp2p_iroh::Transport;
use libp2p_iroh::TransportTrait;
use libp2p_swarm::SwarmEvent;
#[derive(NetworkBehaviour)]
struct MyBehaviour {
    kademlia: libp2p_kad::Behaviour<MemoryStore>,
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let keypair = libp2p_identity::Keypair::generate_ed25519();
    let peer_id = keypair.public().to_peer_id();
    let transport = Transport::new(Some(&keypair)).await?.boxed();
    println!("Copy and paste this in a second terminal, press enter to connect back to this node from anywhere:");
    println!("  /p2p/{peer_id}");
    let kad_config = libp2p_kad::Config::new(StreamProtocol::new("/example/kad/1.0.0"));
    let store = MemoryStore::new(peer_id);
    let behaviour = MyBehaviour {
        kademlia: libp2p_kad::Behaviour::with_config(peer_id, store, kad_config),
    };
    let mut swarm = Swarm::new(
        transport,
        behaviour,
        peer_id,
        libp2p_swarm::Config::with_executor(Box::new(|fut| {
            tokio::spawn(fut);
        })).with_idle_connection_timeout(std::time::Duration::from_secs(300)),
    );
    // Our listener address looks like this: /p2p/12D3KooWEUowGZ...
    swarm.listen_on(Multiaddr::empty())?;
    // Mini cli to dial other peers
    let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel();
    tokio::spawn(async move {
        print!("> ");
        let mut stdin = std::io::stdin().lock();
        let mut line = String::new();
        if std::io::BufRead::read_line(&mut stdin, &mut line).is_ok() && !line.is_empty() {
            if let Ok(peer_multiaddr) = line.trim().parse::<Multiaddr>() {
                tx.send(peer_multiaddr).unwrap();
            };
        }
    });
    loop {
        tokio::select! {
            event = futures::StreamExt::select_next_some(&mut swarm) => {
                if let SwarmEvent::ConnectionEstablished { peer_id, .. } = event {
                    println!("Connection established with {peer_id}!");
                }
            }
            Some(addr) = rx.recv() => {
                println!("Dialing {addr}...");
                swarm.dial(addr)?;
            }
        }
    }
}Run the included swarm_dht example for a full demo:
# Terminal 1
cargo run --example swarm_dht
# Terminal 2 
cargo run --example swarm_dht
# Copy the multiaddr from terminal 1 and dial itBoth peers can be behind different NATs. The connection will establish successfully.
If you're building with libp2p, you know NAT traversal is complicated. You need:
- Multiple transport implementations
- Relay servers
- AutoNAT protocol
- DCUtR for hole punching
- Careful configuration
With libp2p-iroh, you get reliable peer-to-peer connections behind any firewall out of the box. No relay configuration. No hole punching setup. Just dial a peer by their PeerId and it works.
The transport uses iroh's built-in relay network and NAT traversal. When you dial a peer, iroh handles:
- Direct connections when possible
- Relay routing as fallback if (immediate) direct connections fail
- Automatic hole punching
- Connection upgrades from relayed to direct
You don't configure any of this. It just works with the magic of iroh.
Peers are addressed using iroh's node ID format:
/p2p/12D3KooWAbCdEf...  # Standard libp2p PeerId format
The transport automatically handles the conversion between libp2p PeerIds and iroh NodeIds.
- swarm(default): Includes libp2p-swarm and libp2p-kad dependencies for the examples.
Disable default features if you only need the transport:
libp2p-iroh = { version = "0.1", default-features = false }- Uses iroh's QUIC implementation (based on quinn)
- Leverages iroh's relay protocol for guaranteed connectivity
- Supports ed25519 keypairs compatible with libp2p
- Implements libp2p's Transport trait
- Connection multiplexing via QUIC streams
Working work in progress. Contributions welcome!
MIT