iroh 1.0 之路

Lobsters Hottest 工具

摘要

经过四年的开发以及放弃 IPFS 兼容性的战略转向,iroh 正式发布了 1.0 版本。本文回顾了项目的发展历程——从最初的 Rust IPFS 实现,到如今基于 QUIC(通过 quinn)和 BLAKE3 验证流构建的轻量级库,实现了近乎最优的 Blob 数据传输。

<p><a href="https://lobste.rs/s/6laoto/road_iroh_1_0">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/10/16 12:43

# iroh 1.0 的道路 来源: https://www.iroh.computer/blog/the-road-to-iroh-1-0 完成了。经过四年的努力和多次方向调整,我们正式发布了 iroh 1.0。 我们有一个不断扩展的网站,详细描述 iroh 底层的*工作原理*,但我们认为理解我们做出这些决策、形成当前 iroh 形态的*原因*同样重要。所以这是一次对记忆和提交历史的回顾之旅,着重呈现关键的转折点和设计决策。 更重要的是,这篇文章也涵盖了我们曾经考虑过但最终**没有做**的事情。 iroh 最初是一个用 Rust 实现的 ipfs (https://ipfs.tech/)。我们实现了各种 ipfs 协议,走得相当远,甚至能与现有的 ipfs 实现进行互操作。我们对 iroh 的目标是面向轻量级和资源受限的设备,比如手机。 有一个领域让我们对现有协议感到不满,那就是大文件传输(blob transfer)。我们在 ipfs 社区中发起了一个工作组来设计一种新协议,称为 move the bytes 工作组 (https://www.youtube.com/playlist?list=PLuhRWgmPaHtQ--aQ5GlgCyKkQYXUfQpVf)。 在这个工作组的框架下,我们为新的 blob 传输协议编写了一份提案。我们写了一个小型库,名为 krakensync (https://github.com/n0-computer/krakensync)。Krakensync 完全使用 BLAKE3 (https://github.com/BLAKE3-team/BLAKE3) 进行内容寻址,因为 BLAKE3 支持对大文件进行经过验证的增量流式传输。 几天之内我们完成了一个基础算法,想要为它添加网络功能,让演示看起来更真实一些。距离下一次会议只有两天,而通过 rust-libp2p (https://docs.rs/libp2p) 添加网络功能似乎不可能(记住那是在 LLM 编程助手出现之前!)。 我之前对 QUIC (https://www.rfc-editor.org/rfc/rfc9000.html) 有一点经验,还记得朋友的一个项目 quinn-noise (https://docs.rs/quinn-noise),从中了解到 quinn (https://docs.rs/quinn) 既有可扩展性又有简洁的惯用 Rust API。于是我尝试用 quinn 来添加网络功能,发现使用体验*令人愉悦*。 ——Rüdiger 在并行开发 ipfs 兼容版 iroh 的同时,我们还在做一个副项目,实现一个具体的现实场景:在配置新设备时迁移 deltachat (https://delta.chat/) 的聊天记录。技术任务是将一份包含备份数据的单个大文件传输到新设备。在磁盘和网络使用方面有严格的约束,而且传输必须支持断点续传。 我们发现,*无法*使用基于 ipfs 的 iroh 实现来完成这个非常具体的用例。这样做会导致网络性能不够理想,并且会引入非常庞大的依赖树。 所以我们从零开始编写了一个轻量级的同步库,以 QUIC 作为网络层,使用 BLAKE3 验证流式传输来处理大文件。 尽管我们尽了最大努力,主项目仍然距离理论极限还很远。与此同时,同步副项目展示了什么是可能的。因此我们 [做出了决定](https://www.iroh.computer/blog/a-new-direction-for-iroh),不再追求与现有 ipfs 实现保持兼容的目标。 不再使用 [multiaddrs](https://github.com/multiformats/multiaddr),端点改用 [Ed25519](https://www.rfc-editor.org/rfc/rfc8032.html) 公钥来标识。不再使用 [multihashes](https://github.com/multiformats/multihash),我们采用 BLAKE3。不再支持多种传输协议,只保留 QUIC。 新的 iroh 就是基于 QUIC 的网络功能(使用 [quinn](https://docs.rs/quinn))加上使用 BLAKE3 验证流式传输的大文件传输。与之前的版本相比,功能非常单薄。但它所做的事情,做到了接近最优的性能。它是一个足够轻量的依赖,[deltachat 可以将其纳入他们的 Rust 代码库](https://github.com/chatmail/core/commit/24856f3050be9a7705f9df736ab575fd856454d5)。突然之间,我们同时拥有了网络和同步功能,部署在了一个被数千人在各个平台上使用的真实应用中(Windows、Linux、iOS、Android)。旧代码延续在 [beetle](https://github.com/n0-computer/beetle) 中。 我们决定只使用 QUIC。但 RFC 9000 中定义的 QUIC 有明确的客户端和服务器角色,其中服务器通过证书链来标识。当时我们需要在 QUIC 之上添加某种复杂的变通构造,才能让 QUIC 用于点对点连接——使用自签名证书在自定义 X.509 扩展中携带真实的公钥 ([https://github.com/libp2p/specs/blob/master/tls/tls.md](https://github.com/libp2p/specs/blob/master/tls/tls.md)),并用签名将两者绑定。 另外,rustls 是一个重量级依赖,其默认的加密实现是一个 C 语言依赖。因此我们曾一度考虑用基于 [Noise](https://noiseprotocol.org/) 协议的方案替换整个 TLS 加密方案,Noise 从一开始就是为点对点连接设计的。 最终我们还是坚持使用 TLS 这一行业标准。不使用 TLS 的 iroh 就不能真正算是 QUIC,只是复用了它的部分组件。但我们仍然对自签名证书不太满意。 幸运的是,就在我们讨论这个问题的时候,TLS 中对 [Raw Public Keys](https://www.rfc-editor.org/rfc/rfc7250.html) 的支持被[合并进了 rustls](https://github.com/rustls/rustls/pull/2062)。所以我们没有浪费时间,很快就[切换到了 TLS Raw Public Keys](https://www.iroh.computer/blog/iroh-0-34-0-raw-public-keys)。 还有一件事让我们不满意:deltachat 聊天记录传输要正常工作,两台设备必须在同一个网络中。理想情况下,只要两台设备*以某种方式*连接到互联网,就应该能工作。我们也希望这些传输是*快速的*。所以我们需要的是[打洞(Hole Punching)](https://en.wikipedia.org/wiki/Hole_punching_(networking))。我们研究了各种实现了打洞的系统。我们的目标是让打洞可靠地工作,即使这意味着在点对点纯粹性上做出妥协。如果需要一个小的辅助服务器,那就需要。 在所有现有的开源打洞实现中,我们认为 [tailscale](https://tailscale.com/) 的方法最适合我们的需求。所以我们实现了一个最初非常类似的方案,效果很好。这是一个[巨大的工程](https://github.com/n0-computer/iroh/pull/830),但一旦合并,我们的打洞成功率就值得自豪了。在大多数正常环境下,我们几乎瞬间完成打洞;在少数打洞不可能的企业网络中,至少我们还能通过中继(relay)传输数据。它**就是能用**。 我们实现了做一个简单、真正能用的点对点库的目标。但 API 依然很单薄。它对于 deltachat 这样的资深 Rust 团队来说可以引入使用,但对于点对点应用开发者来说还不够有吸引力。所以我们决定逐步增加更多功能。我们最早做的事情之一是扩展 blob 相关的 API,暴露更多 BLAKE3 验证流式传输的能力。与此同时,我们也通过实现一些高级功能来优化验证流式传输,比如 [BLAKE3 chunk groups](https://github.com/n0-computer/iroh/pull/798)。 但即使加入了 blob 支持,这还不够。我们对资深 Rust 团队来说有非常实用的功能,但当时那并不是一个很大的群体。所以我们决定加入[文档抽象层](https://github.com/n0-computer/iroh/pull/1333),使 iroh 成为一个开箱即用的点对点应用开发框架,理想情况下你只需要添加应用特定的代码。 为了让更多开发者更容易上手 iroh,我们开始发布几乎所有主流语言的语言绑定。这个方向效果不错且被广泛使用,但由于通往 1.0 的过程中 API 经常变动,这对我们来说实际上增加了大量的工作。 随着我们不断添加协议,还需要解决协议*协商*问题。 一种常见的模式是在每条流上添加一个[小型握手协议](https://github.com/multiformats/multistream-select),由一方提出协议,另一方接受或拒绝。但这会给*每一条流*都增加开销,而且让我们离纯粹的点对点 QUIC 越来越远。 我们发现,复杂的协议演进方案往往是海市蜃楼。真正实现向后兼容的协议演进会导致变体的组合爆炸,需要测试的版本太多了。要做到这一点非常困难。 所以我们决定保持简单,使用 [ALPN TLS 扩展](https://www.rfc-editor.org/rfc/rfc7301.html)。连接方提出一个 ALPN 列表,服务端要么选择其中一个 ALPN,要么拒绝握手。这直接在 QUIC 握手过程中完成,除了 ALPN 字符串本身的字节外没有额外开销。 总的来说,我们对这个决定非常满意。像 [Media over QUIC](https://datatracker.ietf.org/group/moq/about/) 这样的项目[以非常有创意的方式](https://moq-wg.github.io/moq-transport/draft-ietf-moq-transport.html#name-data-streams-and-datagrams)大量使用 QUIC 流,因为我们提供的是原生的 QUIC 流,所以可以开箱即用地支持这些方案。 但这也不是完全没有缺点。包含 ALPN 的 `ClientHello` 是未加密的,因此路径上的旁观者可以通过解码 `ClientHello` 看到正在使用哪种协议。一种变通方法是使用一个常见的 ALPN,比如 `h3`,然后在之后再协商实际协议。在未来版本的 iroh 中,我们可能会支持 [Encrypted Client Hello](https://datatracker.ietf.org/doc/draft-ietf-tls-esni/)。 使用 ALPN 的另一个后果是,两个 iroh 端点之间如果使用多种协议通信,将不会只有一条而是*多条*连接(一条 QUIC 连接*只能对应*一个 ALPN)。我们为这些连接共享一些内部状态,这样打洞就不必从零开始。 ### sendme 和 dumbpipe sendme (https://www.iroh.computer/sendme) dumbpipe (https://www.dumbpipe.dev/) 为了展示 iroh 的能力,我们发布了两个小工具:[sendme](https://www.iroh.computer/sendme) 和 [dumbpipe](https://www.dumbpipe.dev/)。它们的目的是展示 iroh 的不同能力,同时也作为独立工具发挥作用。 直到今天,我每天都在用 sendme 发送日志。而且有大量流行的 sendme 克隆版本:[Lightning P2P](https://github.com/Kerim-Sabic/lightning-p2p)、[alt-sendme](https://github.com/tonyantony300/alt-sendme)、[ARK Drop Desktop](https://github.com/ARK-Builders/ARK-Drop-Desktop)、[DataBeam](https://github.com/vinay-winai/DataBeam)、[sendme-egui](https://github.com/zignig/sendme-egui) 以及 [iroh-send](https://github.com/thiswillbeyourgithub/iroh-send)。 Dumbpipe 也启发了一些项目:[pai-sho](https://github.com/cablehead/pai-sho)、[iroh-ssh](https://github.com/rustonbsd/iroh-ssh)、[do-ssh](https://github.com/doEggi/do-ssh) 和 [iroh-ssh-android](https://github.com/futpib/iroh-ssh-android)。但更重要的是,它展示了**仅仅通过端点 ID 建立直连**作为一个基础构建模块有多大的价值。 很长一段时间里,我们没有能力仅通过端点 ID 拨号连接一个 iroh 端点。你当然需要端点 ID,但还需要额外的信息:一个 IP 地址或一个中继(relay)URL。直到今天,sendme 仍然使用这种方式工作。 但额外的信息并不稳定。一个 iroh 端点的公网 IP 地址可能频繁变化,例如当 ISP 分配了一个新的公网 IP 时。当它迁移到另一个地理区域时,它的首选中继也可能改变。此外,对于需要处理大量对等节点的大规模系统来说,使用票据(ticket)是很繁琐的。 在许多点对点项目中,实现这种发现机制的方式是分布式哈希表(DHT)。但我们之前在 DHT 方面的经验有些令人头疼。我们很快意识到,我们没有足够的资源去实现一个 DHT 并*推动它获得采用*。所以就像中继一样,我们在点对点纯粹性上做了一些妥协。我们探索了各种中心化的方案,比如 Cloudflare Workers。 但就在这时,我们发现了一个新兴项目,让我们鱼与熊掌兼得。[Pkarr](https://github.com/pubky/pkarr) 是一个极其简单的想法:将签名的 DNS 记录存储在 [bittorrent mainline DHT](https://en.wikipedia.org/wiki/Mainline_DHT) 上。我们最终在中心化地址查找服务中使用了 pkarr 格式,同时也给了 iroh 用户选择直接使用 [mainline dht](https://docs.rs/iroh-mainline-address-lookup) 的选项,从而获得一个完全去中心化的地址查找系统。 Mainline 有一些不幸的局限性,短期内不太可能改善。所以我[开始编写一个 DHT](https://www.iroh.computer/blog/lets-write-a-dht-1),使用 iroh 的 [0-RTT](https://www.iroh.computer/blog/0rtt-api) 连接。但截至目前,这严格来说只是一个探索 iroh 连接极限的实验,并非生产工具。 ——Rüdiger 在将 iroh 作为开箱即用的工具包开发了一段时间之后,API 的表面面积已经大幅增加。我们无法保证 API 各个部分的质量都能达到同等水平。此外,我们注意到每个用户使用的都是 API 的不同子集。所有人都有共同需求的只有一点:按端点 ID 拨号连接和可靠打洞的连接能力。于是我们[做出了决定](https://www.iroh.computer/blog/smaller-is-better),大幅缩小 iroh 的范围。从今以后,iroh 只做一件事:通过端点 ID 拨号建立直连。其他多个团队依赖的功能被[移到了独立的 crate](https://www.iroh.computer/blog/iroh-0-28-let-them-have-crates) 中。 这时我们处于一个令人羡慕的位置:多个重要项目依赖 iroh。但我们需要自由地演进 iroh,而由于之前在功能分支上开发的不愉快经历,我们不想在功能分支上做这件事。所以我们发布了第一个长期支持版本 [0.35](https://www.iroh.computer/blog/iroh-0-35-prepping-for-1-0),并启动了 [canary 系列](https://www.iroh.computer/blog/iroh-0-90-the-canary-series),供那些想在通往 1.0 的路上体验最前沿功能的人使用。 回顾来看,当时称之为 iroh 1.0 也是可以的。这是一个极其稳定的版本,许多项目至今仍然依赖它。但我们对在 main 分支上开发、让人们随时跟进的决定感到满意。 除了各种功能上的变更,我们还花了不少时间思考命名。例如,iroh 端点的身份标识曾被称为 `NodeId`。在 iroh 还是一个包含多种内置协议的大型二进制文件时,这个名字很有道理。现在被称为"地址查找"(Address Lookup)的功能以前就叫 `Discovery`。但我们发现,"发现"并不是描述为已有端点 ID 查找中继或 IP 地址这一过程的好名字。毕竟你并不是去*发现*端点 ID,你只是查找它的额外数据。 有一些地址查找提供者,比如 mDNS,确实会*发现*新的端点。但这不在 [AddressLookup trait](https://docs.rs/iroh/latest/iroh/address_lookup/trait.AddressLookup.html) 的覆盖范围内。 iroh 在大多数情况下工作正常。但地平线上乌云密布。要让 iroh 的一个主要特性——底层传输的无缝迁移——可靠地工作,我们不得不做一些不太优雅的 hack(https://github.com/n0-computer/iroh/pull/2835)。短期内这不是什么大问题。但我们知道,当前的方法在将来会严重限制我们。 所以我们[做出了决定](https://www.iroh.computer/blog/iroh-on-QUIC-multipath),从根本上重新设计 iroh 底层的工作方式。我们当时已经在

相似文章

n0-computer/iroh

GitHub Trending (daily)

Iroh 是一个用于点对点网络的 Rust 库,它通过公钥简化端点连接,使用打洞技术和 QUIC 并配备后备中继服务器,还提供了诸如 blob、gossip 和 docs 等可组合协议。

Iroh 的全局内容发现

Lobsters Hottest

Iroh 探讨如何为其 P2P 网络协议栈构建无需许可的全局内容发现机制,研究了经过实践验证的 BitTorrent Mainline DHT 作为范本,同时主张其基于 BLAKE3 的 iroh-blobs 已验证流式传输协议优于 BitTorrent 的传输协议。