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(用于固化契约 + 防止未来误用):
| Capability | FlyioAdapter | BoxLiteAdapter |
|---|---|---|
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 验证。
步骤
python3 -m venv /tmp/boxlite-spike-venv && pip install boxlite→ 装到 boxlite 0.9.5- 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 实施期验证)
- 长任务稳定性(45min × 10 次)—— 需真 backend。建议 POC 实施期 §7 任务 12 / 16 真跑时观察
- 跨 adapter 切换无副作用 —— 已在 capability spec 测过实例化层;端到端切换要等真 Fly app
- Fly 出口 IP 白名单 / 私有化网络配置 —— 与具体 VPS 选型耦合,留 S3 / S4 spike