超越 WASI:BrowserPod 3.0 让任何 Rust 应用在浏览器中运行
摘要
BrowserPod 3.0 已发布,支持在浏览器中执行任何 Rust 应用并提供完整的 Linux 系统调用支持,同时修复了 Node.js 问题并集成了 Python 初步支持,且无需修改代码。
<p><a href="https://lobste.rs/s/fxv1u9/beyond_wasi_running_any_rust_application">评论</a></p>
查看缓存全文
缓存时间: 2026/08/17 14:09
# 突破WASI限制:通过BrowserPod 3.0在浏览器中运行任意Rust应用
来源:https://labs.leaningtech.com/blog/browserpod-rust
今天我们正式发布BrowserPod 3.0,它完整支持在浏览器中运行任意Rust应用程序,修复了多项Node.js问题,并初步实现了Python支持。
我们的Rust支持超越了现有Wasm目标的常规能力,在标准库功能和第三方crate兼容性方面实现了突破。程序可以访问文件系统、发起网络请求、运行子进程,并与并发运行的应用程序交互。所有这些**无需修改一行代码**!
在下方演示中,您可以看到用Rust编写的Yarn 6(https://v6.yarnpkg.com/concepts/yarn-6)预览版安装NPM项目的实际效果:
## 什么是BrowserPod
BrowserPod是一个浏览器内代码沙箱。其核心目标是通过将应用程序编译为WebAssembly并提供完整的Linux系统调用接口,使任何Linux应用都能在现代浏览器中运行。
它提供高效的本地持久化虚拟文件系统、用于下载包或调用API的出站网络访问,以及用于暴露本地开发服务器的入站连接。BrowserPod通过在独立Worker上运行每个线程或进程来实现真正的并行,同时为所有运行程序提供统一的系统视图。从各方面来看,它都可以被视为为Web平台(https://labs.leaningtech.com/blog/browserpod-deep-dive)实现的操作系统内核,完全基于WebAssembly构建。
这套功能集使BrowserPod特别适合用于安全的浏览器内智能体代码执行、基于Web的IDE和开发环境、交互式文档与实时演示、教育平台,以及其他受益于Web应用内沙箱执行的场景。
## 为何现在支持Rust?
BrowserPod的宏伟目标是在浏览器中运行完整的Linux用户空间。这通常意味着需要编译大量C/C++项目。大多数动态语言如Python、JavaScript或Ruby都运行在解释器和运行时之上,而这些运行时通常也由C/C++编写,因此拥有C/C++工具链(我们使用Cheerp(https://cheerp.io/))能解决大部分问题。
但如今情况已大不相同。Rust和Go等流行语言与C/C++一样采用提前编译模式,并拥有自己的工具链。
许多Node.js构建工具正在用Rust编写(或重写)。因此,Rust一直是BrowserPod路线图的重要组成部分。得益于Discord(https://discord.leaningtech.com/)社区成员提出的具体用例,我们决定将Rust支持优先于Python和Ruby。
Yarn包管理器的一位维护者表示有兴趣构建一个交互式文档页面,展示真实的yarn构建过程并在BrowserPod上运行。与之前JavaScript实现的版本不同,即将发布的Yarn 6(https://v6.yarnpkg.com/concepts/yarn-6)采用Rust编写。
这是我们认为BrowserPod能大放异彩的用例之一,因此我们立即投入开发!一周内我们就完成了首个原型,立即看到了其潜力,但实际涉及的复杂程度超出预期。
## 如何"支持"Rust
在BrowserPod的早期版本中,我们专注于Node.js。用户无需担心针对BrowserPod目标编译,因为我们直接提供了预编译的Node.js版本。
Rust程序的行为完全不同,需要使用`rustc`提前编译。目前用户需要离线编译程序并将生成的二进制文件添加到Pod中。有趣的是,`rustc`本身就是用Rust编写的。原则上,我们可以允许用户直接在浏览器中编译Rust程序。我们需要一些额外功能来实现这一目标,但这将在不久的将来实现。
实际上,今天"支持Rust"意味着提供一个能为BrowserPod目标生成Wasm二进制文件的Rust工具链。
## 现有Rust WebAssembly目标
幸运的是,我们可以在坚实基础上起步。Rust已支持多种Wasm目标:
- `wasm32-unknown-unknown`(https://doc.rust-lang.org/rustc/platform-support/wasm32-unknown-unknown.html):这是最基础、最平台无关的目标。它本身不假设运行环境,但借助`wasm-bindgen`(https://wasm-bindgen.github.io/wasm-bindgen/)和`web-sys`(https://docs.rs/web-sys)等crate,可以与JS和Web环境通信。如果您从一开始就有意针对Web平台,这个目标很有意义。但缺少许多标准库功能。
- `wasm32-unknown-emscripten`(https://doc.rust-lang.org/rustc/platform-support/wasm32-unknown-emscripten.html):这个目标适用于面向Web的独立程序。它可以直接与JavaScript代码交互,并已为方便使用封装了许多Web API。它在一定程度上模拟了部分POSIX API,但许多Rust标准库功能仍未实现。通常需要大量重写才能适配此目标。
- `wasm32-wasip1`(https://doc.rust-lang.org/rustc/platform-support/wasm32-wasip1.html)/`wasm32-wasip2`(https://doc.rust-lang.org/rustc/platform-support/wasm32-wasip2.html)/`wasm32-wasip3`(https://doc.rust-lang.org/rustc/platform-support/wasm32-wasip3.html):这些是WASI(https://wasi.dev/)目标。理论上可在任何环境运行,但主要用途是浏览器外的WebAssembly。它们不假设JS环境存在,但为应用提供了文件系统和网络等I/O设施。
- `wasm32-wali-linux-musl`(https://doc.rust-lang.org/rustc/platform-support/wasm32-wali-linux.html):这个目标旨在通过提供x86-64 Linux系统调用接口作为导入,在Wasm运行时中运行现有Linux应用。主要用于浏览器外场景,目前主要是学术项目。
## 为什么不直接采用WASI?
我们最初的计划是在BrowserPod之上实现WASI(WebAssembly系统接口)层。这个方案能让任何针对WASI的程序(无论是Rust、Go、C还是其他语言编写)立即兼容BrowserPod。
但在尝试将现有Rust程序编译为WASI时,我们很快意识到这**不仅仅是**切换编译目标那么简单。
以`yarn`为例。将其编译为WASI需要:
- 平台特定代码:将`std::os::unix`替换为`std::os::wasi`:这可行,但映射并非1:1(例如WASI中没有绝对符号链接)。
- 移除线程使用:`wasm32-wasip3`有部分协作线程支持(无真实并行),但尚处于早期阶段,标准库和tokio(https://tokio.rs/)均未支持。
- 接受功能缩减:`yarn`依赖外部程序实现部分功能:特别是`git`(获取git依赖)和`node`(运行生命周期脚本)是基本操作所需。
BrowserPod已解决这些问题,因此我们决定跳过中间层,实现自己的Rust目标:`wasm32-browserpod-linux-musl`。
## 创建自定义Rust目标
定义自定义Rust目标(https://doc.rust-lang.org/rustc/targets/custom.html)**出乎意料地简单**:只需在.json文件中定义目标属性:
```
{
"llvm-target": "wasm32-unknown-unknown",
"target-pointer-width": 32,
"target-c-int-width": 32,
"data-layout": "e-m:e-p:32:32-p10:8:8-p20:8:8-i64:64-i128:128-n32:64-S128-ni:1:10:20",
"arch": "wasm64",
"is-like-wasm": true,
"target-family": ["unix"],
"os": "linux",
"env": "musl",
"vendor": "browserpod",
"linker-flavor": "wasm-ld",
"linker": "rust-lld",
...
}
```
然后将此文件路径作为`--target`参数传递即可。
需要一些不稳定功能特性,因此必须使用nightly版本的编译器。还需要在Rust标准库中支持我们的架构与系统组合。由于基于x86 Linux建模,我们可以直接借鉴其大部分实现。最后需要覆盖几个基础库:`libc`(需要链接我们自编译的musl libc)和`linux-raw-sys`(选择`libc`回退方案)。
需要若干`cargo`和`rustc`选项来整合一切(不稳定功能标志、包含C依赖的sysroot、`libc`和`linux-raw-sys`的cargo覆盖设置...);我们提供了封装脚本,用户只需简单执行`cargo build --target wasm32-browserpod-linux-musl`即可编译。
## 过程中的技术突破
在理想情况下,这些就是全部需求。不幸的是,我们必须面对第三方依赖的复杂现实。
如前所述,Rust已支持多个Wasm目标。许多库可以针对其中一个或多个目标编译,但通常功能受限,或者假设`arch="wasm32"`意味着"在浏览器主线程运行",以及其他任意约束。
为了实现**无需修改代码**的编译目标,我们必须规避所有会错误将我们目标归类为"功能受限"的条件编译。
仔细观察上面目标定义的json片段,会发现两个有趣之处:
- `target_family: ["unix"]`:最初我们设置为`["unix", "wasm"]`,但许多crate将wasm目标家族解读为"独立Web构建"、"WASI"、"无文件系统"或类似含义。去掉"wasm"似乎**完全没有影响**。
- `arch: "wasm64"`:这更令人惊讶,但遗憾的是同样必要。`arch="wasm32"`常用于检测独立Web目标(例如`reqwest`(https://docs.rs/reqwest)会将整个HTTP客户端替换为`fetch()`实现)。您可能担心这会导致问题,但指针大小等重要信息已单独定义,传递给LLVM后端的实际目标仍是`wasm32`,因此一切正常运行!
当然,我们希望能消除这些技术突破,但真正的解决方案需要生态系统意识到功能完备的Wasm目标的存在,这需要时间(但我们已有计划(https://labs.leaningtech.com/blog/browserpod-rust#future-plans))。
## 收获与成果
最初目标是运行`yarn`,但由于我们的方案完全通用,发现许多程序**开箱即用**。
以下是部分尝试过的流行程序列表(非完整列表),我们预期大多数CLI应用都能正常工作:
| 项目 | Rust代码行数 | 修改状态 | 运行结果 |
|------|-------------|----------|----------|
| burntsushi/ripgrep (https://github.com/burntsushi/ripgrep) | ~40k | **无需修改** | *正常运行* |
| yarnpkg/zpm (https://github.com/yarnpkg/zpm) | ~50k | **无需修改** | *正常运行* |
| starship/starship (https://github.com/starship/starship) | ~50k | **无需修改** | *正常运行* |
| jj-vcs/jj (https://github.com/jj-vcs/jj) | ~200k | **无需修改** | *正常运行* |
| openai/codex (https://github.com/openai/codex) | ~1.25M | 禁用部分沙箱选项 | *正常运行* |
您可以在下方终端中体验:
Codex代码量较大且需要API密钥运行,您也可以观看以下视频了解其实际效果:
或亲自访问https://browsercode.io/agents/codex/尝试。
## 使用方法
Rust拥有单一权威的包管理器和构建系统:`cargo`。与C/C++相比,这使我们能提供更开箱即用的开发者体验。
Rust还有官方的多工具链获取与管理方案:**rustup (https://rustup.rs/)**。目前我们无法通过rustup分发(因为我们不是官方支持目标),但可以安装到其目录结构中,因此您可以按常规方式为单个项目启用我们的工具链:
```
# 手动安装我们的工具链(未来可能通过rustup分发)
curl https://rt.browserpod.io/3.0.0/rust/install.sh | bash
# 使用rustup覆盖当前项目的工具链
rustup override set browserpod-3.0.0
# 构建
cargo build --target wasm32-browserpod-linux-musl
# 通过rustup卸载
rustup toolchain uninstall browserpod-3.0.0
```
## 未来计划
我解释了现有Wasm目标不适用的原因,但暂时略过了`wasm32-wali-linux-musl`。
尽管WALI(WebAssembly Linux Interface)(https://arxiv.org/abs/2312.03858)面向原生Wasm运行时而非浏览器,但其核心目标与BrowserPod相同:将Linux应用**未经修改**编译为Wasm。主要区别在于BrowserPod针对x86版本的Linux系统调用,而WALI针对x86-64版本。BrowserPod的选择是因为其实现的系统调用与CheerpX(https://cheerpx.io/)共享——这是一个x86 Linux二进制文件虚拟机(您可能通过WebVM(https://webvm.io/)了解过它)。
CheerpX正在推进对x86-64二进制文件的支持,因此未来我们可以直接采用WALI作为目标(适用于Rust和C/C++)。
这将带来多重益处:
- 已是官方支持的Rust目标
- 可在浏览器内外运行相同的Wasm二进制文件
- 可与上游协作解决第三方库问题
我们对这一发展方向充满期待,将在未来分享更多信息!
## 立即体验
访问console.browserpod.io(https://console.browserpod.io/)获取API密钥。详细文档请查阅browserpod.io/docs(https://browserpod.io/docs)。
如果您有希望在浏览器标签页中运行的Rust项目(特别是预计会出现问题的项目),我们非常乐意倾听您的需求。欢迎加入Discord(https://discord.leaningtech.com/)与我们联系。
相似文章
浏览器标签中的类Linux内核 - 深入解析BrowserPod架构
深入解析BrowserPod架构,这是一个基于WebAssembly内核的浏览器内沙箱,完全在客户端运行兼容Linux的应用程序。本文涵盖内核设计、磁盘和网络子系统,以及其在浏览器中运行诸如Claude Code等工具的能力。
weblings:在 WASM 内部将 Rust 编译为 WASM
Weblings 是一个编译为 WebAssembly 的 Rust 编译器工具链,通过带有 playground 和 Rustlings 练习的网页 UI,支持在浏览器中直接编译和执行 Rust 代码。
在浏览器代码运行器中添加Go语言
作者详细介绍了在基于浏览器的代码运行器(dailyprog)中添加Go语言所面临的挑战及解决方案,解释了为什么标准的GOOS=js方法无法在V8隔离环境中工作,以及如何通过使用GOOS=wasip1和最小的WASI主机shim来成功实现。
@browser_use: 我们发布了一个用 Rust 编写的终端界面和工具集 > 无框架 > 浏览器工具集的全部能力 > 连接任何 LLM …
Browser Use 发布了一个用 Rust 编写的终端界面和工具集,提供直接的 CDP 浏览器控制和连接任何 LLM 的能力,用于自动化浏览器任务。
@rauchg:我将迷你浏览器移植到了 Rust 和 Swift。它更快、更安全,而且令人惊讶的是,迭代开发体验比 Elect… 更好
一位开发者将迷你浏览器移植到 Rust 和 Swift,以提升速度和安全性,并引入了 'fx',一个微型的原生编码代理工具。