tech官小西

Iroh 1.0 深度解析:拨号公钥,而非 IP 地址

自互联网诞生以来,连接两台设备就意味着要知道它们在哪里。你需要一个 IP 地址,而且这个地址必须可达。但 IP 地址是一个会漏水的抽象:当你的手机从 WiFi 切到蜂窝网络时它会变,它藏在 NAT 和防火墙之后,而且它是由一个你无法掌控的网络分配给你的。设备一旦移动,地址就失效了——而你对此无能为力。

2026 年 6 月 15 日,一家名为 n0 的小公司发布了 iroh 的 1.0 版本。这个网络库建立在一个看似简单、却彻底颠覆上述模型的想法之上:拨号公钥,而非 IP(dial keys, not IPs)。 你不再追问"那台设备的地址是什么",而是直接说"帮我连到那台设备"——用它的公钥来标识——然后 iroh 会替你找到并维持一条最快的连接,无论它身处何方。本文是一篇技术深度解析,讲清楚它是怎么做到的、1.0 带来了什么,以及为什么这件事值得关注。


核心理念:公钥是比 IP 更好的地址

这个理念一句话就能说清:IP 地址会失效,那就改用公钥拨号。 但它的后果远比一句话深刻。

公钥拥有 IP 地址永远不会有的属性。它由你创建、由你掌控——没有任何 ISP 或云厂商把它分配给你。当你的设备在不同网络间漫游时,它始终不变。你用完可以扔掉,也可以永久保留。而最关键的一点是:那个用来命名设备的公钥,同时也能加密通往该设备的连接。一旦线路上的每一个字节都与这把公钥绑定,你就能在同一个原语之上构建出身份、权限和归属。用 n0 的话说,按公钥拨号"把互联网变成了一个安全的 localhost"。

最后这句话就是整个论点的灵魂。在 localhost 上,连接天然就能用——它们是直连的、瞬时的,你根本不用去想路由器或地址。iroh 想做的,就是让地球上任意两台设备感觉像是在同一台机器上。

这不是一个研究原型。1.0 公告里的数字值得细品:n0 运营的公共中继,仅在过去 30 天内就见证了超过 2 亿个端点(endpoint)被创建,iroh "如今正运行在数百万台设备上"。从开源起步到这个首个稳定版本,它经历了四年多、65 个版本的公开迭代。


一条连接究竟是怎么建立的

"按公钥拨号"这个优雅抽象的底下,藏着一个货真价实的网络难题:当两台设备都深埋在 NAT 和防火墙之后时,你如何在它们之间建立一条直连?iroh 的答案是一套分层策略——永远优先走快速路径,走不通时则优雅降级。

Iroh 连接建立流程:先按公钥发现地址,再尝试打洞建立直连,打洞失败时回落到中继

1. 发现(Discovery)。 在 iroh 能连上一把公钥之前,它得先为这把公钥找到一个候选地址。它通过一套基于 DNS 的系统,把公钥(一个 EndpointId)解析成网络位置——n0 正是为此在 dns.iroh.link 上运行了一台 Pkarr/DNS 服务器——不过发现机制是可插拔的,对于同一局域网内的设备,它也能走本地 mDNS。

2. 打洞建立直连。 最快的路径永远是设备到设备的直连,所以 iroh 会尝试在双方的 NAT 上打洞(hole-punch)。一旦成功——而且成功率高得惊人——流量就完全不经过任何服务器。n0 报告说,一条连接中 95% 的数据走设备间直连是常态。 更少地绕经云端,意味着你的流量出口(egress)账单更低;更少地绕经路由器,意味着整个互联网的效率都更高。

3. 中继回落(Relay fallback)。 有些网络拓扑实在太恶劣,根本无法打洞穿透。当直连建立不起来时,iroh 会回落到一个开放的公共中继服务器生态,由它们转发加密后的流量。中继绝不是单点故障,也不是隐私漏洞——它只能看到密文——而且一旦后续出现可用的直连路径,连接会透明地升级过去。中继的二进制程序是开源的,所以你可以自建;n0 也提供托管中继作为付费服务。

最终得到的,是一条开箱即用的连接:默认加密,物理上可能就直连,做不到时也依然有韧性。


构建于 QUIC 之上——而且是 n0 自研的 QUIC

在底层,iroh 说的是 QUIC——也就是支撑 HTTP/3 的那个传输协议。这是一个深思熟虑、影响深远的选择。QUIC 为 iroh 带来了认证加密、带优先级的多路并发流、数据报传输,以及没有队头阻塞(head-of-line blocking)的特性——这一切都是标准能力,没有一项是后来硬塞进去的。

更不寻常的是,iroh 1.0 跑在 noq 之上——这是 n0 用 Rust 编写的自己的 QUIC 实现。自研一套 QUIC 栈不是谁都会轻易做的决定,但正是它解锁了让 iroh 连接模型得以成立的两个关键特性:

  • QUIC 多路径(multipath)。 单条 iroh 连接可以同时构建并管理多条网络路径,并随网络状况变化在它们之间热切换。你的手机从 WiFi 切到蜂窝时连接不会断——它在底层迁移了路径。正是这套机制,让"公钥即地址"在网络变化时依然真正可靠。
  • QUIC NAT 穿透。 iroh 把 NAT 穿透实现在 QUIC 层本身,因此它能在建立直连的同时让连接细节保持加密。打洞不是一个旁路侧信道,而是协议的一部分。

在标准这件事上 n0 也很克制,尽可能采用 IETF 的草案而非发明私有扩展——多路径和 NAT 穿透都对应着进行中的 IETF 工作。对于一个要你把应用网络层押注在它身上的项目来说,这一点很重要。


"1.0" 到底保证了什么

很多项目只是随手把版本号标成 1.0。但对一个网络库而言,真正要紧的版本,是两个陌生人的设备必须就此达成一致的那个版本。iroh 1.0 在这里给出了一个具体的、承重的承诺:

一个 iroh v1 端点能与任何其他 iroh v1 端点通信——无论次版本号或编程语言为何。

这就是线协议稳定性(wire stability),也是 1.0 版本里最重要的一件事。它意味着你今天可以发布一个应用,而将来某台跑着 Python 版 iroh v1.5 的设备,依然能和一台跑着 Rust 版 iroh v1.0 的设备对话。n0 承诺,任何破坏线协议兼容性的改动,都只会发生在主版本号的跃迁中。 他们甚至为"语言 API 版本"和"线协议版本"各自独立演进留了余地——Rust API 出到 v2,线上协议仍可以是 v1。

支撑这一承诺的,是一份公开的支持政策:主版本和次版本按计划提供支持;公共中继会保持更新(通常在发布后 24 小时内);而会破坏线协议的中继改动会启用全新的 URL,让旧客户端继续可用。对任何曾被某个网络依赖在线上偷偷改协议而坑过的人来说,这部分公告才是真正赢得信任的地方。


头条特性:官方支持 Python、Node.js、Swift 和 Kotlin

在它生命中的大部分时间里,iroh 从 Rust 里用起来最顺手;而早先一次外部函数接口(FFI)绑定的尝试被暂停了——因为在一个尚未稳定、频繁变动的 1.0 前 API 上保持各语言绑定同步,维护成本太高。n0 当时承诺,等 API 稳定下来就把 FFI 带回来——而 1.0 正是他们兑现承诺的时刻。

iroh 1.0 在核心 Rust crate 之外,正式提供了 Python、Node.js、Swift 和 Kotlin 的官方绑定。其现实影响相当大:你现在可以把 iroh 直接嵌进 Swift 写的 iOS 应用、Kotlin 写的 Android 应用、Node 写的后端服务,或是 Python 写的数据管道——它们说着同一套线协议,都能彼此按公钥拨号。"按公钥拨号"不再是 Rust 专属的超能力,而是来到了大多数应用真正栖身的那些平台上。


组合现成协议,而非自己造轮子

iroh 的网络内核给你一条安全、可寻址的连接。但大多数应用需要的不止一根裸管道——它们要传文件、要同步状态、要广播事件。iroh 没有让你从零造这些,而是提供了一个现成协议的生态,它们都构建在同一套连接抽象之上、可自由组合:

  • iroh-blobs——基于 BLAKE3 哈希的内容寻址 blob 传输,从几 KB 一路扩展到 TB 级。因为内容是按哈希寻址的,传输是可验证、可断点续传的:你永远确信自己拿到的,正是你要的那串字节。
  • iroh-gossip——发布订阅(pub-sub)的覆盖网络,资源占用可以小到一台普通手机也扛得住。这是你在一大群设备间按主题广播事件、又无需中心 broker 的方式。
  • iroh-docs——一个构建在 iroh-blobs 之上的最终一致键值存储,用于多设备状态同步。

你可以直接引入一个现成协议,也可以在干净的流抽象之上实现自己的 ProtocolHandler。这种可组合性,正是 iroh 能出现在如此天差地别的各类应用里的重要原因。


它写成代码长什么样

下面是 iroh 最经典的示例——一个 echo(回声)协议,直接取自官方仓库。发起方拨号一个地址和一个 ALPN(应用层协议标识符),打开一条双向 QUIC 流,然后发送一些字节:

const ALPN: &[u8] = b"iroh-example/echo/0";

let endpoint = Endpoint::bind().await?;

// 向接受方端点发起一条连接
let conn = endpoint.connect(addr, ALPN).await?;

// 打开一条双向 QUIC 流
let (mut send, mut recv) = conn.open_bi().await?;

// 发送一些待回显的数据
send.write_all(b"Hello, world!").await?;
send.finish()?;

// 接收回显
let response = recv.read_to_end(1000).await?;
assert_eq!(&response, b"Hello, world!");

// 作为收到最后一段应用数据的一方——道别
conn.close(0u32.into(), b"bye!");

// 关闭端点及其所有连接
endpoint.close().await;

接受方则绑定一个端点,为该 ALPN 注册一个处理器,把收到的任何字节原样沿同一条流回传:

let endpoint = Endpoint::bind().await?;

let router = Router::builder(endpoint)
    .accept(ALPN.to_vec(), Arc::new(Echo))
    .spawn()
    .await?;

// 协议定义:
#[derive(Debug, Clone)]
struct Echo;

impl ProtocolHandler for Echo {
    async fn accept(&self, connection: Connection) -> Result<()> {
        let (mut send, mut recv) = connection.accept_bi().await?;

        // 把收到的任何字节直接回显
        let bytes_sent = tokio::io::copy(&mut recv, &mut send).await?;

        send.finish()?;
        connection.closed().await;

        Ok(())
    }
}

注意这里没有什么:没有 STUN 服务器配置,没有 TURN 凭据,没有 ICE 候选地址收集,没有证书管理。EndpointRouter 这两个抽象,把前面描述的那整套"NAT 穿透加回落"的舞蹈全部吸收了进去。你只需用"输入字节、输出字节"的行为来定义一个协议,剩下的——如何在两台可能身处地球两端、躲在恶劣 NAT 后面的机器之间把这些字节送达——交给 iroh。


iroh 已经跑在哪些地方

为一个网络栈背书最有说服力的论据,就是人们用它造出了多少不同的东西。根据 n0 自己的盘点和公开案例,iroh 已经在一系列惊人多样的领域投入生产:

  • 分布式 AI 训练与智能体。 开发者用 iroh 跨机器训练大语言模型,也用它让智能体彼此对话——设备到设备的直连,避免了把每一份梯度或每一条消息都绕经云端中继。
  • 实时视频流。 iroh 结合 Media-over-QUIC 被用于低延迟视频,n0 甚至为此写了一篇标题相当犀利的博客《要是你的安防摄像头真的安全呢?》。
  • 边缘支付。 Paycode 用 iroh 把高速公路收费站的支付终端直接连到 POS 系统——无需任何额外服务器、且完全满足 PCI 合规,因为连接本身就是直连且端到端加密的。
  • 嵌入式与单片机。 iroh 已被演示运行在一块 ESP32 单片机上——同一套"按公钥拨号"的栈,跑在一块只要几美元的芯片上。
  • 匿名连接。 因为 iroh 支持自定义传输,你可以把 iroh 连接路由经过 Tor,把洋葱服务的隐私性和 iroh 的性能结合起来。
  • 安全聊天、文件同步与游戏。 直连、加密、与地址无关的连接,天然适配即时通讯、点对点文件传输和多人游戏。

广度本身就是重点。一个收费站、一台安防摄像头、一块 3 美元的单片机,和一次分布式 LLM 训练——它们在"网络应用"这个谱系上几乎相隔最远,却都归结为同一个原语:拨号一把公钥,得到一条安全连接。


自定义传输:超越互联网

1.0 里一个更安静、却更激进的能力是自定义传输(custom transports)。 iroh"按公钥拨号"的抽象,其实并不要求互联网的存在。n0 构建了一套钩子,让你能插入别的搬运字节的方式——蓝牙低功耗(BLE)WiFi Aware、LoRa(开发中),甚至 Tor——而它们全都归在同一套"按公钥拨号"的 API 之下。借助 iroh 的本地优先(local-first)配置,一台设备在完全没有互联网接入的情况下,也能发现并连上一个本地对端。

这正是让"安全 localhost"不只是一句口号的东西。无论两台设备是同处一室通过蓝牙相连,还是隔着大洲通过公网相连,应用代码都完全一样:你拨号一把公钥,得到一条连接。底下的传输方式,变成了一个实现细节。


仓库是怎么组织的

对于想读源码的人,主 iroh 仓库是一个由若干职责清晰的 crate 组成的 Rust workspace:

  • iroh——负责打洞和与中继通信的核心库。
  • iroh-relay——中继的客户端与服务端实现。这正是 n0 在生产中为公共中继运行的那份代码,你也可以拿去自己跑。
  • iroh-base——EndpointIdRelayUrl 之类的公共类型。
  • iroh-dns-server——为 EndpointId 提供 Pkarr/DNS 地址查询的 DNS 服务器,在生产环境的 dns.iroh.link 上运行。

可组合协议(iroh-blobsiroh-gossipiroh-docs)、QUIC 实现(noq)和 FFI 绑定(iroh-ffi),各自有独立仓库,都在同一个 n0-computer 组织之下。整个栈采用 Apache-2.0 与 MIT 双许可。


评析:一个真实的、已经发货的地基

对"互联网的未来"这种说辞抱以怀疑很容易——点对点这个领域里,躺满了雄心勃勃却从未抵达生产的项目。iroh 的不同之处在于:生产使用是发生的,1.0 才在其后到来——每月 2 亿个端点、数百万台设备、四年的版本迭代,然后才有人宣称稳定。

1.0 这个版本,与其说是关于新特性,不如说是关于一个承诺:一套稳定的线协议、一套跨五种语言稳定的 API,以及一份你可以据以规划的支持时间表。对一个网络库来说,这个承诺就是产品本身。如果你曾经和 STUN/TURN 服务器搏斗过、眼睁睁看着一条 WebRTC 连接在企业 NAT 后莫名其妙地失败、或为了中继那些本该直连的流量而付过云出口账单,那么 iroh 的主张会精准击中你:别再用设备"碰巧在哪"来给它寻址,开始用它"是谁"来给它寻址。

用 n0 的话说,动手构建的时机,就是现在。


资料来源

iroh(核心):

可组合协议与依赖:

引用的案例研究(n0 博客):

  • 《iroh for payments》(Paycode 收费站终端),2026 年 3 月 26 日
  • 《Running iroh on an ESP32》,2026 年 3 月 24 日
  • 《Use iroh with Tor for anonymous connections》,2026 年 1 月 27 日
  • 《What if your security camera was secure?》,2026 年 3 月 11 日