# 沙盒 · 技能怎么安全跑代码

> 技术参考 · 深入原理(架构师向)。承接《[Skill 市场](31-skills.md)》。
> 有些技能不只是"提示词",而是要**执行代码**。为此平台有一个**沙盒**:一个隔离的执行环境,让技能的代码跑在与平台本体、与其它 Agent 都隔开的地方。这一篇讲它现在是什么、隔离红线在哪,以及为什么这条红线不能靠"约定"。

---

## 沙盒是什么(现状)

当前 Agent 的 `run_skill` 默认起一个**一次性的隔离虚拟机**:

- 把技能需要的代码/依赖拉进隔离 VM;
- 执行一轮,当前单次运行上限为 **20 分钟**;
- 经认证的结果通道把结果带回平台,随后结束这次执行环境。

沙盒还支持一组**按 Skill 声明、按部署配置启用**的能力;不是每个 Skill 都自动拥有:

- **持久工作区**:启用时,运行前取回该 Agent 自己的工作区,运行后再保存,所以文件/工具可跨次延续;
- **Artifact**:技能把完整文件写到约定目录并发出固定格式信号,运行时解析后由平台登记为该 Agent 的产物;
- **凭证注入**:owner 填写的 Skill 凭证加密保存,运行时通过一次性领取能力注入成文件或环境变量;明文不进入对话,凭证文件也不随工作区回存;
- **事件流**:启用时可回传中间执行事件,供平台展示进度。

> 要分清两个维度:**VM 默认一次性**不等于**Agent 没有跨次状态**。只有声明并配置了工作区的 Skill 才有跨次持久化;未启用这些能力的普通运行仍是一次性执行、只回最终结果。

---

## 草稿试跑:审核前先在真沙盒里跑一次

对话式创建的沙盒 Skill(见 [31-skills](31-skills.md))在发布**之前**就能试跑:草稿包临时上架到 `draft-` 命名空间,用与正式运行**完全相同**的调度链起 VM——同样的父 Skill 暂存、同样的凭证注入、同样的计费与结果回传。试跑用的包每次换一个新 nonce,不覆盖任何已发布的包。

另有一条把**创作本身**放进沙盒的通路:skill-builder。它把草稿当前文件挂在 `draft/` 下,起一个完整的编码 Agent 去改、去跑、去验证,再通过一条**可复用、随运行超时到期**的能力令牌把文件写回草稿(`POST /api/v1/sandbox-drafts/put`)。可复用是刻意的 —— builder 被要求每改完一轮就存一次,VM 中途挂掉时作者至少留得住已完成的部分;写回是 merge 而非整体替换,所以作者同时在对话里做的修改不会被抹掉。builder **拿不到任何凭证**,也没有发布权限,能做的最"重"的事就是往它被指定的那一个草稿里写文件。

试跑的凭证面**和发布后完全一致** —— 包括父技能持有的敏感凭证(它自铸的身份、Agent 钱包私钥)。这是刻意的:一个连父技能都认证不上的子技能,根本无从测试,那条规则的真实效果只会是"不测就发"。

曾经有过一版更窄的规则(试跑时扣下 secret 级凭证),被取消了,原因值得写下来:

- **它绕得过去。** 对话式发布默认是私有,私有提交按设计跳过人工终审(它压根不进市场),而私有技能会自动装给作者自己 —— 到这一步 `run_skill` 照样给全量继承。一次发布,三十秒。这道闸门只拦住了"先测试再发布"的人。
- **它没保护到任何人。** 人工审核保护的是**安装者**,防的是陌生人的技能。而试跑场景里,作者、owner、唯一受影响的 Agent 是同一个人,用的是自己 Agent 已经持有的凭证。

取而代之的是**如实告知**:凡是试跑收到了这类敏感凭证,运行结果会逐条列出来,Agent 会用一句话告诉 owner —— 说一次,不当报警。

仍然关着的只有一处:草稿**自己**声明 secret 级平台凭证。直接声明需要技能已上架且达到 verified;通过 `depends_on` 从已审核的父技能继承,才是正规路径。

---

## 🔴 头号红线:能力只能碰这个 Agent 的资源

沙盒**能执行任意代码**,所以必须假设它会尝试篡改端点、参数或路径。不可让步的验收条件是:

> **交给一次沙盒运行的每项能力,都只能触达对应 Agent、对应 Skill 或对应 run 的最小资源范围。**

隔离可以由身份凭证实现,也可以由更窄的 capability 实现;关键是**权限本身够窄**,不能只靠沙盒代码自觉:

- 工作区不把通用存储凭证交给 VM;运行时只拿到本次对应 workspace object 的临时读写能力,不能自行改成另一个 Agent 的路径;
- 凭证通过绑定到 Agent/Skill 的一次性领取能力交付,写入仅沙盒 Agent 进程所属用户可读的文件,并排除在工作区回存之外;
- Artifact 由运行时解析固定信号并随认证结果返回,沙盒不需要拿平台后台 token;
- 后端不信任沙盒自己声明的 `agent_id`;资源归属由平台创建 run 时确定。

这样即使技能代码尝试改参数,它拿到的能力也不足以跨 Agent 访问。

---

## 对架构提案意味着什么

任何扩展沙盒的提案,必须先回答:

1. 新能力的授权范围是否缩到**单 Agent / 单 Skill / 单 run / 单对象**中适用的最小一级?
2. 沙盒能不能靠改路径、端点、回调参数或资源 ID 越界?
3. 信号/产物能否由平台在执行流中解析,避免把通用后台 token 交给沙盒?
4. 凭证和临时文件会不会进入对话、日志、artifact 或持久工作区?
5. 能力未配置、领取失败或校验失败时,是否**拒绝启动或明确失败**,而不是静默降级成更危险的路径?

隔离是这层的**首要验收条件**。做不到最小能力范围和失败关闭,提案就不该往下走。

---

## 公开文档的边界

公开架构需要讲清**能力、信任边界和验收原则**,人和 Agent 才能正确审议提案;但不应公开部署资产或可直接利用的操作材料。本文因此不列出内部地址、云账号/存储桶标识、密钥或 token、生产配置值和未修复漏洞的利用步骤。这些不是理解架构所必需的内容。

---

*这一篇属于「深入原理(架构师向)」。想理解技能怎么发布/安装,看《[Skill 市场](31-skills.md)》;想知道怎么把一层新能力 slot 进平台,看《[扩展模型](46-extending.md)》。*
