How iroh works
by Rüdiger KlaehnWe have had a lot of explanations about how iroh works in the past. But before 1.0 it was a bit of a moving target. Now that 1.0 is out it is time to provide a detailed explanation.
Iroh is a library
Iroh is a lightweight native library that is meant to be embedded into applications. It can be embedded in rust applications directly, and into C, C++, swift, python, javascript and kotlin via our bindings.
The two key features of iroh
Connect by key
Iroh endpoints are identified by a cryptographic key. This allows us to connect no matter where the remote endpoint is.
Reliable connections
We make sure that you do get a connection whenever possible. And we make sure that the connection is as fast as possible.
When conditions change, we immediately react and choose the new best path. This is transparent to the user.
So how this it actually work?
Now that we have shown what iroh does for you, let's go into details about how.
Iroh components
Iroh library
The iroh library itself provides an API to create endpoints and connect to other endpoints, as well as a rich API for connections and data streams.
Iroh relay
The iroh relays work in the background on a publicly reachable servers to facilitate direct connections and to relay data in case establishing a direct connection is not possible.
Endpoint startup
An iroh endpoint is configured with a set of relays. Either the rate limited public relays operated by number0, relays bought via iroh services, or self-hosted relays.
Iroh works just fine with a single relay, but works best if you have relays in all geographic regions where your users are.
Determining the home relay
On startup, an iroh endpoint sends QAD probes to all configured relays. This serves two purposes: learning our own public IP address as well as learning which relay is closest in terms of latency. The closest relay becomes our home relay. We keep a secure websocket connection open to our home relay.
Publishing the home relay
Iroh works just fine with one relay. But frequently we have multiple relays. Alice needs to know which relay to use to talk to Bob. For this we have two mechanisms
DNS based address lookup
We offer a mechanism based on DNS out of the box. Bob publishes a signed DNS record to a DNS server operated by number0 via a https PUT request. Alice or whoever wants to talk to bob then looks up this record using a DNS query or a https query.
Mainline DHT based address lookup
The above mechanism works fine in most cases. But maybe you want something that is fully peer to peer. For that case we offer an optional DHT based address lookup mechanism. We publish the exact same record as before on the mainline DHT, using the bep_0044 extension.
Establishing direct connections
So far we have described a system that makes sure that endpoints can talk at all. But all data is flowing through the relays. This adds latency, limits performance, and causes costs for the relay operator.
How do we make the connections fast and direct? This is aguably the most complex part of the system.
Hole punching
Hole punching happens inside the QUIC connection via an extension n0_nat_traversal. It is inspired by the QUIC NAT traversal draft, but uses its own transport parameter ID.
Local direct connections
If two devices are in the same network, hole punching is not needed. Each side just needs to learn the local address of the remote.
To get started, take a look at our docs, dive directly into the code, or chat with us in our discord channel.