Sandboxes & promote
安全地将 AI 编码智能体对准你的生产数据库——被捕获、被观测、通过一道闸门提升,且可逆。
Agent-Safe Change Control 的一部分。本文所述的
keon sandbox和keon ledger命令以及控制台 Sandboxes 视图都 已上线——需要较新的 keon(cli-v0.1.46+)。
Kisenon 让你把一个数据库交给编码智能体(Claude Code、Cursor 或你自己的), 任其自由更改——而不必冒险危及生产环境。沙箱是 你分支的一份每次运行的 fork,配有受限凭据、一份实时操作日志, 以及一个服务端的提升步骤。本页覆盖完整的循环:捕获 → 工作 → 提升 → 落地(可逆地)→ 证明。
Why it's safe
两项保证,二者皆为确定性的——信任路径中没有 LLM 介入:
- 智能体无法触及生产环境。 它以受限的、
非超级用户的凭据连接到一份 fork。它绝不持有能
写入
main的凭据。提升在服务端运行,并且只应用那些 已在沙箱中通过验证的更改。 - 你能看清它究竟做了什么。 每一条语句都被归因并 流式推送到一个只读控制台——是真实的 SQL,而非摘要。
The loop
keon sandbox run \
--migrate "alembic upgrade head" \
--verify "pytest tests/db"
# → green/red verdict + schema diff + a sandbox you can inspect
keon sandbox promote <id> # cp applies the validated changes to main智能体始终掌控着这个循环;正是那道边界使其变得安全。
Durable capture
操作日志是提升的事实来源,因此它必须完整。
每个沙箱携带一个 capture_state —— ok 或 lost。如果捕获曾经
受损,状态会翻转为 lost 并 保持 在那里:一个 lost 沙箱
无法提升(409 sandbox_capture_lost),并且绝不会悄悄应用
部分变更。没有自动修复——丢弃它,并在一个
全新的沙箱中重新运行该工作。keon sandbox get / list 会显示捕获健康状况。
Promote: self-serve or human-approved
每个项目选择一种 promote_mode:
| 模式 | 由谁提交到 main |
|---|---|
self(默认) | 智能体在其检查通过后自行提升——在其边界之内。 |
human | 智能体提议;由 owner/admin 审查 diff + 操作日志后点击 Approve。 |
Blast-radius dry-run
在你提升之前,你可以请求平台 测量 提升会做什么 ——真实的锁级别、真实的持锁时长、真实的行数—— 而不是从语句文本去猜测:
keon sandbox dry-run <id>平台将沙箱的确切语句集对父分支的两个短期存活的
一次性 fork 进行重放——一个在父分支的当前 HEAD(在那里测量锁、
时长和行数),一个在沙箱的 fork 点(分歧
基线)——然后销毁两者。它绝不触及 main,且
智能体绝不收到对任一 fork 的凭据。该试运行是 建议性的:
它产出报告,绝不阻断提升。
它还回答了一个静态 diff 无法回答的问题:现实是否在
智能体脚下移动了? 任何在父分支 HEAD 处报错、或影响与
fork 点不同行数的语句都会被暴露为一个 冲突 —— 例如
一条 UPDATE ... WHERE id = 2,其目标行在 fork 之后已在 main 上被删除。
已完成的报告携带:
| 字段 | 含义 |
|---|---|
statements[].lock_level | 该语句在提升事务中新获取的最强锁(例如表重写时的 AccessExclusiveLock)。 |
statements[].lock_wait_est_ms | 在 head fork 上测量到的执行时间——该锁在 main 上被持有时长的下限。 |
statements[].rows_measured | 在 head fork 上受影响的行数(DDL 为 0)。 |
statements[].divergence | 当语句在 HEAD 处的行为与 fork 时不同时,为 {kind:"error"} 或 {kind:"row_count"}。 |
rollup.conflicts | 每一处分歧,外加一个 baseline_unavailable 标记(如果基线通道无法运行)——因此一道以"无冲突"为条件的闸门在测量降级时会 失败即关闭。 |
rollup.max_lock_level / rollup.duration_ms_total | 聚合的锁强度与总重放时间。 |
capture_advanced / parent_head_lsn | 陈旧性信号:该报告是一个快照,重新运行成本很低。 |
端点是 POST /v1/sandboxes/{id}/dry-run(启动一次异步运行,202)
和 GET /v1/sandboxes/{id}/dry-run(最新记录)。一次失败的试运行
携带一个机器可读的 error_code
(capture_fence_failed、incomplete_capture、fork_failed、
compute_unreachable、replay_infra_failed、timeout)。沙箱详情页上的
Blast radius 面板在控制台中渲染同一份报告。
Undo a promote
提升是可逆的。在 cp 将验证过的语句应用到
main 之前,它锚定父分支提升前的确切状态(一个 LSN)。一条
命令即可将分支回滚到那个锚点:
keon sandbox undo <id> # restore the parent branch to its pre-promote state父分支的连接字符串不变——客户端重新连接到同一
端点。撤销是一个 owner/admin 操作;智能体能力密钥会被拒绝
(403 scope_insufficient),因为一次撤销会丢弃锚点之后落到分支上的
任何内容。
keon sandbox get <id> 携带一个 undo 对象——undoable、restored_lsn、
undone_at,以及在它无法运行时的 blocked_reason
(superseded_by_later_promote、already_undone)。撤销只针对 最近的
一次提升,并在锚点消失后以 409 拒绝(sandbox_not_promoted、
undo_anchor_missing、undo_anchor_invalidated)。
只要锚点有效且在你的存储保留期内,它就保持可用
——没有单独的倒计时。
撤销按 LSN 恢复:它回滚锚点之后的 一切,而 不只是被提升的变更——包括直接写入和该分支上后来的 智能体会话。参见 Common pitfalls。
Emitted migrations
每一次落地的提升都会发出一份确定性的 up/down SQL 迁移——没有 LLM, 从捕获的模式变更生成,并在提供之前于一次性 fork 上经过 往返验证。获取它:
keon sandbox migration <id> --out ./migrations --wait该制品携带 up 和 down SQL(files[],每个带一个 role)、一个
coverage 标志(full 或 partial)、down_fidelity、任何 uncovered_seqs、
一个 validation 结果,以及一个 sha256。终态是 validated、
validation_failed、emission_failed 和 no_schema_changes。v1 发出
sql;GET /v1/sandboxes/{id}/migration(以及 .../migration/files/{role})
提供它。发出在提升落地之后运行,绝不阻断或回滚
它——一次纯数据的提升只会报告 no_schema_changes。
Signed ledger
每一次提升和撤销都会向一个每项目的账本追加一条签名的、哈希链接的 记录——一份对所变更内容的可离线验证的证明。验证不需要 网络,且不信任 Kisenon:
keon ledger list --project <id> # the chain (reports chain_ok)
keon ledger export <id> --out att.json # one attestation document
keon ledger verify att.json # offline; exit 0 iff valid
keon ledger keys --save # pin the signing keys you trustkeon ledger verify 检查 Ed25519 签名和哈希链,并
报告精确的失败(signature_invalid、statement_chain_mismatch、
seq_gap、key_untrusted、chain_broken、…);信任是三态的
(trusted / untrusted / unverified)。路由:GET /v1/sandboxes/{id}/ledger、GET /v1/projects/{projectId}/ledger、GET /v1/ledger/keys。
如果 cp 未配置签名密钥,提升仍会成功但是 未签名的(
ledger_enabled: false)——签名不是无条件的。
What a sandbox is not
- 不是代码沙箱——它限定的是你的数据库,而非智能体的进程。
- 不是 LLM 评审——捕获、重放与审查都是逐字节确定性的。
Try it
在 kisenon.com 试用,并查看 快速开始。