跳到主要内容

总览:E2B 是什么、两平面全景与阅读地图

30 秒导读: E2B 是一套开源基础设施,让 AI(或你)生成的、不可信的代码在云端安全隔离的沙箱里运行。你几乎不直接碰基础设施——而是用它的 SDK(JS / Python)当客户端:Sandbox.create() 起一个沙箱,然后 sandbox.commands.run(...)sandbox.files.read(...) 就像操作一台自己的云端 Linux。核心结论只有一句:SDK 内部分成两条平面,一条 REST「控制面」找 E2B 编排器管沙箱的生老病死,一条直连沙箱内 envd 守护进程的「数据面」管沙箱里面的活(命令、文件);两条平面的协议全部由 spec/ 目录代码生成,所以 JS、Python、CLI 三份实现长得一模一样。本篇是这套讲解的门厅,只做导航与全景,实现细节留给各章。


1. 这是什么(零基础也能懂)

一句话定义: E2B 是"给 AI agent 的云端一次性电脑"——把一段来历不明的代码放进一个开箱即焚的隔离沙箱里跑,跑完可以随手扔掉,炸了也伤不到你。

官方的自我定位很直白(README.md:19-20,"What is E2B"):

"an open-source infrastructure that allows you to run AI-generated code in secure isolated sandboxes in the cloud."

而 SDK 的角色,packages/js-sdk/package.jsondescription 一句点破:"E2B SDK that give agents cloud environments"——SDK 就是递给 agent 一台云环境的那只手

解决什么问题 / 给谁用

设想你在做一个 AI 编码助手:模型吐出一段 Python,你总不能直接在自己服务器上 exec——它可能 rm -rf、可能装恶意包、可能死循环吃满 CPU。你需要一个用完即弃、跑飞了也不心疼的地方去执行它。E2B 就是这个地方。

典型用户:

  • AI 编码 / code-interpreter 类产品——把 LLM 生成的代码丢进沙箱执行,取回 stdout / 图表 / 文件。
  • agent 框架——给 agent 一双"手":跑命令、读写文件、开网页服务、做 git 操作。
  • 数据 / 自动化流水线——需要一个隔离、可复现、可秒起秒销的 Linux 执行环境。

它能做什么

沙箱内你能做的事(packages/js-sdk/src/sandbox/index.ts:52-63 类注释):

  • 访问一台 Linux OS
  • 增删查文件与目录
  • 运行命令(前台 / 后台 / 流式)
  • 跑 git 操作
  • 跑隔离的代码
  • 访问互联网(可按域名放行/拦截/改写)

用起来什么样

一个最小的真实示例(README.md:47-53)——三行就摸到全部直觉,注意 create() 走的是控制面commands.run() 走的是数据面(这条分界线是全篇主线):

import Sandbox from 'e2b'

const sandbox = await Sandbox.create() // 控制面:请编排器造一台沙箱
const result = await sandbox.commands.run('echo "Hello from E2B!"') // 数据面:直连沙箱内 envd 跑命令
console.log(result.stdout) // Hello from E2B!

Python 版几乎一模一样(README.md:57-62),并且用 with 语法自动回收沙箱:

from e2b import Sandbox

with Sandbox.create() as sandbox:
result = sandbox.commands.run('echo "Hello from E2B!"')
print(result.stdout) # Hello from E2B!

sandbox 这个对象上挂着四个模块:files(文件)、commands(命令)、pty(伪终端)、git(git 操作)。你对沙箱做的一切,都是调它们(packages/js-sdk/src/sandbox/index.ts:82-94)。

一句话直觉 / 类比

把 E2B 当成「云函数版的 Docker,但带一个可以远程遥控内部的机器人」:控制面像 docker run / stop / commit(管容器本身),数据面像 docker exec(钻进容器里干活)——只不过这两件事走的是两条完全不同的网络链路和协议create() 像是"租一台机器并拿到它的门牌号",之后所有操作都是通过网络遥控那台机器


2. 顶层全景(它大概怎么转)

理解 E2B SDK 的唯一关键,是看懂它把工作切成了两条独立的网络平面。搞混这两条,后面每一章都会读得别扭;分清了,一切豁然开朗。

两条平面

平面连到哪用什么协议管什么对应章
控制面(control plane)编排器 https://api.<domain>REST(OpenAPI 生成的 client)沙箱生命周期与编排:create / kill / pause / list / 模板构建01 · 05
数据面(data plane)沙箱内部envd 守护进程,直连 49983-<sandboxId>.<domain>Connect-RPC(命令/PTY/文件元数据)+ HTTP(文件字节流)沙箱里面的实际操作:跑命令、读写文件、开 PTY02 · 03 · 04

一句话记法:控制面管"这台沙箱的生死",数据面管"沙箱里发生什么"。 控制面对着 E2B 的编排器说话;数据面绕过编排器,直接敲沙箱里那个叫 envd("env daemon",沙箱内的执行代理守护进程)的东西。

全景图

怎么读这张图:从上到下是一次典型使用的时间顺序;左边是控制面(对编排器),右边是数据面(对沙箱内的 envd)。

你的代码 / AI agent

await Sandbox.create()

┌─────────────────┴──────────────────┐
│ ① 控制面:POST api.<domain>/sandboxes │ ← REST,向编排器要一台沙箱
│ 拿回 sandboxId + envdVersion + token │
└─────────────────┬──────────────────┘
│ ② 构造 Sandbox 对象
│ 装配四个数据面模块

┌───────────────────────┐
│ Sandbox 实例 │
│ .files .commands │
│ .pty .git │
└───────────┬───────────┘
│ ③ 数据面:直连沙箱里的 envd

49983-<sandboxId>.<domain> (envd 守护进程)
├─ Connect-RPC ── 跑命令 / PTY / 文件元数据
└─ HTTP /files ── 上传下载文件字节

部件一句话职责

部件干什么在哪
Sandbox(类)门面对象:静态 create/connect 起沙箱,实例上挂四个数据面模块packages/js-sdk/src/sandbox/index.ts:74
SandboxApi(父类)控制面的全部 REST 调用(createSandbox / kill / pause / list…)packages/js-sdk/src/sandbox/sandboxApi.ts
ConnectionConfig解析域名 / API key / debug,推导所有 URL(api 地址与沙箱主机名)packages/js-sdk/src/connectionConfig.ts
envd(沙箱内守护进程)数据面的服务端:在沙箱里真正执行命令 / 文件 / PTY沙箱镜像内(spec 定义其协议,见 06
spec/单一事实源:OpenAPI + protobuf,代码生成出各 SDK 的 clientspec/openapi.ymlspec/envd/

主线走一遍(高层,不进代码)

跟着一次 Sandbox.create(),把两条平面串起来:

  1. 控制面要一台沙箱。 Sandbox.create() 走到 SandboxApi.createSandbox(),向编排器 POST /sandboxes,请求体带上模板 ID、超时、网络策略等(packages/js-sdk/src/sandbox/sandboxApi.ts:1180)。
  2. 拿回身份与凭证。 响应给出 sandboxIdenvdVersion(沙箱里 envd 的版本,用来做能力协商)、以及可选的 envdAccessToken / trafficAccessToken(数据面鉴权用)——见 sandboxApi.ts:1197-1203
  3. 构造 Sandbox 对象、装配数据面。 new this({...sandboxInfo, ...config}) 进到 Sandbox 构造函数(packages/js-sdk/src/sandbox/index.ts:129-227):它用 ConnectionConfig 推导出 envd 的直连 URL,建好 RPC transport,然后 new Filesystem/Commands/Pty/Git(...) 把四个数据面模块挂上去(index.ts:215-226)。
  4. 此后全在数据面。 你调 sandbox.commands.run(...)sandbox.files.write(...),SDK 都是直连沙箱主机 49983-<sandboxId>.<domain> 上的 envd,编排器不再介入。超时到点或你显式 kill(),才又回到控制面 DELETE /sandboxes/{id},microVM 被销毁。

这条主线里,create()唯一横跨两条平面的地方:它先在控制面拿到沙箱身份,再用这身份布线到数据面。看懂这一跳,就看懂了整个 SDK 的骨架。记住这条分界线:凡是关于「沙箱这台机器本身」的动作走控制面 REST;凡是「机器里面」的动作走数据面 RPC/HTTP。

两个关键常量与 URL 推导(记住即可,细节在 02 章)

  • 数据面端口写死在类里:envdPort = 49983mcpPort = 50005packages/js-sdk/src/sandbox/index.ts:111-112)。
  • 沙箱主机名的推导规则是一条简单模板 `${port}-${sandboxId}.${sandboxDomain}`packages/js-sdk/src/connectionConfig.ts:456getHost)——所以数据面 URL 长成 49983-<sandboxId>.<domain>
  • 控制面 API 地址则是 `https://api.${this.domain}`packages/js-sdk/src/connectionConfig.ts:372-375)。

这三行就是"两条平面各连去哪"的全部秘密。02 章会讲透 URL 推导、sandbox.<domain> 的稳定主机优化、以及签名鉴权。


3. 仓库全景(monorepo 布局)

E2B 是一个 pnpm monorepo。工作区定义极简(pnpm-workspace.yaml):

packages:
- packages/*
- spec

package.json 是私有聚合包("name": "e2b", "private": true),只放跨包脚本(version / publish / test / lint)和 pnpm 版本锁(packageManager: pnpm@9.15.5)——它本身不发布,真正的产物在各子包里。

顶层代码地图

目录是什么备注
packages/js-sdk/JavaScript / TypeScript SDK(npm 包 e2b,本讲解主要剖析对象)版本 2.31.0,description: "E2B SDK that give agents cloud environments"
packages/python-sdk/Python SDK(PyPI 包 e2b),同 description与 JS 保持功能等价,含 sync + async 两套实现
packages/cli/命令行工具"CLI for managing e2b sandbox templates"主要面向模板构建,见 05 章
packages/connect-python/Go 实现的 connect(Python 侧数据面 RPC 桥接相关)go.mod / cmd/,跨语言一致性的一环
spec/单一事实源:OpenAPI(openapi.yml)+ envd protobuf(spec/envd/)+ MCP schema代码生成 client 的输入,见 06 章

为什么 spec/ 单独列进 workspace?因为它是所有 SDK 的共同上游:改一次 spec,make codegen 就能把 JS / Python 的 client 一起重生成。这保证了多语言 SDK 行为一致——这正是 06 章的主题。

JS SDK 内部结构速览

packages/js-sdk/src/ 的分层与本讲解的章节几乎一一对应:

源码位置职责对应章
sandbox/index.tsSandbox 门面类(create/connect/构造装配)本篇 + 01/02
sandbox/sandboxApi.ts控制面 REST 全家桶01
connectionConfig.ts配置解析 + URL 推导02
sandbox/commands/命令执行 + PTY + CommandHandle03
sandbox/filesystem/文件读写 + 目录 watch04
sandbox/git/git 操作(基于 commands 之上的薄封装,index.ts:22603
template/模板 DSL 与构建05
api/envd/由 spec 生成的 client(REST client + envd RPC/HTTP)06

packages/js-sdk/src/index.ts 是这些的公共出口:它 re-export Sandbox(默认导出)、四个模块的类型、以及错误类型与 template 全套(packages/js-sdk/src/index.ts:114-152)。想知道"用户能拿到哪些 API",看这个 barrel 文件即可。


4. 阅读地图(各章顺序与精华)

建议按编号顺序读——它就是由浅入深、先控制面再数据面排的。若你带着具体问题来,用下表直接跳章。

顺序一句话讲什么精华(读完带走什么)
0index.md(本篇)E2B 是什么、两平面全景、仓库地图建立"控制面 vs 数据面"的心智模型
101-control-plane.md沙箱生命周期与编排器 RESTcreate/kill/pause/list 如何映射到 POST /sandboxes 等;快照两档语义、分页器、超时、网络策略
202-data-plane-connection.md连到沙箱里的 envdURL 推导(49983-<id>.<domain>)、签名/token 鉴权、握手 vs 空闲双超时、流的生命周期
303-commands-pty.md命令执行与 PTY流式 Connect-RPC、CommandHandle 把流拆成 stdout/stderr/exit、背压与取消、伪终端
404-filesystem.md文件系统为什么元数据走 RPC、字节走 HTTP /files;目录 watch 事件流;签名直传 URL
505-templates.md模板构建Dockerfile 式 DSL → 可秒起镜像;按层内容 hash 去重上传;CLI 的角色
606-spec-and-multilang.md一份 spec 多个 SDKOpenAPI/protobuf 代码生成如何保证 JS/Python 一致

推荐路线:

  • 只想会用 SDK → 本篇 §1–§2 + 03 + 04 足矣。
  • 想理解生命周期 / 自建后端 → 本篇 + 01 + 02
  • 想改 SDK / 加语言 → 全读,重点 06(代码生成是改动的起点)。

给 AI agent 的选章提示: 任务涉及「造/停/恢复/快照沙箱」→ 读 01;「连不上沙箱 / URL / 鉴权 / 超时」→ 读 02;「跑命令 / 流输出 / 终端」→ 读 03;「读写文件 / 监听」→ 读 04;「自定义镜像」→ 读 05;「改协议 / 加字段 / 跨语言」→ 读 06。


5. 巧妙之处(值得带走的设计)

这一节把 E2B SDK 里几个「不显然但很聪明」的决定提炼出来,作为读各章前的路标;细节在对应章节展开。

  • ① 两平面分离 = 编排器不做数据代理。 命令输出、文件字节这些高频大流量不经过编排器,而是客户端直连沙箱 host。编排器只做「调度 + 生命周期」这种低频控制,天然可横向扩展。分界点就在 Sandbox 构造函数:控制面结果一到手,立刻用它建数据面 transport(sandbox/index.ts:166-198)。详见 02
  • ② URL 靠约定推导,不靠查询。 连数据面不需要再问编排器要地址——getHost() 直接把端口和 sandboxID 拼成 ${port}-${sandboxId}.${domain}connectionConfig.ts:451)。任意端口都能这样对外暴露(sandbox.getHost(3000) 就能拿到沙箱里 3000 端口的公网地址),零额外往返。详见 02
  • ③ 一条 RPC 流承载「启动 + 输出 + 退出码」。 Process.Start 是 server-streaming:第一帧是「进程已启动、这是 pid」,中间帧是 stdout/stderr,末帧是 exit code。SDK 只 await 到第一帧就返回句柄,既拿到 pid 又保留后续流——一个连接干完握手和数据两件事。详见 03
  • ④ 把「连接断了」翻译成「沙箱是不是死了」。 长流中途断开,不同 JS 运行时(Node/Bun/Deno)报的错文案都不一样。SDK 匹配这些运行时特有文案识别出「连接被拉断」后主动探一次沙箱健康envd/rpc.tshandleRpcErrorWithHealthCheck),据此把裸网络错翻成语义明确的 TimeoutError——告诉你是沙箱寿终正寝,而非临时抖动。详见 02
  • ⑤ 文件字节不塞进 RPC。 文件系统的元数据操作(stat/mkdir/list/move/remove/watch)走 protobuf RPC,但读写字节走独立的 /files HTTP 端点。大文件因此能流式上传下载、能 gzip、能靠预签名 URL 直传,不必被 protobuf 消息边界束缚。详见 04
  • ⑥ 协议单一事实源,三份 SDK 零漂移。 JS、Python、CLI 不各写各的客户端——控制面从 spec/openapi.yml、数据面从 spec/envd/**/*.proto 代码生成。改协议只改 spec/make codegen 一跑,三端同步。跨语言行为一致是「生成」保证的,不是「纪律」保证的。详见 06
  • ⑦ 快照的两档语义被类型系统钉死。 pause({keepMemory}) 分「全内存快照」和「仅文件系统快照」;后者 resume 时是冷启动,因此不能autoResume。SDK 把这个约束做成判别联合类型 + 运行时再校验,非法组合在编译期就报错。详见 01

6. 代码地图(导航索引)

一张表跳进源码。优先用符号名 grep 定位(行号会随上游漂移,符号名通常还在);file:line as-of commit 2869feb

主题文件路径符号名
门面类 / 四个数据面模块挂载packages/js-sdk/src/sandbox/index.ts:74class Sandboxfiles/commands/pty/git:82-94
构造函数:URL 推导 + transport + 装配模块packages/js-sdk/src/sandbox/index.ts:129-227Sandbox constructor
起沙箱(横跨两平面的入口)packages/js-sdk/src/sandbox/index.ts:277-331Sandbox.create
重连已有沙箱packages/js-sdk/src/sandbox/index.ts:353-377Sandbox.connect
数据面端口常量packages/js-sdk/src/sandbox/index.ts:111-112envdPort = 49983mcpPort = 50005
控制面 create(REST)packages/js-sdk/src/sandbox/sandboxApi.ts:1119createSandboxPOST /sandboxes @ :1180,返回体 @ :1197-1203
沙箱主机名推导packages/js-sdk/src/connectionConfig.ts:451getHost(模板 ${port}-${sandboxId}.${sandboxDomain} @ :456
控制面 API 地址推导packages/js-sdk/src/connectionConfig.ts:372-375ConnectionConfigapiUrl
数据面 RPC transport 装配packages/js-sdk/src/sandbox/index.ts:166-198createConnectTransport(在构造函数内)
公共 API 出口(barrel)packages/js-sdk/src/index.ts:129-152export { Sandbox } / export default Sandbox / export * from './template'
控制面协议源spec/openapi.ymlOpenAPI paths / securitySchemes
数据面协议源spec/envd/process/process.protospec/envd/filesystem/filesystem.protoservice Processservice Filesystem
代码生成入口Makefilepackages/js-sdk/package.jsoncodegengenerate
monorepo 工作区pnpm-workspace.yamlpackages/* + spec
SDK 定位(description)packages/js-sdk/package.json"E2B SDK that give agents cloud environments"

本篇到此为止只做导航与全景:两条平面、一条主线、仓库地图与阅读顺序。每条平面的真实实现——REST 的每个端点、URL 推导与鉴权细节、流式 RPC 的背压、文件双通道、模板 DSL、代码生成——分别在 0106 各章展开。从 01-control-plane.md 开始下钻。