
Docker Sandboxes 工作区怎么接:Direct、Clone 和它们的边界
TL;DR
Direct 模式下,工作区以读写方式挂进 sandbox,Agent 改的文件立刻出现在 Mac 上,适合人在旁边、和 Agent 改同一棵树;代价是 Agent 也能删掉或弄乱这棵树。
Clone 模式(--clone)反过来:宿主机仓库只读挂载,Agent 在 sandbox 内部一份私有 clone 里改、commit。宿主机看不到实时变化,要主动 git fetch sandbox-<name> 才能拿到提交;删除 sandbox 前不 fetch,未保存的提交跟着一起没。这是官方建议无人值守场景优先验证的模式,不是 Direct 的可选装饰。
上一篇留的边界
上一篇 装完 sbx、跑通 OpenCode 后收在一句话:工作区挂进去之后,.env 对 Agent 是可见的,隔离挡的是工作区外面,不是工作区里面。
这句话说的其实是默认行为——Direct 模式。工作区外面有 kernel、文件系统、网络、Docker Engine 四层墙;工作区里面呢,Agent 和你本人权限一样,没有额外限制。这一篇拆开这道边界本身:它是不是必须这样,能不能换一种挂法。
Direct:默认挂法,读写实时双向
sbx run opencode ~/my-project
不加任何参数就是这个模式,上一篇 从头到尾用的也是它。工作区通过 virtiofs 挂进 sandbox,路径和 Mac 上一样,读写。我在宿主机写一个文件、在 sandbox 内 cat 能立刻读到,反过来也一样——不是轮询同步,是同一份数据的两个入口。
特点摆在一起看:
- Agent 改的文件立刻出现在 Mac 上,你随手
git diff就能看 - Agent 能直接用 Git,
branch、commit、push都在你熟悉的仓库里发生 - 宿主机切 branch,sandbox 里下一秒看到的就是另一批文件——两边共用同一个
.git,没有独立视角 - Agent 也能删除或污染这棵工作区;工作区外面有四层墙,工作区里面没有额外的一层
这也是为什么上一篇结尾要单独点一句 .env:Direct 模式下没有“工作区内的沙箱”,只有“工作区外的沙箱”。人不在旁边看着,这条模式的风险敞口和裸跑一个本地 Agent 差别不大——只是权限收窄到这一个目录。
Clone:给 Agent 一份私有副本,commit 要自己 fetch 回来
cd ~/my-project
sbx create --clone --name my-sandbox opencode .
sbx run --name my-sandbox
--clone 翻转了挂载方向。我实测了一遍完整链路,比官方一句话描述更具体:
sbx create --clone执行后,宿主机仓库以只读挂进 sandbox(/run/sandbox/source,ro)- sandbox 内部起一个
git-daemon,导出这份数据;命令行直接告诉你端口,比如git://127.0.0.1:49153/my-sandbox - 宿主机这边自动多一条 git remote,命名规律是
sandbox-<name>:
$ git remote -v
sandbox-my-sandbox git://127.0.0.1:49153/my-sandbox (fetch)
sandbox-my-sandbox git://127.0.0.1:49153/my-sandbox (push)
Agent 在 sandbox 内部的私有 clone 里改、commit、开 branch,这些动作不会实时出现在你的 Mac 上。你要主动拉:
git fetch sandbox-my-sandbox
git log --oneline sandbox-my-sandbox/main
拿到提交后可以 git branch <name> sandbox-my-sandbox/main 落地成本地分支,走平时的 review 流程。
删除前必须 fetch,这条是硬约束,不是建议。 我删掉一个没 fetch 过的 clone sandbox,sbx rm 会先打印警告:
Warning: sandbox "my-sandbox" runs on an in-container clone (--clone). Any
commits the agent made inside the sandbox are lost on removal
unless preserved first. To keep them, before removing run:
git fetch sandbox-my-sandbox
已经 fetch 过的分支不受影响:sbx rm 会把它们镜像进 refs/sandboxes/<name>/*,删完 sandbox 之后这些 ref 还在,用 git branch <local-name> refs/sandboxes/<name>/<branch> 随时能捞回来。没 fetch 过的提交,删了就是没了,找不回。
还有一条容易踩的限制:--clone 必须在主仓库的 checkout 里创建,不能从次级 Git worktree 里创建——想在 worktree 上用 Clone 模式,会在这一步失败。
两种挂法怎么选
| Direct(默认) | Clone(--clone) | |
|---|---|---|
| 挂载方式 | 读写,virtiofs | 只读源 + sandbox 内私有 clone |
| 变化何时可见 | 立刻,双向 | 需要主动 git fetch sandbox-<name> |
| Agent 能否弄乱工作区 | 能,没有额外墙 | 不能,改动困在私有 clone 里 |
| 适合场景 | 人在旁边、同步协作、边跑边看 diff | 无人值守、批量任务、多个 Agent 同时跑一个仓库 |
| 删除 sandbox 的代价 | 无——工作区是宿主机自己的 | 未 fetch 的提交直接丢失 |
官方的建议很直接:真正要无人值守跑,先验证 Clone 模式,不要只测过 Direct 就假设它也扛得住没人盯着的场景。我倾向于反过来理解这条建议——不是“Clone 更安全所以永远用它”,是“Direct 的安全边界依赖你人在,一旦人不在,边界就没了”。
还没验证的第三条路
素材里还记了一种:宿主机自己为不同任务开独立 Git worktree,再把每个 worktree 目录当工作区分别喂给不同 sandbox。这样改动照样实时落在宿主机上,但走的是 worktree 的路径映射和 Git 元数据,跟 Docker 的 Clone 模式是两套不同的机制,不能混着说。这条我还没跑通完整验证,这篇先不展开,等测完再单独写。
小结
Direct 和 Clone 不是“哪个更好”,是两种不同的信任模型:前者信你在场,后者信 Git 的 fetch/push 边界。选错一个,代价不是报错,是无人值守时悄悄丢掉一次 commit,或者被 Agent 顺手改坏的一棵工作树。下一篇打算写 Skills store 和 Host-side MCP Gateway——sandbox 之间怎么共享技能、怎么让 Agent 摸到本地跑着的 MCP server。



