犀牛鸟 2026 研究笔记 estelledc.github.io

沙箱领域待解决问题深度研究

针对 sandbox-research-retrospective.md 中列出的 5 个待解决问题,逐一深挖并给出答案。 数据来源:本地代码 + GitHub issue + 各项目 CLAUDE.md / AGENTS.md / CONTRIBUTING.md


待解决问题 1:Daytona 的完整架构(控制平面 → 计算平面通信 / Daemon / 镜像 / 快照 / 网络 / 存储)

1.1 三层架构总览

Daytona 是 6 个项目里架构最复杂的。它不是一个沙箱引擎,而是一个完整的 Agent 执行平台

┌─────────────────────────────────────────────────────────┐
│                    Interface Plane                       │
│  Python/TS/Go/Java/Ruby SDK · CLI · Dashboard · WebTTY  │
├─────────────────────────────────────────────────────────┤
│                    Control Plane                         │
│  NestJS API · PostgreSQL · Redis · Kafka · Snapshot Mgr │
├─────────────────────────────────────────────────────────┤
│                    Compute Plane                         │
│  Runner (Go) · Docker · Daemon (inside sandbox)        │
└─────────────────────────────────────────────────────────┘

1.2 控制平面 → 计算平面:异步 Job Queue(v2)

这是 Daytona 最关键的架构决策。

v0(旧):同步 REST — API 直接 HTTP 调用 Runner,等 Runner 返回结果。简单但耦合紧、容错差。

v2(当前):异步 Job Queue — 三步解耦:

  1. API 在 PostgreSQL 里创建 Job(CREATE_SANDBOX / START_SANDBOX / STOP_SANDBOX / BUILD_SNAPSHOT 等)
  2. Runner 用 长轮询(long-polling)从 API 拉取 Job
  3. Runner 执行完后通过 API client 回传状态

关键代码路径

为什么用长轮询而不是消息队列?

Redis 的角色:不是 Job Queue,而是事件总线。Runner 发布状态变更事件 → API 订阅 → 更新 DB → 推送给 SDK 客户端。这是实时状态同步的机制。

1.3 沙箱内部跑什么:Daemon

每个 Daytona 沙箱内部不是裸容器,而是跑了一个 Daemon 进程(Go 写),它是沙箱的”操作系统”。

Daemon 提供 5 个服务

服务 端口 功能
Toolbox API 2280 文件读写、命令执行、Git、LSP、Computer Use
Terminal Server 22222 PTY 终端访问
SSH Server 222 SSH 直连
Recording Dashboard 会话录制回放
Session Service 进程生命周期管理

Daemon 的注入方式:不是打包在镜像里,而是 Runner 在启动容器时动态挂载

daemonPath, err := daemon.WriteStaticBinary("daemon-amd64")
// Runner 把编译好的 daemon 二进制挂载到 /usr/local/bin/daytona-daemon

这意味着 Daemon 版本和 Runner 版本是绑定的——Runner 升级时自动升级 Daemon。

初始化流程

Runner 启动容器
  → Runner 轮询 http://<container-ip>:2280/version 等待 Daemon 就绪
  → Daemon 就绪后,Runner POST /init 传入 auth token
  → Daemon 初始化 telemetry,开始接受请求

Daytona #1418 的教训:Daemon 要求容器里有 bashsudocurl。如果用 node:18-alpine(只有 BusyBox),整个流程失败。这说明 Daemon 对基础镜像有隐式依赖。

1.4 沙箱镜像:三档 + BuildKit

Daytona 提供三种基础镜像:

镜像 基础 包含
sandbox Python 3.14 devcontainer XFCE 桌面、Chromium、FFmpeg、PyTorch/TensorFlow、Node.js 25、Claude/OpenAI SDK
sandbox-slim Python 3.14 slim 基础工具链、Node.js 22
sandbox-gpu sandbox + CUDA CUDA 13、PyTorch CUDA、vLLM、FlashInfer

镜像构建流程

  1. 用户提供 Dockerfile + build context
  2. Runner 调用 Docker BuildKit 构建
  3. 构建产物推送到内部 Docker Registry(apps/snapshot-manager
  4. Registry 后端支持文件系统或 S3

备份(Backup)机制:用 docker commit 把运行中的容器快照为镜像。这是 Daytona 快照的底层实现——不是 KVM 级别的快照,而是容器镜像级别的快照。这意味着:

1.5 网络隔离:iptables + Docker Bridge

Daytona 的隔离比 CubeSandbox/gVisor 弱——它是容器级隔离,不是 VM 级。

三层隔离机制

  1. Docker Bridge:每个沙箱在独立的 bridge 网络上,com.docker.network.bridge.enable_icc: false 关闭容器间通信
  2. iptables DOCKER-USER 链:每个沙箱有独立的出站过滤规则
  3. Linked Network:需要通信的沙箱共享一个 bridge(daytona-link-<owner-id>),用 iptables ACCEPT 规则放行

三种出站模式

与 CubeSandbox 的对比

1.6 持久化存储:S3 + FUSE

Daytona 的 Volume 实现很有意思:

S3 存储桶
  ↓ mount-s3(FUSE 驱动)
Runner 主机的 /mnt/daytona-volume-<uuid>/
  ↓ bind mount
沙箱容器内的 /user-specified-path/

这个设计的优点是 Volume 数据持久化不依赖特定 Runner(Runner 挂了 Volume 还在 S3 上)。缺点是 FUSE 的 I/O 性能比本地盘差。


待解决问题 2:E2B 基础设施全貌

2.1 E2B 不只是 SDK——它是三层架构

本地仓库只包含 SDK + CLI 代码(packages/),但 E2B 的真实架构有三层:

┌─────────────────────────────────────────┐
│  SDK 层(本仓库)                        │
│  JS SDK / Python SDK / CLI              │
├─────────────────────────────────────────┤
│  API 层(本仓库 spec/)                  │
│  OpenAPI 3622 行 / 60+ 端点             │
│  Sandbox / Template / Volume / Node     │
├─────────────────────────────────────────┤
│  基础设施层(e2b-dev/infra,不在本地)   │
│  Terraform + Firecracker + AWS          │
└─────────────────────────────────────────┘

2.2 E2B SDK 的完整 API 面

从本地代码中提取的完整 API:

生命周期create() / kill() / connect() / pause() / resume() / set_timeout() / is_running()

命令执行commands.run() / commands.start() / commands.list() / pty.start()

文件系统files.read() / files.write() / files.list() / files.watch() / files.mkdir() / files.remove() — 支持 gzip 压缩和签名 URL

Git 操作git.clone() / git.init() / git.status() / git.add() / git.commit() / git.push() / git.pull() / git.branches() / git.checkout()

快照create_snapshot() / list_snapshots() / delete_snapshots()

网络配置

Volume(持久化存储)Volume.create() / Volume.connect() / sandbox = Sandbox.create(volume_mounts={"/data": volume})

MCP Server:内置 McpServerGitHubMcpServer,支持通过 stdin/stdout 运行自定义 MCP server

Metricssandbox.get_metrics() — CPU / 内存 / 磁盘使用率 + 日志

Template Builder:Dockerfile-based 模板构建,支持 template.copy() / template.run_cmd()

2.3 与 Firecracker 的关系

E2B 的底层就是 Firecracker(在 AWS 上)。从 OpenAPI spec 可以看到:

E2B 添加的价值层

  1. API Gateway(NestJS 风格,60+ 端点)
  2. 计费系统(API Key 控制)
  3. Template 构建系统(Dockerfile → 沙箱模板)
  4. 网络策略管理(出站过滤、header 变换)
  5. Volume 持久化(S3 后端)
  6. MCP Server 集成
  7. Metrics & 监控

2.4 云平台支持与限制

平台 支持 原因
AWS ✅ 主要 KVM 支持好,Firecracker 原生
GCP KVM 支持好
Azure 嵌套虚拟化限制,KVM 不可用
通用 Linux 需要完整的编排基础设施

2.5 自托管的已知限制

  1. Terraform 部署复杂:需要 KVM / 网络 / 存储专业知识
  2. 硬件要求高:需要裸金属或专用 KVM 实例
  3. 无官方支持:自托管是 DIY
  4. 维护负担:Firecracker 安全补丁、监控、升级全自己来
  5. Open Core 锁定:SDK 免费但 API Key 限制规模,大规模必须付费

2.6 SDK 三方并行的维护负担(CLAUDE.md 揭示)

E2B 的 CLAUDE.md 有一条没在公开文档中提到的硬约束

“Equivalent changes are applied to both JS as well as sync and async Python implementations.”

E2B 同时维护 3 个 SDK 实现(JS / Python sync / Python async),任何 API 变更必须三个都同步。这是一个显著的隐性维护成本——公开文档不会告诉你这个。


待解决问题 3:6 个项目的 CLAUDE.md / AGENTS.md 内部指引

3.1 CubeSandbox — 最完善的 AI Agent 配置体系

文件结构

.claude/
  agents/
    security-code-reviewer.md      — OWASP Top 10 / 注入 / 认证审计
    documentation-accuracy-reviewer.md — 文档与实现一致性验证
    performance-reviewer.md        — 算法复杂度 / N+1 / 内存泄漏
    code-quality-reviewer.md       — 代码整洁度 / 错误处理
    test-coverage-reviewer.md      — 测试缺口分析
  commands/
    check-issue-duplicate.md       — Issue 合规检查 + 去重
    review-pr.md                   — 编排 5 个 reviewer 做全面 PR review
    label-issue.md                 — 自动打标签

AI 署名政策(AGENTS.md):

Commit 规范:每个组件有前缀(cubeapi: / cubelet: / docs: / shim:),支持跨组件 cherry-pick。

“秘方”发现:CubeSandbox 的 5-agent review 系统是一个完整的代码审查流水线——从安全、文档、性能、质量、测试五个维度自动审查 PR。这反映了一个理念:AI Agent 的代码沙箱项目,自己先用 AI Agent 做代码审查。

3.2 Daytona — Nix 优先的开发环境

AGENTS.md(347 行)是 6 个项目里最长的 AI 配置文件。

核心约束

.claude/settings.json 权限:预批准了大量 Bash 操作:

["Bash(gh pr:*)", "Bash(git fetch:*)", "Bash(nix develop:*)", 
 "Bash(git rebase:*)", "Bash(gh api:*)"]

说明 Daytona 团队深度使用 Claude Code,且给了 AI agent 很大的 git/GitHub 操作权限。

定制 Agent — batch-dependabot

“秘方”发现:Daytona 内部 Go 包之间有严格的版本耦合(api-client-gotoolbox-api-client-go 必须同版本),batch-dependabot agent 专门检测这个——说明这些包的历史上出过版本不一致的 bug。

3.3 E2B — 9 条硬规则

CLAUDE.md 极简但关键:

  1. pnpm(Node)/ poetry(Python)包管理器
  2. 提交前必须跑 format / lint / typecheck
  3. SDK 三方同步:JS 变更必须同步到 sync + async Python
  4. spec/ 修改后必须 make codegen 重新生成 API client
  5. 测试覆盖受影响路径
  6. packages/js-sdk / packages/python-sdk 更新需要 changeset
  7. PR 描述必须包含使用示例
  8. 默认凭据存在 .env.local~/.e2b/config.json

“秘方”发现:E2B 的 SDK 不是手写的——是通过 spec/ 目录下的 OpenAPI/protobuf 定义用 make codegen 自动生成的。这意味着修改 API 接口时,SDK 代码是”衍生品”,不能直接改,必须改 spec 再重新生成。

3.4 Firecracker — 无 AI 配置,但有最严格的 unsafe 代码规范

没有 CLAUDE.md / AGENTS.md / .claude/ 目录

但 CONTRIBUTING.md 揭示了一个严格的安全文化

unsafe 代码必须附上量化理由

// JUSTIFICATION: This cannot be accomplished without unsafe as
// `external_function()` returns `RawFd`...
// SAFETY: `external_function()` returns a valid file descriptor.
unsafe {
    libc::close(external_function());
}

要求:

错误处理哲学:禁止 Option::unwrap / Result::unwrap,必须用 .map / .map_errmatch。CONTRIBUTING.md 里有具体的前后对比 refactoring 示例。

“秘方”发现:Firecracker 的 core 包有严格的依赖白名单——core 只能依赖特定列表里的包。这在一个 Rust 项目里不常见(Rust 的模块系统通常不会限制依赖方向),说明 Firecracker 团队对攻击面有极其严格的管理。

3.5 gVisor — 专家级 Syscall 实现工作流

没有 CLAUDE.md,但有 AGENTS.md + .claude/skills/add-syscall/SKILL.md(267 行)。

AGENTS.md 定义了”专家系统工程师”人设:专精 Linux 内核 / ABI / 内存管理 / 沙箱逃逸漏洞。

add-syscall SKILL.md 是 6 个项目里技术含量最高的 AI 配置——它定义了一个完整的 Linux syscall 实现工作流:

Phase 1:理解现状

Phase 2:规划

Phase 3:实现

Phase 4:测试驱动验证

  1. 先跑原生测试:bazel test //test/syscalls:<syscall>_test_native
  2. 构建 gVisor
  3. 在 gVisor 下跑:bazel test //test/syscalls:<syscall>_test_runsc_ptrace_shared
  4. 迭代直到两层测试都通过

代码模板(直接嵌入 SKILL.md):

// 错误返回
return 0, nil, linuxerr.EINVAL

// 用户空间内存读写
_, err := primitive.CopyInt32Out(t, args[1].Pointer(), value)

// 未实现选项
t.Kernel().EmitUnimplementedEvent(t, sysno)
return 0, nil, linuxerr.ENOSYS

// 权限检查
creds := t.Credentials()
if !creds.HasCapabilityIn(linux.CAP_SYS_ADMIN, creds.UserNamespace) {
    return 0, nil, linuxerr.EPERM
}

“秘方”发现:gVisor 的”原生测试 → gVisor 测试”两步验证是一个独特质量门禁——确保 Sentry 的行为与真实 Linux 内核一致。这是 gVisor 能作为容器 runtime 使用的核心保障。

3.6 Kata Containers — 无 AI 配置

没有 CLAUDE.md / AGENTS.md / .claude/ 目录。CONTRIBUTING.md 直接指向社区外部文档。.editorconfig 只有基础的 LF + UTF-8 配置。

这说明 Kata 的治理完全走 OpenStack 基金会的社区流程,不依赖单一公司的内部工具链。


待解决问题 4:Firecracker 架构深度解析

4.1 VMM 进程启动流程

入口src/firecracker/src/main.rs

main()
  → 设置 panic handler / logger / metrics
  → 注册 signal handler
  → 解析命令行参数(API socket 路径、实例 ID、seccomp 过滤器)
  → 调整 FD 表大小(避免重分配开销)
  → 二选一:
     ① run_with_api(有 REST API)
        创建 EventManager + API Server 线程 + VMM 线程
        通过 channel 通信
     ② run_without_api(无 API,直接 boot)
        直接 event loop

Event Loop 架构

4.2 MicroVM 创建(build_microvm_for_boot)

文件src/vmm/src/builder.rs

逐步流程

  1. KVM 初始化:创建 Kvm 实例 + KvmVm(KVM VM fd)
  2. vCPU 创建vm.create_vcpus(vcpu_count) → 每个 vCPU 独立线程
  3. 内存配置:分配 guest 内存区,支持热插(virtio-mem)
  4. Device Manager:管理 MMIO / PCI / legacy / ACPI 设备
  5. 加载内核:把 kernel image 加载到 guest memory
  6. 挂载设备(按固定顺序保证 MMIO 地址一致):
    • Boot Timer → Balloon → Block → Net → PMEM → Vsock → RNG → virtio-mem → VMGenID → VMClock
  7. 系统配置:CPU 特性、中断控制器、ACPI 表

4.3 vCPU 事件循环

文件src/vmm/src/vstate/vcpu.rs

vCPU 线程启动
  → 加载 seccomp 过滤器
  → 进入 Paused 状态
  → running() 循环:
      run_emulation()
        → KVM_RUN(进入 KVM 执行 guest 指令)
        → 处理退出:
            MMIO Read/Write → 路由到 MMIO bus
            System Events → 关机/重启信号
            EINTR → 外部中断(暂停/恢复)
        → 循环直到 Stopped

安全设计:vCPU 线程被视为不可信(运行潜在恶意 guest 代码),通过两层保护:

4.4 vsock 通信模型

文件src/vmm/src/devices/virtio/vsock/device.rs + unix/muxer.rs

核心设计:绕过 vhost 内核代码,virtio-vsock 完全在用户态实现。

Guest(AF_VSOCK)↔ Firecracker(VsockMuxer)↔ Host(AF_UNIX)

两种连接模式

方向 流程
Host → Guest Host 连 AF_UNIX → 发 “CONNECT <port>” → Firecracker 转发到 guest AF_VSOCK
Guest → Host Guest AF_VSOCK connect → Firecracker 转发到 AF_UNIX socket

CID(Context ID):Firecracker 对 guest 来说是 CID 2。

后端实现VsockMuxer 用 epoll 做异步 I/O,管理连接状态机(VsockConnection)。

4.5 Jailer 安全机制

文件src/jailer/src/main.rs + cgroup.rs + chroot.rs

7 层安全措施(按执行顺序):

机制 作用
1 close_range() 关闭所有非标准 FD
2 环境变量清理 清除所有 env var
3 路径验证 canonicalize + 检查硬链接
4 Chroot Jail pivot_root + chroot 隔离文件系统
5 Cgroup CPU / 内存 / IO 资源限制(v1+v2)
6 Network Namespace setns(CLONE_NEWNET) 隔离网络
7 权限降级 UID/GID 设为非特权用户

最终效果:Firecracker 进程以完全非特权身份运行,只能访问 chroot 内的文件,受 cgroup 限制,网络隔离。

4.6 VirtIO 四件套

设备 文件 后端 关键特性
Net virtio/net/device.rs TAP 设备 Checksum offload / TSO / 带宽限速 / MMDS
Block virtio/block/device.rs 主机文件 async I/O / 只读模式 / rate limiter
Vsock virtio/vsock/device.rs AF_UNIX socket 完全用户态 / epoll 异步
RNG virtio/rng/device.rs AWS-LC-RS 密码学安全随机数 / 速率控制

额外设备:Balloon(内存 ballooning)/ PMEM(持久内存)/ virtio-mem(内存热插)

4.7 快照 / 恢复

文件src/vmm/src/persist.rs + src/vmm/src/snapshot/

两种快照

类型 内容 大小 速度
Full Snapshot 完整 guest memory + KVM state + vCPU state + device states
Diff Snapshot 仅自上次快照以来修改的内存页(dirty bitmap)

创建流程

  1. API 暂停 VM
  2. Device Manager 序列化所有设备状态
  3. KVM 保存 vCPU 状态和 VM 状态
  4. 内存写入文件(Full = 全部 / Diff = dirty pages)

恢复流程

  1. 创建 KVM VM + vCPU
  2. 恢复 vCPU 状态
  3. 恢复设备状态(vsock UDS 路径可覆盖)
  4. MAP_PRIVATE 映射内存文件(CoW 语义)
  5. 恢复 vCPU 运行

安全注意:快照包含敏感数据(内存中的密钥等),但只有 CRC 校验没有加密。


待解决问题 5:跨项目的 CLAUDE.md / AGENTS.md 对比

5.1 AI 署名政策对比

项目 DCO 签名 AI 署名 具体政策
CubeSandbox ✅ 必需 Assisted-by: / Autonomously-by:
daytona ✅ DCO v1.1 ❌ 未指定 标准 DCO
E2B ❌ 未提及 ❌ 未提及
firecracker ✅ 必需 ❌ 未指定 标准 DCO
gvisor ✅ Google CLA ✅ 鼓励 Assisted-by: Gemini CLI
kata-containers ❓ 外部 ❓ 外部 社区治理

5.2 AI Agent sophistication 对比

项目 配置类型 复杂度
CubeSandbox 5 agents + 3 commands ⭐⭐⭐⭐⭐ 专家级多 Agent 系统
daytona 1 专用 agent + 宽松权限 ⭐⭐⭐⭐ 生产级
E2B 9 条 CLAUDE.md 规则 ⭐⭐⭐ 基础但关键
firecracker ⭐ 安全优先文化
gvisor 1 专家 skill(syscall 工作流) ⭐⭐⭐⭐ 深度专业
kata-containers ⭐ 社区治理

5.3 技术栈”秘方”汇总

项目 不公开的关键约束
CubeSandbox 多 Agent review 流水线;commit 前缀规则支持 cherry-pick
daytona 内部 Go 包版本耦合(api-client-go ≈ toolbox-api-client-go);Nix flakes 强制;5 语言 dev shell 映射
E2B SDK 三方同步(JS + Python sync + Python async);spec → codegen → SDK 的生成式工作流
firecracker unsafe 代码需量化理由;core 包依赖白名单;禁止 unwrap
gvisor 原生测试 → gVisor 测试两步验证门禁;syscall 行为与 Linux 一致性的隐式合约
kata-containers 无(全部在社区层面管理)

总结:5 个待解决问题的答案

# 问题 答案
1 Daytona 架构 三层架构(Interface/Control/Compute);控制→计算用异步 Job Queue(DB + 长轮询);Daemon 是沙箱内的多服务 agent;镜像用 Docker BuildKit;快照用 docker commit;网络用 iptables + Docker bridge
2 E2B 基础设施 SDK 三层架构(SDK / API spec / Terraform infra);核心代码在 e2b-dev/infra 不在本地;只支持 AWS/GCP(Firecracker 需要 KVM);自托管复杂且无官方支持
3 CLAUDE/AGENTS 配置 CubeSandbox 最完善(5-agent review);daytona 最长(Nix 强制 + batch-dependabot);E2B 有 SDK 三方同步约束;gVisor 有专家级 syscall 工作流 skill;Firecracker/Kata 无 AI 配置但有严格代码规范
4 Firecracker 架构 VMM 事件循环(epoll)→ build_microvm 创建 → vCPU KVM_RUN 循环 → 4 VirtIO 设备 → vsock 用户态通信 → 7 层 Jailer 安全隔离 → snapshot/restore
5 跨项目对比 6 个项目的 AI 配置 sophistication 差异极大;最”秘密”的约束是 E2B 的 SDK 三方同步和 daytona 的内部包版本耦合