<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
<channel>
  <title>iroh.computer</title>
  <link>https://aggr.aymericbeaumet.com/sources/iroh.computer/</link>
  <description>Browse aymeric&#39;s, an independent, searchable archive of readable snapshots from followed feeds, preserved in Git with aggr.</description>
  <language>en</language>
  <atom:link href="https://aggr.aymericbeaumet.com/sources/iroh.computer/rss.xml" rel="self" type="application/rss+xml"/>
  <lastBuildDate>Fri, 16 Oct 2026 12:02:05 +0000</lastBuildDate>
  <generator>aggr</generator>
  <item>
    <title>The road to iroh 1.0</title>
    <link>https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-16-the-road-to-iroh-1-0/</link>
    <guid isPermaLink="false">urn:aggr:item:3c2f04ffea3a12a65dc4c01409461ddbef865f7d</guid>
    <atom:link href="https://www.iroh.computer/blog/the-road-to-iroh-1-0" rel="via" type="text/html"/>
    <pubDate>Fri, 16 Oct 2026 12:02:05 +0000</pubDate>
    <dc:creator>iroh.computer via Frando</dc:creator>
    <description>Comments</description>
    <content:encoded>&lt;p&gt;It is done. After four years of work and various pivots we have released iroh 1.0.&lt;/p&gt;
&lt;p&gt;We have an expanding web site describing &lt;em&gt;how&lt;/em&gt; iroh works under the hood, but we think it is important to also understand &lt;em&gt;why&lt;/em&gt; 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.&lt;/p&gt;
&lt;p&gt;Perhaps more importantly, this post also covers some things we considered but ended up &lt;strong&gt;not doing&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Iroh started as a rust implementation of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ipfs.tech/&quot;&gt;ipfs&lt;/a&gt;. 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.&lt;/p&gt;
&lt;p&gt;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 &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/playlist?list=PLuhRWgmPaHtQ--aQ5GlgCyKkQYXUfQpVf&quot;&gt;move the bytes working group&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;For that working group, we worked on a proposal for new blob transfer protocol. We wrote a small library called &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/krakensync&quot;&gt;krakensync&lt;/a&gt;. Krakensync used &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/BLAKE3-team/BLAKE3&quot;&gt;BLAKE3&lt;/a&gt; exclusively for content addressing because BLAKE3 supports verified incremental streaming of large blobs.&lt;/p&gt;
&lt;p&gt;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 &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/libp2p&quot;&gt;rust-libp2p&lt;/a&gt; seemed impossible (Remember that this was before LLM coding assistants!).&lt;/p&gt;
&lt;p&gt;I had a tiny bit of previous experience with &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc9000.html&quot;&gt;QUIC&lt;/a&gt;, and remembered from a project of a friend of mine, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/quinn-noise&quot;&gt;quinn-noise&lt;/a&gt; that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/quinn&quot;&gt;quinn&lt;/a&gt; is both hackable and has a simple idiomatic rust API. So I tried to add networking via quinn and found it &lt;em&gt;an absolute joy&lt;/em&gt; to work with.&lt;/p&gt;
&lt;p&gt;— Rüdiger&lt;/p&gt;
&lt;p&gt;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 &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://delta.chat/&quot;&gt;deltachat&lt;/a&gt; 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.&lt;/p&gt;
&lt;p&gt;We found that &lt;em&gt;we could not&lt;/em&gt; 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.&lt;/p&gt;
&lt;p&gt;So we started writing a lightweight sync library from scratch, using QUIC as the networking layer and BLAKE3 verified streaming for large blob transfer.&lt;/p&gt;
&lt;p&gt;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 &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/a-new-direction-for-iroh&quot;&gt;made the decision&lt;/a&gt; to no longer pursue the goal of staying compatible with existing ipfs implementations.&lt;/p&gt;
&lt;p&gt;Instead of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/multiformats/multiaddr&quot;&gt;multiaddrs&lt;/a&gt;, endpoints were identified by an &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc8032.html&quot;&gt;Ed25519&lt;/a&gt; public key.&lt;br /&gt;
Instead of &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/multiformats/multihash&quot;&gt;multihashes&lt;/a&gt;, we had BLAKE3.&lt;br /&gt;
And instead of multiple transport protocols we had just QUIC.&lt;/p&gt;
&lt;p&gt;The new iroh was just QUIC based networking using &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/quinn&quot;&gt;quinn&lt;/a&gt; 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 &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/chatmail/core/commit/24856f3050be9a7705f9df736ab575fd856454d5&quot;&gt;deltachat could justify including it into their rust code base&lt;/a&gt;. 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 &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/beetle&quot;&gt;beetle&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;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, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/libp2p/specs/blob/master/tls/tls.md&quot;&gt;a self-signed certificate carrying the real public key in a custom X.509 extension&lt;/a&gt;, with a signature binding the two.&lt;/p&gt;
&lt;p&gt;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 &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://noiseprotocol.org/&quot;&gt;noise&lt;/a&gt; that is made for peer-to-peer connections from the start.&lt;/p&gt;
&lt;p&gt;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.&lt;/p&gt;
&lt;p&gt;Thankfully right around the time when we were discussing this &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc7250.html&quot;&gt;raw public keys&lt;/a&gt; in TLS support was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/rustls/rustls/pull/2062&quot;&gt;merged into rustls&lt;/a&gt;. So we didn&#39;t waste any time and switched to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-0-34-0-raw-public-keys&quot;&gt;raw public keys in TLS&lt;/a&gt; soon after.&lt;/p&gt;
&lt;p&gt;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 &lt;em&gt;somehow&lt;/em&gt; connected to the internet. We also wanted these transfers to be &lt;em&gt;fast&lt;/em&gt;. So what we needed was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Hole_punching_(networking)&quot;&gt;hole punching&lt;/a&gt;. We looked at various systems that implement hole punching. Our goal was to get holepunching to work reliably, even at the expense of compromising p2p purity. If we would need a small server to help, so be it.&lt;/p&gt;
&lt;p&gt;Of all the existing open source implementations of holepunching, we found the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://tailscale.com/&quot;&gt;tailscale&lt;/a&gt; approach best suited to our requirements. So we implemented something that was initially very similar, with good results. This was a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh/pull/830&quot;&gt;huge effort&lt;/a&gt;, but once we merged it we had hole punch rates to be proud of. In most normal circumstances we punched holes almost immediately, and in the few corporate networks where hole punching was impossible, at least we could still transfer data via relays. It &lt;strong&gt;just worked&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;We achieved our goal of having a simple p2p library that just worked. But the API was still anemic. It was something that experienced rust teams such as deltachat could include, but not yet attractive to p2p application developers. So we decided to gradually add more functionality. One of the first things we did was extending the blob related API to expose more functionality for BLAKE3 verified streaming. In parallel we also optimized the verified streaming by implementing some advanced features such as &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh/pull/798&quot;&gt;BLAKE3 chunk groups&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But even with blobs included, it was not enough. We had very useful functionality for experienced rust teams, but at the time that wasn&#39;t exactly a large group. So we decided to add a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh/pull/1333&quot;&gt;document abstraction&lt;/a&gt; that would make iroh viable as a batteries included p2p application development framework, where ideally you would only need to add application specific code.&lt;/p&gt;
&lt;p&gt;As part of the push to make iroh approachable to more developers, we started to publish language bindings to almost all major languages. This worked well and was frequently used, but turned out to be a lot of work for us because of the frequent API changes on the road to 1.0.&lt;/p&gt;
&lt;p&gt;As we were adding protocols we also had to solve protocol &lt;em&gt;negotiation&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;One common pattern is to add a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/multiformats/multistream-select&quot;&gt;little handshake protocol&lt;/a&gt; on each stream where one side can propose a protocol and the other side can either accept or reject. But that would add overhead for &lt;em&gt;every single stream&lt;/em&gt; and take us further away from just peer-to-peer QUIC.&lt;/p&gt;
&lt;p&gt;We found that complex schemes for protocol evolution are often a mirage. Truly evolving a protocol in a backwards compatible way leads to a combinatorial explosion of variants that have to be tested. It is really hard to get this right.&lt;/p&gt;
&lt;p&gt;So we decided to keep things simple and use the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc7301.html&quot;&gt;ALPN TLS extension&lt;/a&gt;. The connecting side proposes a list of ALPNs, and the server side either chooses one of the ALPNs or refuses the handshake. This happens directly during the QUIC handshake, so it has no overhead other than the raw bytes of the ALPN string.&lt;/p&gt;
&lt;p&gt;All in all we are very happy with this decision. Projects like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://datatracker.ietf.org/group/moq/about/&quot;&gt;Media over QUIC&lt;/a&gt; use QUIC streams extensively in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://moq-wg.github.io/moq-transport/draft-ietf-moq-transport.html#name-data-streams-and-datagrams&quot;&gt;a very creative way&lt;/a&gt;, and since we provide just raw QUIC streams we can support these approaches out of the box.&lt;/p&gt;
&lt;p&gt;It is not entirely without downsides. The &lt;code&gt;ClientHello&lt;/code&gt; containing the ALPN is unencrypted, so on-path observers can see which protocol is used by decoding &lt;code&gt;ClientHello&lt;/code&gt;s. One way to work around this is to use a common ALPN such as &lt;code&gt;h3&lt;/code&gt; and then just negotiate the actual protocol later. In a future version of iroh we might support &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://datatracker.ietf.org/doc/draft-ietf-tls-esni/&quot;&gt;encrypted client hello&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another consequence of the decision to use ALPNs is that two iroh endpoints talking multiple protocols will have not one but &lt;em&gt;multiple&lt;/em&gt; connections open (A QUIC connection is always for &lt;em&gt;exactly one&lt;/em&gt; ALPN). We share some internal state for these connections so that e.g. hole punching doesn&#39;t have to start from zero.&lt;/p&gt;
&lt;h3&gt;Sendme and dumbpipe&lt;/h3&gt;
&lt;figure class=&quot;article-figure&quot;&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/sendme&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/svg/sendme_icon.svg&quot; alt=&quot;sendme&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;figcaption&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.dumbpipe.dev/&quot;&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/svg/dumbpipe_icon.svg&quot; alt=&quot;dumbpipe&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/a&gt;&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;To show off iroh, we published two small tools: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/sendme&quot;&gt;sendme&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.dumbpipe.dev/&quot;&gt;dumbpipe&lt;/a&gt;. These were meant to just show off different iroh capabilities, but also be useful on their own.&lt;/p&gt;
&lt;p&gt;To this day we use sendme every day to send logs around. And there are a large number of popular sendme clones: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/Kerim-Sabic/lightning-p2p&quot;&gt;Lightning P2P&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/tonyantony300/alt-sendme&quot;&gt;alt-sendme&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/ARK-Builders/ARK-Drop-Desktop&quot;&gt;ARK Drop Desktop&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/vinay-winai/DataBeam&quot;&gt;DataBeam&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/zignig/sendme-egui&quot;&gt;sendme-egui&lt;/a&gt;, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/thiswillbeyourgithub/iroh-send&quot;&gt;iroh-send&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Dumbpipe also inspired a number of projects: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/cablehead/pai-sho&quot;&gt;pai-sho&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/rustonbsd/iroh-ssh&quot;&gt;iroh-ssh&lt;/a&gt;, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/doEggi/do-ssh&quot;&gt;do-ssh&lt;/a&gt;, and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/futpib/iroh-ssh-android&quot;&gt;iroh-ssh-android&lt;/a&gt;. But more importantly it showed &lt;strong&gt;how useful&lt;/strong&gt; just direct connections by endpoint id are a building block.&lt;/p&gt;
&lt;p&gt;For a long time we did not have the ability to dial an iroh endpoint by endpoint id only. You needed the id of course, but you also needed an additional piece of info: either an ip address or a relay url. Sendme works like this to this day.&lt;/p&gt;
&lt;p&gt;But the additional information is not stable. An iroh endpoint can frequently change its public IP address, e.g. when the ISP assigns a new public IP address. It can also change its home relay when it moves to another geographic region. In addition, dealing with tickets is cumbersome for large scale systems where you work with a large number of peers.&lt;/p&gt;
&lt;p&gt;In many peer-to-peer projects the mechanism to implement such discovery is a distributed hash table. But our previous experience with DHTs was somewhat traumatic. We deciced very quickly that we did not have the resources to implement a DHT and &lt;em&gt;get adoption for it&lt;/em&gt;. So just like with the relays, we compromised a bit on peer-to-peer purity. We explored various centralized options such as cloudflare workers.&lt;/p&gt;
&lt;p&gt;But right at this time we discovered an emerging project that would allow us to have our cake and eat it too. &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/pubky/pkarr&quot;&gt;Pkarr&lt;/a&gt; is the incredibly simple idea of storing signed DNS records on the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Mainline_DHT&quot;&gt;bittorrent mainline DHT&lt;/a&gt;. We ended up using the pkarr format for our centralized address lookup service, but also giving iroh users the option to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/iroh-mainline-address-lookup&quot;&gt;use the mainline dht directly&lt;/a&gt; to get a fully decentralized address lookup system.&lt;/p&gt;
&lt;p&gt;Mainline has some unfortunate limitations that are unlikely to go away any time soon. So &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/lets-write-a-dht-1&quot;&gt;I started writing a DHT&lt;/a&gt;, using on iroh &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/0rtt-api&quot;&gt;0-rtt&lt;/a&gt; connections. But as of now this is strictly an experiment that pushes the limits for iroh connections, not a production tool.&lt;/p&gt;
&lt;p&gt;— Rüdiger&lt;/p&gt;
&lt;p&gt;After working on iroh as a batteries included toolkit for some time, the API surface was increasing substantially. And we could not guarantee the same level of quality for all parts of the API. In addition, we noticed that each of our users was using a different subset of the API. The one thing everybody had in common was that they needed dial by endpoint id and connections that reliably hole punch. So we &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/smaller-is-better&quot;&gt;made the decision&lt;/a&gt; to narrow the scope of what iroh is significantly. From now on iroh would be just direct connections dialed by endpoint id. The other functionality, that several teams depend on, was &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-0-28-let-them-have-crates&quot;&gt;moved into separate crates&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We were now at the enviable position that several important projects depended on iroh. But we needed freedom to evolve iroh, and did not want to do so on a feature branch due to previous bad experiences with this approach. So we made our first long term stable release, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-0-35-prepping-for-1-0&quot;&gt;0.35&lt;/a&gt; and started the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-0-90-the-canary-series&quot;&gt;canary series&lt;/a&gt; for people that wanted to stay on the bleeding edge on the way to 1.0.&lt;/p&gt;
&lt;p&gt;In retrospect it would have been fine to call this iroh 1.0. It is a rock solid version of iroh that many projects depend on even today. But we are happy with the decision to develop on main and let people follow along.&lt;/p&gt;
&lt;p&gt;In addition to various functionality changes we also spent quite some time thinking about naming. For example, the identity of an iroh endpoint used to be called &lt;code&gt;NodeId&lt;/code&gt;. Back when iroh came as a large binary with several different built in protocols, this made a lot of sense. Also what is now known as address lookup used to be called just &lt;code&gt;Discovery&lt;/code&gt;. But we found that discovery isn&#39;t really a good name for the process of finding relay or ip addresses for an existing endpoint id. After all you don&#39;t &lt;em&gt;discover&lt;/em&gt; the endpoint id, you just look up additional data for it.&lt;/p&gt;
&lt;p&gt;There are some address lookup providers such as mDNS that &lt;em&gt;actually&lt;/em&gt; discover new endpoints. But this is not covered by the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/iroh/latest/iroh/address_lookup/trait.AddressLookup.html&quot;&gt;AddressLookup trait&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Iroh was working just fine in most cases. But there were some dark clouds on the horizon. To get one of the main features of iroh, seamless migration of the underlying transport, to work reliably, we had to do some &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh/pull/2835&quot;&gt;not so nice hacks&lt;/a&gt;. In the short term this wasn&#39;t really a big problem. But we knew that the current approach would limit us substantially in the future.&lt;/p&gt;
&lt;p&gt;So we &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-on-QUIC-multipath&quot;&gt;made the decision&lt;/a&gt; to fundamentally rework how iroh works under the hood. We were already using multiple paths of course, but QUIC was unaware of all of this. Which was the root cause for various problems.&lt;/p&gt;
&lt;p&gt;So far we had used quinn and contributed a few improvements. But now we were doing something fundamentally new: extending quinn to support multipath. For a long time we worked with the assumption that we would upstream our changes, but as the diff grew this became increasingly unlikely. So we &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/noq-announcement&quot;&gt;decided to fork quinn&lt;/a&gt; and maintain our own QUIC implementation, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/noq&quot;&gt;noq&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Configurable key algorithms&lt;/h3&gt;
&lt;p&gt;A frequent request for iroh was to use key algorithms other than Ed25519. We ended up deciding against it. Many of our current systems such as pkarr address lookup are only ever going to work with Ed25519 keys. So while an iroh version with support for e.g. secp256k1 or P-256 would technically be straightforward, it would make iroh more complex. Our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc7250.html#section-3&quot;&gt;key encoding&lt;/a&gt; for the TLS handshake is already key agnostic, so we can likely add support for additional key algorithms without breaking the wire format.&lt;/p&gt;
&lt;h3&gt;Post quantum signatures&lt;/h3&gt;
&lt;p&gt;Currently standardized post quantum signature algorithms have very large public key sizes. So supporting them would require us to change from the endpoint id &lt;em&gt;being&lt;/em&gt; the public key to the endpoint id being something &lt;em&gt;derived from&lt;/em&gt; the public key to keep the size manageable.&lt;/p&gt;
&lt;h3&gt;Encrypted ClientHello&lt;/h3&gt;
&lt;p&gt;Encrypted ClientHello has &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.rfc-editor.org/rfc/rfc9849.html&quot;&gt;recently been standardized&lt;/a&gt;. We might want to use it or a very similar mechanism to hide handshake metadata such as the ALPN.&lt;/p&gt;
&lt;p&gt;In the four years we have been working on iroh we have written a lot of code, without any LLM assistance. We have thrown away almost as much code. What remains is a minimal library that heavily relies on existing standards and gives you authenticated, encrypted, direct connections to any endpoint, addressed by nothing but its public key.&lt;/p&gt;
&lt;p&gt;The road to 1.0 was anything but straight, but certainly extremely exciting and interesting. And we are not at the end. Iroh 1.0 just means that the wire format and API are stable. But there are various extension points both in the wire format and in the API, and we continue to improve things under the hood.&lt;/p&gt;
&lt;p&gt;Meanwhile we can&#39;t wait to see what you build on iroh.&lt;/p&gt;
&lt;p&gt;Iroh is a dial-any-device networking library that just works. Compose from an ecosystem of ready-made protocols to get the features you need, or go fully custom on a clean abstraction over dumb pipes. Iroh is open source, and already running in production on hundreds of thousands of devices.&lt;br /&gt;
To get started, take a look at our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://iroh.computer/docs&quot;&gt;docs&lt;/a&gt;, dive directly into &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh&quot;&gt;the code&lt;/a&gt;, or chat with us in our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://iroh.computer/discord&quot;&gt;discord channel&lt;/a&gt;.&lt;/p&gt;
</content:encoded>
    <category>news</category>
    <category>networking</category>
  </item>
  <item>
    <title>Iroh global content discovery</title>
    <link>https://aggr.aymericbeaumet.com/items/lobste-rs/2026-10-04-iroh-global-content-discovery/</link>
    <guid isPermaLink="false">urn:aggr:item:b545c1829ea9d6243e6c8027cfa6e02613329e2a</guid>
    <atom:link href="https://www.iroh.computer/blog/iroh-global-content-discovery" rel="via" type="text/html"/>
    <pubDate>Sun, 4 Oct 2026 19:19:42 +0000</pubDate>
    <dc:creator>iroh.computer via sanqui</dc:creator>
    <description>Comments</description>
    <content:encoded>&lt;p&gt;What got me excited about &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ipfs.tech&quot;&gt;IPFS&lt;/a&gt; many years ago, briefly after it was announced, was being able to publish a personal website, blog post, or political pamphlet and have it remain available globally as long as enough people are interested in the content. As governments have been more sophisticated in their firewalling methods, this use case for circumvention tools and permissionless global content discovery is still incredibly relevant.&lt;/p&gt;
&lt;p&gt;Recent events have added some urgency to this. IPFS &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ipshipyard.com/blog/2026-the-end-of-ipfs-at-shipyard/&quot;&gt;shipyard is shutting down&lt;/a&gt;. This does not mean that IPFS will stop working, but it does not bode well for the future of the project.&lt;/p&gt;
&lt;p&gt;When we had to solve hole punching, we started looking at existing systems and chose the best open source system as an initial starting point for our own implementation. So let&#39;s do the same for global content discovery.&lt;/p&gt;
&lt;p&gt;There are a number of projects trying to solve this problem. But one project stands above all others: &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.bittorrent.org/&quot;&gt;BitTorrent&lt;/a&gt;. It &lt;em&gt;just works&lt;/em&gt; and has done so for over two decades.&lt;/p&gt;
&lt;p&gt;So let&#39;s take a look at what makes BitTorrent the current leader in permissionless global content discovery. BitTorrent has a relatively simple protocol for blob transfer and a &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Distributed_hash_table&quot;&gt;DHT&lt;/a&gt; called &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.bittorrent.org/beps/bep_0005.html&quot;&gt;&lt;em&gt;Mainline&lt;/em&gt;&lt;/a&gt; for global content discovery.&lt;/p&gt;
&lt;p&gt;The transfer protocol and content discovery are &lt;em&gt;separate systems&lt;/em&gt;. In fact, the DHT was developed later than the transfer protocol. BitTorrent was released in 2001 using centralized trackers for content discovery; the Mainline DHT was added in 2005.&lt;/p&gt;
&lt;p&gt;BitTorrent works by creating a &lt;code&gt;.torrent&lt;/code&gt; file that contains information about the data to be downloaded. The file is encoded using &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.bittorrent.org/beps/bep_0003.html#bencoding&quot;&gt;bencode&lt;/a&gt;, which is conceptually similar to JSON.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;pieces&lt;/code&gt; contains the concatenated SHA-1 hashes of all &lt;code&gt;piece length&lt;/code&gt;-sized pieces. The transfer protocol downloads blocks of these pieces from different peers.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-1&quot; id=&quot;fnref-1&quot; data-footnote-ref&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The transfer protocol allows requests for ranges within pieces, but you can only validate a piece &lt;strong&gt;after&lt;/strong&gt; you have downloaded it completely and computed its SHA-1 hash.&lt;/p&gt;
&lt;p&gt;I don&#39;t want to dwell on this too long, but I think that &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/oconnor663/bao#readme&quot;&gt;BLAKE3 verified streaming&lt;/a&gt; is a superior replacement for the transfer protocol. We implemented a protocol called &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.iroh.computer/protocols/blobs&quot;&gt;iroh-blobs&lt;/a&gt; that uses BLAKE3 verified streaming for sharing blobs of data over iroh connections. With BLAKE3, we only need a single root hash, and neither a &lt;code&gt;piece length&lt;/code&gt; parameter nor hashes of the individual pieces. This is what allows iroh-blobs to validate any piece of a large blob without needing intermediate hashes.&lt;/p&gt;
&lt;p&gt;For a visual explanation of how it works, see my &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.youtube.com/watch?v=nk4nefmguZk&quot;&gt;BLAKE3 and Bao deep dive&lt;/a&gt;, which covers verified streaming, outboard encoding, and range requests.&lt;/p&gt;
&lt;p&gt;We still have some work to do to make multiprovider downloads of single blobs efficient, but the streaming protocol itself with its fine grained validation is superior to the BitTorrent transfer protocol.&lt;/p&gt;
&lt;p&gt;Initially BitTorrent used trackers to get providers for a torrent. What the DHT adds is a way to get providers &lt;em&gt;without&lt;/em&gt; having to rely on trackers.&lt;/p&gt;
&lt;p&gt;This works by computing the SHA-1 hash of the &lt;code&gt;info&lt;/code&gt; section, which includes the hashes of all pieces. Then you announce on the DHT that you have content for this hash. The mainline functions for this are &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.bittorrent.org/beps/bep_0005.html#announce-peer&quot;&gt;&lt;code&gt;announce_peer&lt;/code&gt;&lt;/a&gt; and &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.bittorrent.org/beps/bep_0005.html#get-peers&quot;&gt;&lt;code&gt;get_peers&lt;/code&gt;&lt;/a&gt;. Each DHT node will store a large set of providers.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/animations/announce-peer-dht-dark.svg?v=12&quot; alt=&quot;Bob and Carol announce the same infohash to DHT nodes. Each node keeps both provider addresses in a list. Alice uses get_peers to retrieve the providers.&quot; loading=&quot;eager&quot; decoding=&quot;async&quot; fetchpriority=&quot;high&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/animations/announce-peer-dht.svg?v=12&quot; alt=&quot;Bob and Carol announce the same infohash to DHT nodes. Each node keeps both provider addresses in a list. Alice uses get_peers to retrieve the providers.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Many more modern protocols also use DHTs for content discovery. But Mainline works extremely well compared to many modern alternatives. It is also an extremely large and stable public DHT deployment, so it is unlikely to go away any time soon. You can look at current &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://ipinfo.io/tags/bittorrent&quot;&gt;mainline statistics&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/blog/iroh-global-content-discovery/mainline-statistics.png&quot; alt=&quot;Mainline DHT statistics from IPinfo.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;So let&#39;s take a look at why.&lt;/p&gt;
&lt;p&gt;The mainline protocol takes into account that a DHT operates across an &lt;em&gt;extremely large&lt;/em&gt; number of nodes. You &lt;strong&gt;can&#39;t&lt;/strong&gt; afford large per-node connection state. Therefore both queries and responses are constrained to fit into single non-fragmented UDP packets.&lt;/p&gt;
&lt;p&gt;There are a number of other limitations compared to modern DHT implementations that all serve an important purpose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;the data stored for a provider is just the public &lt;code&gt;host:port&lt;/code&gt; pair of the provider &lt;em&gt;as seen from the DHT node&lt;/em&gt;. There is no user defined information in a provider record.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-2&quot; id=&quot;fnref-2&quot; data-footnote-ref&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;For newer extensions like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.bittorrent.org/beps/bep_0044.html&quot;&gt;BEP 44&lt;/a&gt; the data is fully self-contained and verifiable, either a tiny piece of data or a signed record, both limited to fit into a typical MTU.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;you can only store data after first querying the DHT node and returning the short-lived token from its response, &lt;em&gt;proving&lt;/em&gt; that &lt;em&gt;at publishing time&lt;/em&gt; you can receive packets at your claimed IP address.&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;#fn-3&quot; id=&quot;fnref-3&quot; data-footnote-ref&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The result of this minimalism is that mainline lookups typically complete in less than a second.&lt;/p&gt;
&lt;p&gt;Here is a real BEP 44 lookup of a pkarr record using the &lt;code&gt;get_mutable&lt;/code&gt; example from &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/n0-mainline&quot;&gt;n0-mainline&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you are traumatized by DHT lookups taking forever or timing out: &lt;strong&gt;it doesn&#39;t have to be this way&lt;/strong&gt;. The mainline DHT shows that millisecond lookups are possible at a global scale.&lt;/p&gt;
&lt;h2&gt;&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-global-content-discovery#mainline-for-finding-blobs-providers&quot;&gt;Mainline for finding blobs providers&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that we have established why mainline works so well, let&#39;s see if there is a way to use it for iroh blobs content discovery.&lt;/p&gt;
&lt;p&gt;Mainline provider records are just an IPv4 &lt;code&gt;host:port&lt;/code&gt; pair. But iroh connections are dialed by cryptographic identity, currently the Ed25519 public key aka &lt;code&gt;EndpointId&lt;/code&gt;. So to use mainline provider records for iroh blobs endpoint discovery, we would need a way to know which &lt;code&gt;EndpointId&lt;/code&gt; is currently listening on this &lt;code&gt;host:port&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In most cases this &lt;code&gt;host:port&lt;/code&gt; will be behind a NAT, so it is &lt;em&gt;not reachable&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I tried a number of ways to use current mainline mechanisms such as BEP 44 to store this mapping, but currently this is not possible. So we need a tiny extra UDP address index service (&lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-content-discovery/tree/main/udp-addr-index&quot;&gt;&lt;code&gt;udp-addr-index&lt;/code&gt;&lt;/a&gt;) to provide this mapping.&lt;/p&gt;
&lt;p&gt;This can be an incredibly simple service. It just stores some tiny arbitrary metadata for each verified UDP &lt;code&gt;host:port&lt;/code&gt; pair and is reachable exclusively via a simple single-packet UDP protocol. Since mainline requires UDP, and this is an extension for mainline, we don&#39;t need to handle the case where sending UDP packets is not possible.&lt;/p&gt;
&lt;p&gt;Writes need a mechanism similar to the mainline or QUIC token mechanism to verify that the remote is actually reachable on the &lt;code&gt;host:port&lt;/code&gt; pair. Reads don&#39;t need this, we just require a padded query packet to prevent amplification.&lt;/p&gt;
&lt;p&gt;The service stores up to 1 KiB of arbitrary metadata. It&#39;s a &lt;em&gt;last writer wins&lt;/em&gt; map from a live UDP &lt;code&gt;host:port&lt;/code&gt; pair to a tiny blob. Mappings expire after some time, so a content provider has to update the mapping at regular intervals as well as when its public UDP &lt;code&gt;host:port&lt;/code&gt; pair changes.&lt;/p&gt;
&lt;p&gt;The current implementation keeps the map purely in memory. Records have to be updated at regular intervals anyway, and not requiring persistence makes the service much simpler and cheaper to operate. You can run an address index service for millions of iroh endpoints on a single small box with a publicly reachable UDP socket.&lt;/p&gt;
&lt;p&gt;The address index service does not depend in any way on iroh. It is infrastructure that could also be useful for non iroh applications using mainline.&lt;/p&gt;
&lt;p&gt;In the long term I would love the address index service function to be handled by a BitTorrent Mainline extension. It is simple, generically useful, and not specific to iroh.&lt;/p&gt;
&lt;p&gt;For our endpoint discovery purposes we store a record containing the endpoint id, current UDP socket addr, and a timestamp, signed with the endpoint private key. So &lt;em&gt;only the owner of the endpoint key&lt;/em&gt; can write a valid signed record, so you can&#39;t impersonate other endpoints.&lt;/p&gt;
&lt;p&gt;The address index service however does &lt;strong&gt;not&lt;/strong&gt; check anything about the endpoint. What &lt;code&gt;get_peers&lt;/code&gt; in conjunction with this service gives us is just a list of &lt;em&gt;candidate&lt;/em&gt; iroh endpoints which &lt;em&gt;might&lt;/em&gt; serve the content.&lt;/p&gt;
&lt;p&gt;Endpoint discovery doesn&#39;t have to be for content discovery. We also have an example that shows how to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-content-discovery/blob/main/iroh-mainline-endpoint-discovery/examples/gossip.rs&quot;&gt;discover peers for a gossip topic&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;So now let&#39;s take a look at the overall workflow when announcing and discovering content. For both announce and discovery we will need a mainline DHT node. We use the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/n0-mainline&quot;&gt;&lt;code&gt;n0_mainline&lt;/code&gt;&lt;/a&gt; crate just like in the existing &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.rs/iroh-mainline-address-lookup&quot;&gt;&lt;code&gt;iroh-mainline-address-lookup&lt;/code&gt;&lt;/a&gt; crate.&lt;/p&gt;
&lt;h3&gt;Announce&lt;/h3&gt;
&lt;p&gt;We want to associate the UDP socket of &lt;code&gt;n0_mainline&lt;/code&gt; with our &lt;code&gt;EndpointId&lt;/code&gt;. As outlined above, we use an UDP addr index server for this: we publish a signed record containing our &lt;code&gt;EndpointId&lt;/code&gt; via the same UDP socket to the addr index server. &lt;code&gt;n0_mainline&lt;/code&gt; has a mechanism to publish arbitrary UDP packets on its socket and to intercept incoming UDP packets. This needs to happen at regular intervals as well as immediately when the public &lt;code&gt;host:port&lt;/code&gt; pair changes.&lt;/p&gt;
&lt;p&gt;For the announce itself: the mainline keyspace is 20 bytes, usually used to announce SHA-1 hashes of the info section of a .torrent file. We want to announce BLAKE3 hashes, so we first compute the SHA-1 hash of the BLAKE3 hash. Then we use &lt;code&gt;announce_peer&lt;/code&gt; to announce that we are providing data for that hash. These announces also need to happen at regular intervals.&lt;/p&gt;
&lt;p&gt;You might wonder if SHA-1 is still safe for this purpose. Its collision resistance is broken, but there is no known practical preimage attack. More importantly, the DHT is just a best-effort mechanism for finding candidate providers. We verify downloaded data against the original BLAKE3 hash, so a misleading DHT result can waste time, but cannot make us accept the wrong content.&lt;/p&gt;
&lt;h3&gt;Discovery&lt;/h3&gt;
&lt;p&gt;For discovery we first need to find at least one working service that can translate &lt;code&gt;host:port&lt;/code&gt; pairs into &lt;code&gt;EndpointId&lt;/code&gt;s. We have two mechanisms built in for this: a rendezvous hash that allows address index services to announce themselves, and a BEP 44 record that is a &lt;em&gt;curated list&lt;/em&gt; of good address index services. Announce and discovery need to agree on the rendezvous hash or BEP 44 record name to find address index services.&lt;/p&gt;
&lt;p&gt;Once we have at least one working address index service, we compute the SHA-1 hash of the BLAKE3 hash we are looking for and call &lt;code&gt;get_peers&lt;/code&gt;. The output of &lt;code&gt;get_peers&lt;/code&gt; is a list of &lt;code&gt;host:port&lt;/code&gt; pairs which we translate to &lt;code&gt;EndpointId&lt;/code&gt;s using the address index service.&lt;/p&gt;
&lt;p&gt;At this point we get a stream of &lt;em&gt;unverified&lt;/em&gt; &lt;code&gt;EndpointId&lt;/code&gt;s. We currently do a quick BLAKE3 size query for the content we are looking for to make sure they are live and serve the right content, then hand them over to the iroh-blobs downloader to download the actual content.&lt;/p&gt;
&lt;p&gt;Note that the actual connection now uses the &lt;code&gt;EndpointId&lt;/code&gt; and iroh&#39;s built in address lookup and hole punching to establish a connection. The QUIC connection does &lt;em&gt;not&lt;/em&gt; work via the announced UDP &lt;code&gt;host:port&lt;/code&gt; pair. That is just a key for the address index service.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/animations/content-discovery-workflow-dark.svg?v=9&quot; alt=&quot;Bob and Carol publish address-to-endpoint mappings to the address index service and announce content to Mainline. Alice gets provider addresses from Mainline, then resolves them to candidate endpoint IDs through the address index service.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/animations/content-discovery-workflow.svg?v=9&quot; alt=&quot;Bob and Carol publish address-to-endpoint mappings to the address index service and announce content to Mainline. Alice gets provider addresses from Mainline, then resolves them to candidate endpoint IDs through the address index service.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;All of the above is implemented in the experimental &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-content-discovery&quot;&gt;iroh-content-discovery&lt;/a&gt; repository. Its workspace contains the address index service, its protocol and client as well as a few extra crates that make use of it.&lt;/p&gt;
&lt;p&gt;To see the entire workflow in action, we can run the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-content-discovery/blob/main/iroh-mainline-endpoint-discovery/examples/blobs/main.rs&quot;&gt;&lt;code&gt;blobs&lt;/code&gt; example&lt;/a&gt;. It runs both publish and resolve in one process, but discovery nevertheless goes via mainline and the address index service.&lt;/p&gt;
&lt;p&gt;So now we have a mechanism to do content discovery for blobs. This will be useful for tools like &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/sendme&quot;&gt;sendme&lt;/a&gt; and in general for sending around large amounts of data.&lt;/p&gt;
&lt;p&gt;But what about permissionless publishing of websites such as a blog? To make this work we need some extra components.&lt;/p&gt;
&lt;p&gt;We need a syntax for content-addressed links. We don&#39;t go into a giant rabbit hole about how to encode these links. Our content addressed links are always BLAKE3. We need to reserve some global namespace for them, so we reserved a domain &lt;code&gt;blake3.net&lt;/code&gt;. A content-addressed link is just &lt;code&gt;https://&amp;lt;hash&amp;gt;.blake3.net&lt;/code&gt;, with the hash encoded using &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt&quot;&gt;zbase32&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/blog/iroh-global-content-discovery/link-syntax-dark.svg?v=2&quot; alt=&quot;A content-addressed URL with its 52-character z-base-32 BLAKE3 hash and blake3.net namespace labeled.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/blog/iroh-global-content-discovery/link-syntax.svg?v=2&quot; alt=&quot;A content-addressed URL with its 52-character z-base-32 BLAKE3 hash and blake3.net namespace labeled.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;We don&#39;t want to run an actual gateway at &lt;code&gt;blake3.net&lt;/code&gt;. That would be a &lt;em&gt;very bad idea&lt;/em&gt; for various reasons.&lt;/p&gt;
&lt;p&gt;Instead we want the browser to interpret these links as something that can be resolved in a different way. So we wrote a simple browser plugin that just rewrites these links to &lt;code&gt;http://&amp;lt;hash&amp;gt;.blake3.localhost:&amp;lt;port&amp;gt;&lt;/code&gt; where the port is configurable in the plugin.&lt;/p&gt;
&lt;p&gt;That&#39;s all the plugin does.&lt;/p&gt;
&lt;p&gt;It currently works for Brave, Chrome, and Firefox.&lt;/p&gt;
&lt;p&gt;For Chrome and Brave, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://chromewebstore.google.com/detail/iroh-link/aajlbmaphckgbinhnifpiggcmdfnofcd&quot;&gt;install iroh link from the Chrome Web Store&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Firefox extension is awaiting review. For now, save the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-content-discovery/releases/download/extension-v0.2.1/iroh-link-0.2.1-firefox-unsigned.xpi&quot;&gt;unsigned Firefox XPI&lt;/a&gt; to your computer using &lt;strong&gt;Save Link As&lt;/strong&gt;. In Firefox 140 or later, open &lt;code&gt;about:debugging#/runtime/this-firefox&lt;/code&gt;, click &lt;strong&gt;Load Temporary Add-on&lt;/strong&gt;, and select the downloaded file. Open the extension popup, click &lt;strong&gt;Save&lt;/strong&gt;, and allow access to the requested sites. You must load it again after restarting Firefox; the unsigned package cannot be installed through &lt;strong&gt;Install Add-on From File&lt;/strong&gt; in regular Firefox.&lt;/p&gt;
&lt;p&gt;The last component is a local gateway that serves content-addressed data on localhost. It interacts with mainline to find content and orchestrates the actual blobs downloads. It supports &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.iroh.computer/protocols/blobs#collections&quot;&gt;iroh-blobs collections&lt;/a&gt; and shows a directory index for them. It also does file type detection. It is derived from iroh blobs gateway in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-examples/tree/main/iroh-gateway&quot;&gt;iroh-examples&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;You can &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-content-discovery/tree/main/iroh-link-gateway&quot;&gt;compile the gateway from source&lt;/a&gt;, or &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-content-discovery/releases/latest&quot;&gt;download the latest release&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We could build a service worker to verify the data inside the browser process.&lt;/p&gt;
&lt;p&gt;Instead we verify the data inside the gateway.&lt;/p&gt;
&lt;p&gt;It is software that you have to trust, whether it lives in the browser process or elsewhere doesn&#39;t matter much.&lt;/p&gt;
&lt;p&gt;With these two components in place we can browse content-addressed data.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/blog/iroh-global-content-discovery/content-addressed-browsing.png&quot; alt=&quot;The local iroh gateway displaying the Open Content Library directory with Books, Essays, Films, LLM, and Website folders.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Now we have a mechanism to publish and consume content-addressed data. But we don&#39;t have a way to refer to &lt;em&gt;mutable&lt;/em&gt; data. You could use &lt;code&gt;blake3.net&lt;/code&gt; links from an existing website, but for that you need a registrar and a hoster, so it is not the permissionless publishing we are after.&lt;/p&gt;
&lt;p&gt;Fortunately a permissionless DNS system already exists, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://pkarr.org&quot;&gt;pkarr&lt;/a&gt;. We &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/iroh-global-node-discovery&quot;&gt;have been using it&lt;/a&gt; for years for endpoint address lookup. Pkarr is a standard to publish a DNS record for a keypair, so only the owner of the secret key can publish new versions. The records are published on mainline DHT, which fits our setup neatly.&lt;/p&gt;
&lt;p&gt;To make this compatible with our browser plus local gateway usage scenario, we reserved another domain &lt;code&gt;pkarr.net&lt;/code&gt; and added a rewrite rule to the plugin and pkarr support to the gateway.&lt;/p&gt;
&lt;p&gt;A pkarr link has the format &lt;code&gt;&amp;lt;name&amp;gt;.pkarr.net&lt;/code&gt;, where &lt;code&gt;name&lt;/code&gt; is the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://philzimmermann.com/docs/human-oriented-base-32-encoding.txt&quot;&gt;zbase32&lt;/a&gt; encoding of an Ed25519 public key. The plugin rewrites it to &lt;code&gt;&amp;lt;name&amp;gt;.pkarr.localhost:&amp;lt;port&amp;gt;&lt;/code&gt;. The gateway will then resolve the pkarr record and either perform a redirect or directly serve content-addressed data if the pkarr record links to &lt;code&gt;hash.blake3.net&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/animations/pkarr-link-rewrite-dark.svg?v=2&quot; alt=&quot;The browser plugin keeps the public key unchanged while rewriting https://uinsazmmp47ejo8gs5dbc6rfxya14cgqhxmdqin8ae55w5aqnsio.pkarr.net to http://uinsazmmp47ejo8gs5dbc6rfxya14cgqhxmdqin8ae55w5aqnsio.pkarr.localhost:1234.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/animations/pkarr-link-rewrite.svg?v=2&quot; alt=&quot;The browser plugin keeps the public key unchanged while rewriting https://uinsazmmp47ejo8gs5dbc6rfxya14cgqhxmdqin8ae55w5aqnsio.pkarr.net to http://uinsazmmp47ejo8gs5dbc6rfxya14cgqhxmdqin8ae55w5aqnsio.pkarr.localhost:1234.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;If you are familiar with IPFS, this is very similar to &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://docs.ipfs.tech/concepts/ipns/&quot;&gt;IPNS&lt;/a&gt;. I even did a little toy experiment called &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-experiments/tree/main/iroh-pkarr-naming-system&quot;&gt;Iroh Pkarr Naming System&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-content-discovery/blob/main/iroh-mainline-endpoint-discovery/examples/pkarr-publish-resolve.rs&quot;&gt;&lt;code&gt;pkarr-publish-resolve&lt;/code&gt; example&lt;/a&gt; in &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-content-discovery&quot;&gt;iroh-content-discovery&lt;/a&gt; demonstrates the whole workflow: generate a keypair, publish a blob and its name, then resolve the name, discover a provider, and download the verified content from a separate client.&lt;/p&gt;
&lt;p&gt;Pkarr gives us permissionless names, but not human-readable ones: anyone can generate a keypair and publish under its public key, without asking a naming authority. This is the tradeoff illustrated by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://en.wikipedia.org/wiki/Zooko%27s_triangle&quot;&gt;Zooko’s triangle&lt;/a&gt;: pkarr names are decentralized and cryptographically verifiable, but a 52-character encoded public key is not a memorable name.&lt;/p&gt;
&lt;p&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/blog/iroh-global-content-discovery/zookos-triangle-dark.svg?v=3&quot; alt=&quot;Zooko’s triangle: pkarr sits between decentralized and secure; DNS with DNSSEC sits between human-readable and secure.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;picture class=&quot;article-picture article-picture-fallback&quot; style=&quot;--image-ratio:16 / 9&quot;&gt;&lt;img src=&quot;https://www.iroh.computer/blog/iroh-global-content-discovery/zookos-triangle.svg?v=3&quot; alt=&quot;Zooko’s triangle: pkarr sits between decentralized and secure; DNS with DNSSEC sits between human-readable and secure.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; fetchpriority=&quot;low&quot; referrerpolicy=&quot;no-referrer&quot; class=&quot;progressive-image&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;You can combine human readable naming systems with pkarr to get a memorable name that you can retarget in a permissionless way. We have some ideas about how to do this, stay tuned.&lt;/p&gt;
&lt;p&gt;The first step is to run the local gateway. Most readers of this blog will probably just compile it from source, but there are also installers for windows and MacOS on github.&lt;/p&gt;
&lt;p&gt;The next step is to install the browser plugin. For Chrome and Brave, &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://chromewebstore.google.com/detail/iroh-link/aajlbmaphckgbinhnifpiggcmdfnofcd&quot;&gt;install iroh link from the Chrome Web Store&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Make sure the port configured on the gateway and the browser plugin match. The default is &lt;code&gt;45475&lt;/code&gt; or &lt;code&gt;B1A3&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;And then you are ready to browse the content-addressed web.&lt;/p&gt;
&lt;p&gt;Try &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://y7rmokt6h5mryuauw83em4u1br6tqrukaw3ngtde7zp8p3bg6hto.blake3.net/&quot;&gt;https://y7rmokt6h5mryuauw83em4u1br6tqrukaw3ngtde7zp8p3bg6hto.blake3.net/&lt;/a&gt; for some static content, or &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://5ti57aszf7kaicsncb4wgigkf9bju39kofiz8dthwdujkmz85u8y.pkarr.net/&quot;&gt;https://5ti57aszf7kaicsncb4wgigkf9bju39kofiz8dthwdujkmz85u8y.pkarr.net/&lt;/a&gt; for a pkarr record &lt;em&gt;currently&lt;/em&gt; pointing to the above.&lt;/p&gt;
&lt;p&gt;Traditional website publishing just means copying your files to a directory on a server. But for content-addressed data and permissionless pkarr DNS records, &lt;em&gt;you&lt;/em&gt; are the publisher.&lt;/p&gt;
&lt;p&gt;So we wrote a tool iroh-share that simplifies publishing. You can think of it as &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/sendme&quot;&gt;sendme&lt;/a&gt;, but running as a daemon with a separate user interface.&lt;/p&gt;
&lt;p&gt;Both content-addressed data and pkarr records need continuous announcements, so you might want to run this daemon on a small box in your attic that is on 24/7, or a vm in the cloud. I run it on an old Synology NAS in my attic. It&#39;s behind a NAT of course, but you do get direct connections anyway.&lt;/p&gt;
&lt;p&gt;You can find releases at &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh-share/releases&quot;&gt;https://github.com/n0-computer/iroh-share/releases&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We now have a system for global content discovery of BLAKE3 hashed content addressed data. It is &lt;em&gt;far from perfect&lt;/em&gt;, but &lt;em&gt;it&#39;s a start&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;user interface&lt;/em&gt; of sharing content addressed data is extremely simple. You just state what content you want, and the gateway gets it for you.&lt;/p&gt;
&lt;p&gt;The &lt;em&gt;system behind it&lt;/em&gt; has a lot of room for improvement.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Mainline is quite scalable, but it probably won&#39;t scale for the vision of making all content on the internet content-addressable.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Mainline traffic is also unencrypted and therefore easily blocked by middleboxes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The existing pkarr naming mechanism is using Ed25519 and therefore is not post quantum secure.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;And perhaps most importantly, mainline &lt;em&gt;does not provide any privacy&lt;/em&gt;. If you share content, &lt;em&gt;anybody can look up your ip address&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;But we are currently in the &lt;em&gt;food and shelter&lt;/em&gt; phase of global content discovery. We will continue to improve the underlying content discovery system, possibly by extending mainline, possibly by &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/lets-write-a-dht-1&quot;&gt;writing our own DHT&lt;/a&gt; using iroh connections.&lt;/p&gt;
&lt;p&gt;But the current system is certainly &lt;em&gt;better than nothing&lt;/em&gt;, and we can do all these improvements while keeping the user interface stable.&lt;/p&gt;
&lt;p&gt;We are currently working on getting our most frequently used iroh protocols &lt;code&gt;irpc&lt;/code&gt;, &lt;code&gt;iroh-gossip&lt;/code&gt; and &lt;code&gt;iroh-blobs&lt;/code&gt; to 1.0. The endpoint discovery mechanism is experimental, but all relevant crates are published on crates.io for you to play with.&lt;/p&gt;
&lt;hr /&gt;
&lt;p&gt;Iroh is a dial-any-device networking library that just works. Compose from an ecosystem of ready-made protocols to get the features you need, or go fully custom on a clean abstraction over dumb pipes. Iroh is open source, and already running in production on hundreds of thousands of devices.&lt;br /&gt;
To get started, take a look at our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://iroh.computer/docs&quot;&gt;docs&lt;/a&gt;, dive directly into &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://github.com/n0-computer/iroh&quot;&gt;the code&lt;/a&gt;, or chat with us in our &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://iroh.computer/discord&quot;&gt;discord channel&lt;/a&gt;.&lt;/p&gt;
&lt;section class=&quot;footnotes&quot; data-footnotes&gt;
&lt;ol&gt;
&lt;li id=&quot;fn-1&quot;&gt;
&lt;p&gt;In &lt;em&gt;endgame mode&lt;/em&gt;, it may request the same remaining blocks from multiple peers and use whichever responses arrive first. &lt;a href=&quot;#fnref-1&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;1&quot; aria-label=&quot;Back to reference 1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-2&quot;&gt;
&lt;p&gt;Technically, you can choose the port in an &lt;code&gt;announce_peer&lt;/code&gt; request. But that gives you only 16 bits, which is not enough to store, for example, a 32-byte iroh &lt;code&gt;EndpointId&lt;/code&gt;. &lt;a href=&quot;#fnref-2&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;2&quot; aria-label=&quot;Back to reference 2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn-3&quot;&gt;
&lt;p&gt;This mechanism is similar to the &lt;a target=&quot;_blank&quot; rel=&quot;noopener noreferrer&quot; href=&quot;https://www.iroh.computer/blog/address-validation-tokens&quot;&gt;address validation token&lt;/a&gt; in QUIC. &lt;a href=&quot;#fnref-3&quot; class=&quot;footnote-backref&quot; data-footnote-backref data-footnote-backref-idx=&quot;3&quot; aria-label=&quot;Back to reference 3&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;
</content:encoded>
    <category>news</category>
    <category>distributed</category>
  </item>
</channel>
</rss>
