mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
135 lines
6.9 KiB
Markdown
135 lines
6.9 KiB
Markdown
# ADR-0163:原子加密 Tool 成功完成协议
|
||
|
||
- 状态:Accepted
|
||
- 日期:2026-07-26
|
||
- 关联:ADR-0156 至 ADR-0162;RFC D-149 至 D-152
|
||
|
||
## 背景
|
||
|
||
ADR-0162 已能在 durable start barrier 后执行首个受信只读 Tool,并返回经过当前
|
||
Definition Registry 规范化的内存结果,但该结果还不是可恢复的 durable fact:
|
||
|
||
- 进程在 adapter 成功后、StepRun 完成前崩溃时,无法证明 exact output 是否已经完成;
|
||
- 只更新 StepRun 会让 result、RunEvent 和 mutation ledger 出现部分提交;
|
||
- 把明文 output 存入 StepRun、RunEvent 或普通 JSON 会扩大数据库泄露面;
|
||
- 只读 `retry_safe` 只能说明重试不会重复副作用,不能重放同一次 exact result。
|
||
|
||
该缺口必须同时适配单 SQLite 的低配路由设备和 PostgreSQL 多副本节点,且不能为一个
|
||
协议继续增加只有单文件的 workspace package。
|
||
|
||
## 决策
|
||
|
||
### 1. Result Artifact 是密文,不是 StepRun payload
|
||
|
||
`@qinglong/runtime-core/tool-execution-completion` 定义三个 exact-shape v1 contract:
|
||
|
||
- `qinglong/tool-execution-result-artifact@v1`;
|
||
- `qinglong/tool-execution-completion@v1`;
|
||
- `qinglong/tool-execution-completion-command@v1`。
|
||
|
||
成功 output 必须先通过同一 current Tool Definition Registry 规范化,再以
|
||
AES-256-GCM 密封。AAD、Artifact digest 和 completion digest 绑定:
|
||
|
||
- Project、Run、StepRun 与 start barrier;
|
||
- Tool、adapter、output digest 与 execution result digest;
|
||
- key ID、算法、密文长度和完成时间。
|
||
|
||
数据库只保存密文 Artifact,不保存 output 明文。output 上限为 256 KiB,Result
|
||
Artifact JSON 上限为 384 KiB,Completion JSON 上限为 24 KiB。key provider 仍只
|
||
返回 owned 32-byte key;加解密持有的 byte buffer 在成功和失败路径都覆零。
|
||
|
||
### 2. 成功完成与 StepRun/Run 事实同事务提交
|
||
|
||
双方言 repository 的 `commit(command)` 必须在一个事务中完成:
|
||
|
||
1. 读取并复验 exact durable start barrier;
|
||
2. 复验 start mutation 对应的 Run version/event sequence;
|
||
3. 锁定并复验当前 `running` Tool StepRun 与非 terminal Run;
|
||
4. 把 StepRun 原子迁移为 `succeeded`,且 `outputRef` 必须等于 Result Artifact ID;
|
||
5. CAS 递增 Run version/event sequence;
|
||
6. 插入 RunEvent、StepRunMutation;
|
||
7. 插入不可变 Result Artifact 与 Completion record。
|
||
|
||
任一身份、digest、version、event sequence、时间或 output reference 漂移均失败关闭。
|
||
同一个 start、Artifact、mutation、event 或 `(Run, StepRun, completed version)` 不能
|
||
绑定到第二组事实。完整命令重放返回 `existing`;部分相同或内容漂移返回 conflict。
|
||
|
||
SQLite 使用 `BEGIN IMMEDIATE`。PostgreSQL 使用既有 SERIALIZABLE 有界重试事务,
|
||
并对 StepRun/Run 使用行锁;读取 `jsonb` barrier 后必须先通过领域 normalizer
|
||
规范化,不能依赖 PostgreSQL 保留 JSON 对象键顺序。
|
||
|
||
### 3. 表与权限保持追加式
|
||
|
||
SQLite migration 0061 新增 `ToolExecutionCompletions`,0062 将 local capability
|
||
推进至 v31。PostgreSQL migration `pg-0033-tool-execution-completions` 新增
|
||
`ql3.tool_execution_completions`,将 `control-core` 推进至 v32。
|
||
|
||
单表同时保存 Artifact JSON、Completion JSON、关键 mirror 列、复合唯一约束和到
|
||
start barrier、StepRun mutation、RunEvent 的外键。这样避免再为 Result Artifact
|
||
创建一个单用途表和一套不完整提交协议。
|
||
|
||
PostgreSQL `ql3_runtime` 对该表只有 `SELECT, INSERT`;admin、Package manager、
|
||
Package executor、Worker ingress 与 PUBLIC 均无权限。repository 不提供 update 或
|
||
delete。
|
||
|
||
### 4. 不新增 package,按部署边界使用显式 subpath
|
||
|
||
共享协议留在现有 `runtime-core`,SQLite/PostgreSQL adapter 分别留在现有
|
||
`local-sqlite` 与 `cluster-postgres`。三个入口均为显式 subpath,不从 package root
|
||
聚合导出。
|
||
|
||
workspace 继续保持 21 个 QL3 package,没有新增第三方依赖、进程、连接池、timer、
|
||
watcher、socket 或后台队列。Edge/Standalone 每次成功调用只增加一次有界 AES/JSON
|
||
工作和一个现有 SQLite 事务;Cluster 复用现有 PostgreSQL Pool。
|
||
|
||
## 当前边界
|
||
|
||
本 ADR 只关闭 `running → succeeded`:
|
||
|
||
- `failed`、`timed_out` 和 cancellation 的 Tool-specific result/error envelope 尚未
|
||
建立;
|
||
- trusted adapter 的产品 composition 尚未把 execute→seal→commit 串成公开路径;
|
||
- Result Artifact key 的持久 catalog、rotation、retention/rekey 仍由后续决策完成;
|
||
- key lost、Artifact/关系损坏和 result ambiguity 仍需 inspect/manual recovery;
|
||
- HA 门已验证 schema/ACL/复制与既有领域提交响应丢失,但尚未对新的
|
||
Tool completion transaction 注入 COMMIT-response-loss。
|
||
|
||
因此 Tool execution production admission 继续关闭。
|
||
|
||
## 被否决方案
|
||
|
||
1. **把明文 output 写入 StepRun 或 RunEvent**:扩大敏感结果的持久化与读取面。
|
||
2. **先保存 Artifact、再更新 StepRun**:崩溃窗口会产生无完成事实的孤立结果。
|
||
3. **只保存 output digest**:无法在重启或响应丢失后重放 exact output。
|
||
4. **复用 invocation Artifact 表**:输入与结果具有不同身份、生命周期和授权方向。
|
||
5. **拆成新的 result package**:没有独立部署、依赖或权限生命周期,只会继续增加
|
||
单文件 package。
|
||
6. **赋予 runtime UPDATE/DELETE**:破坏 append-only 证据边界。
|
||
7. **直接比较 PostgreSQL `jsonb` 字符串**:对象键顺序不是语义,可能误报 conflict。
|
||
8. **把成功协议宣传为全 terminal completion**:会掩盖 failed/timed_out 的恢复缺口。
|
||
|
||
## 验证
|
||
|
||
- runtime-core:320/320;
|
||
- local-sqlite:117/117,真实内存 SQLite 覆盖原子提交与 exact replay;
|
||
- cluster-postgres unit:178 pass / 1 条件 skip;
|
||
- 全新 PostgreSQL 18 六角色真库 integration:41 pass / 1 条件 skip,直接覆盖加密
|
||
Result Artifact、`running → succeeded`、Run version/event sequence、exact replay
|
||
和无明文持久化;
|
||
- PostgreSQL 18.4 arm64 双节点物理 HA:streaming、`remote_apply`、旧主隔离、晋升、
|
||
`pg_rewind` 回归只读、双控制副本恢复及全部报告 gate 通过;
|
||
- 21 个 QL3 workspace package 全量 build/test 通过;
|
||
- edge import、cluster dependency、edge artifact 审计通过;edge artifact
|
||
3,554,497 bytes、40 个加载模块、RSS 增量 10,731,520 bytes;
|
||
- 临时 PostgreSQL 容器已删除,无测试资源残留。
|
||
|
||
## 后续门禁
|
||
|
||
1. 在 edge/standalone 与 cluster-control composition 中装配
|
||
start→execute→seal→commit,并保持公开入口默认关闭;
|
||
2. 定义 failed/timed_out 的低敏、可恢复、不可伪造完成 envelope;
|
||
3. 增加 SQLite crash、PostgreSQL COMMIT-response-loss 与 Tool completion 的物理 HA
|
||
故障注入;
|
||
4. 建立 result key catalog/rotation/retention/rekey 与人工恢复流程;
|
||
5. 完成物理 Edge idle/fault/scale 证据后再评审 production admission。
|