The Road to Iroh 1.0

pimterry1 pts0 comments

The road to iroh 1.0 - Iroh<br>Blog Index<br>It is done. After four years of work and various pivots we have released iroh 1.0.

A trip down github memory lane

We have an expanding web site describing how iroh works under the hood, but we think it is important to also understand why we made the decisions that led to the current iroh. So here is a trip down memory lane / our commit history which highlights major turning points and design decisions.

Perhaps more importantly, this post also covers some things we considered but ended up not doing .

The early days

Iroh started as a rust implementation of ipfs. We got pretty far implementing various ipfs protocols and had interop with existing ipfs implementations working. Our goal with iroh was to target lightweight and resource constrained devices such as mobile phones.

One area where we were unhappy with existing protocols was blob transfer. We started a working group within the ipfs community to come up with a new protocol, called the move the bytes working group.

First steps with QUIC

For that working group, we worked on a proposal for new blob transfer protocol. We wrote a small library called krakensync. Krakensync used BLAKE3 exclusively for content addressing because BLAKE3 supports verified incremental streaming of large blobs.

After a few days we had a bare algorithm, and wanted to add networking to it to make the demo feel more real. We had only two days before the next meeting, and adding networking via rust-libp2p seemed impossible (Remember that this was before LLM coding assistants!).

:first-child]:mt-0 [&>:last-child]:mb-0">I had a tiny bit of previous experience with QUIC, and remembered from a project of a friend of mine, quinn-noise that quinn is both hackable and has a simple idiomatic rust API. So I tried to add networking via quinn and found it an absolute joy to work with.<br>— Rüdiger

A real world use case

In parallel to working on the ipfs compatible iroh, we worked on a side project to implement a single narrow real world use case: transfer of deltachat chat history when provisioning a new device. The technical task was to transfer a single large blob of backup data to a new device. There were hard constraints regarding disk and network usage. And the transfer had to be resumable.

We found that we could not implement this very narrow use case with our ipfs based iroh implementation. Doing so would have had less than optimal network performance, and would have pulled in a very large dependency tree.

So we started writing a lightweight sync library from scratch, using QUIC as the networking layer and BLAKE3 verified streaming for large blob transfer.

Abstraction fatigue

Despite our best efforts, our main project was still far away from the theoretical limits. Meanwhile our sync side project showed what is possible. So we made the decision to no longer pursue the goal of staying compatible with existing ipfs implementations.

Instead of multiaddrs, endpoints were identified by an Ed25519 public key.

Instead of multihashes, we had BLAKE3.

And instead of multiple transport protocols we had just QUIC.

The new iroh was just QUIC based networking using quinn and blob transfer using BLAKE3 verified streaming. Compared to the previous version, the functionality was anemic. But the single thing it did, it did with close to optimal performance. It was a lightweight enough dependency that deltachat could justify including it into their rust code base. And suddenly we had both networking and sync deployed in a real app used by thousands of people across various platforms (windows, linux, iOS, android). The old code lives on in beetle.

TLS or noise

We decided to use just QUIC. But QUIC as specified in RFC 9000 has clear client and server roles, where the server is identified by a certificate chain. At the time we had to add a somewhat Rube Goldberg construction on top to make QUIC work for peer-to-peer connections, a self-signed certificate carrying the real public key in a custom X.509 extension, with a signature binding the two.

Also, rustls is a heavy dependency, and its default crypto provider is a C dependency. So there was a temptation to replace the entire TLS encryption scheme with a protocol based on noise that is made for peer-to-peer connections from the start.

We ended up sticking with TLS as the industry standard. Without using TLS iroh would not really be QUIC, just reuse some of its parts. But we were still unhappy about the self-signed cert.

Thankfully right around the time when we were discussing this raw public keys in TLS support was merged into rustls. So we didn't waste any time and switched to raw public keys in TLS soon after.

Hole punching

One thing we were not yet happy with: for the deltachat chat history transfer to work, both devices had to be in the same network. Ideally this should work in all cases where the two devices are somehow connected to the internet. We also wanted these transfers to be fast. So...

iroh quic transfer ipfs networking work

Related Articles