超越 WASI:BrowserPod 3.0 让任何 Rust 应用在浏览器中运行

Lobsters Hottest 产品

摘要

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架构

Lobsters Hottest

深入解析BrowserPod架构,这是一个基于WebAssembly内核的浏览器内沙箱,完全在客户端运行兼容Linux的应用程序。本文涵盖内核设计、磁盘和网络子系统,以及其在浏览器中运行诸如Claude Code等工具的能力。

weblings:在 WASM 内部将 Rust 编译为 WASM

Lobsters Hottest

Weblings 是一个编译为 WebAssembly 的 Rust 编译器工具链,通过带有 playground 和 Rustlings 练习的网页 UI,支持在浏览器中直接编译和执行 Rust 代码。

在浏览器代码运行器中添加Go语言

Lobsters Hottest

作者详细介绍了在基于浏览器的代码运行器(dailyprog)中添加Go语言所面临的挑战及解决方案,解释了为什么标准的GOOS=js方法无法在V8隔离环境中工作,以及如何通过使用GOOS=wasip1和最小的WASI主机shim来成功实现。