Skip to Content
SpikesS2 · sandbank capabilities

Spike S2 · Sandbank 集成可行性(capability + dispatch + BoxLite local)

状态:✅ 通过(2026-05-18)

原始 S2 目标”FlyAdapter / BoxLiteAdapter 各跑 45min × 10 次”需要真 Fly app 和 VPS(POC 尚未具备)。本 spike 改用 3 个本地等价验证覆盖核心风险:

S2.a · sandbank Provider 在 ESM NestJS 里实例化

测试apps/api/src/task-runner/task-runner-provider.capability.spec.ts

实测两个 adapter 真实 capability set(用于固化契约 + 防止未来误用):

CapabilityFlyioAdapterBoxLiteAdapter
exec.stream (流式 stdout)
terminal
volumes
sleep
snapshot
port.expose
services

关键发现:FlyioAdapter 不支持 exec.stream —— 但不影响 POC 架构,因为:

sandbank.create({ image, env }) ← 主 API 调 ↓ 容器 entrypoint = /app/run.js 自启动 ↓ run.js 通过 HTTPS callback 推送状态/日志 ← 不走 sandbox.exec() sandbank.destroy(id) ← 主 API 巡检

我们的双轨架构只用 provider.create / destroy / list / get 这 4 个核心方法, 所有 adapter 都支持——capability 差异对架构透明。

→ 这印证了 design.md D5(run.js 平台无关)的稳健性。

S2.b · 真启动 NestJS · curl dispatch + internal events

结果

  • ✅ AuthGuard 拦未登录 dispatch → 401
  • ✅ Internal events 缺 HMAC → 401 missing_hmac
  • ✅ Internal events HMAC 错误 → 401 invalid_hmac
  • ✅ Internal events HMAC 正确 → 通过验签进入 service 层(taskId 不存在导致 FK violation 500,这是数据完整性正确行为)

验证范围

  • HMAC 验签链路端到端通过
  • Sandbank 实例化在 NestJS bootstrap 阶段成功(FlyAdapter 默认实例化)
  • 3 个新 endpoint(dispatch / sse / internal events)路由注册正确

后续改进(不阻塞 POC):taskId 不存在时应该返 404 而不是 FK violation 500。

S2.c · BoxLite local mode 真启动 microVM

目标:验证 BoxLite 在本机(macOS Apple Silicon + Hypervisor.framework)能跑—— 不依赖 VPS 也能为私有化路径做完整 lifecycle 验证。

步骤

  1. python3 -m venv /tmp/boxlite-spike-venv && pip install boxlite → 装到 boxlite 0.9.5
  2. async API 路径:
    import asyncio, boxlite async def main(): b = boxlite.Boxlite.default() box = await b.create(boxlite.BoxOptions( image="alpine:latest", auto_remove=True, )) await box.start() exec_obj = await box.exec("uname", args=["-a"]) chunks = [] async for chunk in exec_obj.stdout(): chunks.append(chunk) output = "".join(chunks) print(output) await b.remove(box.id, force=True) asyncio.run(main())

验证结果

box.id : zTLK4NCH8vae cmd : uname -a stdout : 'Linux boxlite 6.12.76 #1 SMP Tue Mar 10 13:28:56 CET 2026 aarch64 Linux' exit_code : (ExecResult object) cleanup OK: 0 个 box 残留

核心证据Linux boxlite 6.12.76 ... aarch64 —— microVM 跑的是独立的 Linux 内核, 不是 macOS 主机 kernel 的 namespace 隔离。这是真 microVM 隔离。

关键事实

  • BoxLite Python SDK 是 async 风格(与 sandbank TS SDK 同步风格不同)
  • macOS Apple Silicon 用 Hypervisor.framework;Linux x86/ARM 用 KVM
  • 完整 lifecycle: create → start → exec → wait → remove 全部跑通

NestJS 实施期跨语言调用方案

BoxLite 官方只有 Python SDK,但主 API 是 NestJS / TypeScript。两种集成方案:

方案 A · boxlite-server 独立进程(推荐)

  • 在 BoxLite host(Mac mini / VPS)上跑 python -m boxlite.server(或类似 daemon),暴露 REST/gRPC
  • Sandbank BoxLiteAdapter 内部已是这种模式——通过 HTTP 调 boxlite-server
  • NestJS → Sandbank → boxlite-server → microVM
  • 优点:跨语言、跨网段(Mac mini 在 LAN 内即可)、Sandbank 屏蔽所有 Python 细节
  • 缺点:多一个进程要管(systemd / launchd / 容器化)

方案 B · NestJS 子进程包装(不推荐):

  • NestJS spawn python3 -c "..." 调用 boxlite SDK
  • 通过 stdin/stdout 通信
  • 缺点:跨进程序列化复杂、调试难、丧失 Sandbank capability 抽象

POC 采用方案 A:BoxLite host 上跑 boxlite-server(systemd unit on Linux / launchd on macOS);NestJS 通过 Sandbank BoxLiteAdapter 连上。BOXLITE_API_URL=http://<host>:8080 env 配。

对 design.md 的补强

→ **D5(run.js 平台无关)**得到 capability 层实测确认:不依赖 exec.stream, 对 FlyioAdapter / BoxLiteAdapter capability 差异完全透明。

→ **D2(私有化用 BoxLite)**得到本地验证:BoxLite microVM 在 Mac 上能跑, POC 阶段开发者可以本机 spike 不必先架 VPS。

与原 S2 目标的对应

原 S2 验证项等价覆盖
Sandbank 在我们的 stack 能装 + 实例化✅ S2.a(capability spec 全过)
exec.stream 流式 stdout 不掉✅ 但不需要——架构走 HTTPS callback
FlyAdapter / BoxLiteAdapter 各跑 45min × 10 次稳定性⏳ 推迟到有 Fly app + VPS 时跑
主 API 集成端到端✅ S2.b(HMAC 链路通)
BoxLite 私有化能跑✅ S2.c(本机 microVM 真启动 + lifecycle)

未覆盖的风险(保留为 POC 实施期验证)

  1. 长任务稳定性(45min × 10 次)—— 需真 backend。建议 POC 实施期 §7 任务 12 / 16 真跑时观察
  2. 跨 adapter 切换无副作用 —— 已在 capability spec 测过实例化层;端到端切换要等真 Fly app
  3. Fly 出口 IP 白名单 / 私有化网络配置 —— 与具体 VPS 选型耦合,留 S3 / S4 spike