笔记本一合上 agent 就没了,那是 demo。工作区能重连、能冻结证据,却仍然拒绝自己发布——这才更像产品。
今天我们发了 Appaloft 1.4.0。发布说明里有远程 Agent Workspace、Task Run、协作、休眠、可迁移恢复、可复用 Snapshot、远程 terminal attach,以及声明式 Agent Adapter 的第一刀。这篇写这些 bullet 背后的架构:谁拥有什么、Cloud 注入什么、我们停在哪里,以及它和 OpenSandbox、E2B、Modal 一类产品差在哪一层。
如果你读过构建应用的 agent,不该是那个点发布的角色和英文的 How we built the delivery evidence chain,这篇是更底下那一层:东西变成可发布候选之前,agent 真正干活的地方。
一直撞上的问题
本地 agent 循环挂得很无聊:
- 人合上盖子,agent 进程没了。
- 两个人想看同一次 run,共享笔记本会话扩不开。
- agent 给你一个别人打不开的
localhost:3000。 - 活干得差不多了,写文件的那个身份还想顺便部署。
所以我们要的不只是「起一个容器再 exec」。它得能重连、隔离租户、暴露临时预览、扛住计划内的算力释放,再接到一条 agent 主体单独走不通的证据链。
Appaloft 里的 Agent Workspace 是什么
Agent Workspace 不是 Cloud 新建的 aggregate。它是盖在现有 Sandbox 操作之上的公共入口工作流:
Sandbox
+ Agent Runtime(今天是 Pi / OpenCode)
+ 可重连 Terminal Session
+ 会过期的 Sandbox Port 暴露
+ 持久的 Agent Task Run
+ Workspace Collaboration(lane、writer lease、handoff)
身份规则是故意的:workspaceId = sandboxId。自托管、Cloud、未来的 Enterprise 发行版用同一套 CLI、SDK 和 operation catalog。Cloud 不会再造一张 Workspace 表,也不会平行搞一套 REST。
flowchart LR
subgraph Clients
CLI["CLI workspace *"]
SDK["SDK workspaces.*"]
Console["Console /workspaces"]
end
subgraph Public["Public Appaloft"]
WF["Workspace 入口工作流"]
SBX["Sandbox<br/>workspaceId = sandboxId"]
RT["Agent Runtime<br/>Pi / OpenCode"]
TERM["Terminal Session"]
PORT["Port Preview<br/>TTL + revoke"]
TASK["Agent Task Run"]
COLL["Collaboration<br/>writer lease"]
DELIVER["Source Artifact → Preview → Promote"]
end
subgraph Cloud["Cloud overlay 只注入"]
AUTH["authz / quota"]
SERVER["registered Server"]
GW["gateway / attach"]
REC["recovery + snapshot policy"]
ADAPT["Adapter 审批"]
end
CLI --> WF
SDK --> WF
Console --> WF
WF --> SBX
SBX --> RT
SBX --> TERM
SBX --> PORT
SBX --> TASK
COLL --> SBX
TASK --> DELIVER
Cloud -.->|"composition only"| Public
这张图就是产品赌注。客户端只打 Public Appaloft。Cloud 做 composition:registered server、gateway attach、模型访问、quota、recovery mount、snapshot 策略、Adapter 审批。虚线,不是归属。
部件怎么接上
创建路径很机械:
sandboxes.create- 对已注册 harness 调
sandboxes.agents.runtimes.create - 可选 terminal、preview port、task run、collaboration lane
给人用和给 agent 用的 CLI:
appaloft workspace create
appaloft workspace connect
appaloft workspace terminal
appaloft workspace preview
appaloft workspace task run
appaloft workspace pause
appaloft workspace resume
规范操作仍在 sandboxes / terminals / ports / collaborations 下面。workspace 命令是工作流门面,不是第二套领域语言。
协作也是公共且显式的。每个人或 agent 用自己的 Sandbox 身份。lane 里旁观者可以看真实 PTY 输出,写入走带 generation fencing 的可续期 writer lease。我们不会把一队人塞进同一个 checkout,指望大家守 tmux 礼节。
休眠、drain 和 Snapshot
1.4 把生命周期做硬一点,好让你关电脑:
| 机制 | 保留什么 | 明确不承诺什么 |
|---|---|---|
process-frozen pause |
进程 + 文件系统 | 更适合短暂停 |
compute-released hibernation |
/workspace 与文档约定的持久路径;释放 CPU/内存 |
任意 PTY 内存、未落盘的模型流 |
| Portable drain | 同一 SandboxId 迁到共享 recovery family 里的另一台 Server |
从未写入 recovery store 的字节 |
| Reusable Snapshot | 策略控制下的一对多 restore | 我们不替你运营的 shared mount HA SLA |
Resume 会重签 capability。旧的 terminal attach token 和 preview URL 故意作废。烦一次,正确一辈子。
2026-07-26 我们已经做过双 registered Server 验收(Yundu → Hostinger planned drain、同 identity 连续性、snapshot restore、精确 cleanup)。这够发表架构,不够编造 RPO 数字。文档拒绝承诺的地方,这篇也拒绝。
工作区何时变成可发布物
Workspace 可变,发布不可变。交接还是那条证据链:
flowchart LR
W["1. Workspace<br/>可变 FS + agent run"] --> F["2. Freeze<br/>Source Artifact digest"]
F --> P["3. Candidate Preview<br/>过期 URL + digest"]
P --> G["4. Promote / deliver<br/>仅外部 actor"]
subgraph AgentReachable["agent 可达"]
W
F
P
end
subgraph HumanGate["人 / 委托门禁"]
G
end
Sandbox 范围内的主体只负责准备,不能 accept promotion。如果这条边界显得严,那正好——见先前的agent 发布边界和英文的证据链。
和 OpenSandbox 等怎么比
市场上的 “sandbox” 其实是好几类产品。公平对比要靠轴,不靠感觉。下表依据 2026-07-27 的公开文档与仓库;我们没有横向重跑冷启动。
| 轴 | Appaloft Agent Workspace | OpenSandbox | E2B | Modal Sandboxes |
|---|---|---|---|---|
| 主业 | 持久 coding workspace + 交付控制面 | 通用沙箱平台(exec、agent、评测、RL) | 给 agent 的隔离 Linux(云) | 跑不可信代码的安全容器 |
| 身份模型 | workspaceId = sandboxId 的公共 ops 工作流 |
Sandbox lifecycle API + execd | 带 pause/snapshot 的 Sandbox 对象 | Modal 里的 Sandbox 对象 |
| 隔离(文档口径) | provider/template 策略;我们栈里有 gVisor 执行路径 | 默认 Docker/runc;可选 gVisor / Kata / Firecracker |
Firecracker microVM | gVisor |
| Agent 耦合 | Harness adapter(Pi、OpenCode;声明式 Adapter 下一步) | harness 无关 SDK / MCP 示例 | harness 无关 SDK | harness 无关 |
| 自托管 | 公共控制面 + 你的 Server | Docker / Kubernetes 一等公民 | 云 + BYOC | Modal 托管 |
| Preview | 会过期的 sandbox port + artifact candidate preview | port endpoint / proxy | sandbox domain / envd | Connect token / tunnel |
| 发布路径 | Freeze → candidate preview → 外部 promote / deliver | 不承担部署控制面 | 不承担 | 不承担 |
架构评审时更有用的几点:
1. 谁拥有生命周期。 OpenSandbox 和 E2B 拥有带 TTL、pause、snapshot 的 sandbox 对象。Appaloft 也拥有 Sandbox——然后在同一身份上叠 workspace、task、collaboration、promotion 工作流。如果你只要「跑一段不可信命令」,纯 sandbox API 更薄,完全够用。如果你要「明天还能重连,而且仍不能自己发布」,就需要工作流层。
2. 默认隔离 vs 可配置隔离。 E2B / Modal 把强默认写进产品(Firecracker / gVisor)。OpenSandbox 让 API 保持中立,由运维选 secure_runtime。Appaloft 更接近后者:隔离是 provider/template 属性,不是 Workspace 类型上的营销词。别把 “workspace” 读成「我们发明了新的 microVM」。
3. 凭证叙事。 OpenSandbox 的 Credential Vault 一类出站注入,尽量让 guest 看不到真密钥。我们关心同一失败模式,但当前发布硬线不同:密钥不能进不可变 Source Artifact,sandbox 主体也不能 promote。guest 密钥注入和 artifact 密钥扫描解决相邻问题,威胁模型里两样都要。
4. 产品层别混。 Cloudflare Sandbox SDK(Linux 容器 + preview URL)和 Dynamic Workers(V8 isolate)很容易被当成一类。只有前者才和 coding workspace 同场。DevPod / Codespaces 在人用 IDE 轴。OpenHands 的 sandbox 和 agent 绑死。跨层比功能清单没有意义。
5. 开源路径。 OpenSandbox 是 Apache-2.0、偏自托管。E2B 开源 SDK,BYOC 另讲。Daytona 公开仓在 2026-06 停更、核心迁私有——读旧 README 时别把「开源自托管」当成现状。Appaloft 的 Workspace 真相源在 public core;Cloud 是 overlay。
我们不宣称自己是唯一可 pause 的沙箱、唯一的 gVisor 选项,或唯一友好 MCP 的 exec API。那些已经存在。我们的主张更窄:一个远程 agent workspace,仍然说部署证据那一套操作语言。
门禁未齐,架构先正确
1.4 有些面已经收口,有些没有。没收口的部分,方向我们打算继续守。
形状已经可以说清楚、不必再对产品形状打补丁的
- Pi / OpenCode 的远程 Workspace 入口
- Task Run 过程管理器(
taskRunId = runId) - Collaboration 的 writer lease / observer / handoff
- compute-released 休眠、capability 重签、planned drain、可复用 Snapshot(双 Server 验收已有记录)
方向已定、退出门禁还在关的
- 托管环境对 test-owned GitHub 仓做真实 PR write go-live(幂等重试 + 精确 cleanup)。Task Run 架构在,验收未关。
- 开放 Adapter / Workspace Profile:声明式 manifest、租户安装审批、profile pin、credential grant。契约已是公共的(
appaloft.agent-adapter/v1、appaloft.agent-workspace-profile/v1)。完整的 Codex-on-registered-server smoke 和任何 marketplace 故事是后面章节,不是今天的主张。
flowchart TD A["Declarative Adapter / Profile"] --> B["Harness 解析<br/>不执行租户上传的控制面代码"] B --> C["Pinned Workspace 实例<br/>digest + installation id"] C --> D["同一套 Sandbox / Task / Collaboration / Promote ops"]
这是故意未完成的产品,不是空气。Cloud 会审批和审计安装,不会执行租户上传的控制面包。哪天这条线糊了,这篇就成了我们写错的证据。
正在承受的取舍
- Workspace 和 Sandbox 同身份让 ops 和 cleanup 简单。每个 Workspace 功能也必须服从 Sandbox 过期、网络策略和 provider 限额——没有逃逸 aggregate。
- Harness adapter 而不是唯一钦定 agent让控制面保持诚实。每个新 runtime 都要付 conformance 成本。
- 文件系统优先的休眠可运维。承诺完整 TUI 内存复活则不可。
- 先证据再发布拖慢 demo。这也是我们愿意让 agent 靠近交付的唯一理由。
自己摸一摸
- Agent workspaces 文档(若导航调整,以 docs 的 agent 分区为准)
- 发布:v1.4.0
- 相关阅读:面向 AI 时代的部署控制面、Skills 与 MCP、CLI auth handoff
如果你在做相近或互补的沙箱,我们想听具体反馈:workspaceId = sandboxId 帮没帮上忙?密钥你更想放 guest vault、host 注入,还是 artifact 门禁?Codex 验收路径绿灯之后,Adapter profile 下一个该接哪个 harness?