mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
162 lines
8.4 KiB
Markdown
162 lines
8.4 KiB
Markdown
# ADR-0162:首个 Trusted Built-in Run Read Tool Adapter
|
||
|
||
- 状态:Accepted
|
||
- 日期:2026-07-26
|
||
- 关联:ADR-0133、ADR-0154 至 ADR-0161;RFC D-131/D-149/D-150/D-151
|
||
|
||
## 背景
|
||
|
||
ADR-0158 已要求 Tool adapter 只能在 durable start barrier 提交后启动;ADR-0159 至
|
||
ADR-0161 又把 input/preview 变成不可变 Artifact,并把每个 start 与 exact Artifact
|
||
pair 以双方言关系事实绑定。但此前仍没有任何可执行 adapter:
|
||
|
||
- binding registry 只保存受审元数据,没有函数或 `execute` seam;
|
||
- Artifact 可以被领域函数解封,却没有一个 start-first 的产品无关执行门;
|
||
- current Project snapshot、binding、Artifact、key 和 output schema 还可能由调用方以
|
||
多套对象分别注入;
|
||
- response-loss 后没有 adapter-specific 证据说明能否安全重试。
|
||
|
||
首个实现必须验证整条安全链,同时不能为了一个内置只读能力增加 workspace package、
|
||
数据库表、连接、timer 或低配设备常驻成本。
|
||
|
||
## 决策
|
||
|
||
### 1. 执行 authority 只从两个现有 package subpath 暴露
|
||
|
||
在 `@qinglong/runtime-core` 内新增:
|
||
|
||
- `/trusted-tool-execution`:durable-start 后的通用执行门和 executable adapter
|
||
registry;
|
||
- `/builtin-run-read-tool`:首个 `qinglong.run.get@1.0.0` adapter。
|
||
|
||
它们不从 `@qinglong/runtime-core` root 聚合导出,不新增 workspace package、第三方
|
||
依赖、bin、进程、socket、连接池、timer、watcher 或缓存。Package 资源、Agent 输入、
|
||
HTTP/MCP caller 和 plan 均不能运行期注册 executable adapter。
|
||
|
||
`TrustedToolExecutionAdapterRegistry` 由受信 Profile composition 一次性构造。每个
|
||
adapter 必须携带 exact Project/snapshot-specific binding 和 Profile;registry 在构造
|
||
时复验 current binding、Definition effect、`builtin_in_process` execution class,并
|
||
冻结 binding/Profile/recovery mode 与已绑定的 execute seam。执行函数不再额外接受一套
|
||
Definition/binding registry,避免两个信任源。
|
||
|
||
### 2. 顺序固定为 barrier → current binding → Artifact → key → execute
|
||
|
||
`executeTrustedToolAfterStart(startId, dependencies)` 固定执行:
|
||
|
||
1. 从 `ToolExecutionStartBarrierRepository.findByStartId` 读取并规范化 durable
|
||
barrier;缺失、损坏或 identity 漂移失败关闭;
|
||
2. 以 barrier 的 `bindingDigest` 在 executable registry 持有的 current Project
|
||
binding registry 中反查 exact Tool,再复验 Project、snapshot、Definition、
|
||
adapter/redaction/audit contract、Profile、execution class 与 timeout;
|
||
3. 读取 exact input Artifact,复验 reference、Project、action、requester、Tool、
|
||
seal time 和由当前 Definition 重新计算的 invocation action digest;
|
||
4. 按 Artifact `keyId` 解析一次 owned 32-byte key,AES-256-GCM 解封,并再次通过同一
|
||
current Tool Definition Registry;
|
||
5. 调用 frozen adapter seam,并使用同一 Registry 对 output 做 schema 与 256 KiB
|
||
总上限规范化;
|
||
6. 返回 domain-separated output/result digest 绑定的低敏内存结果。
|
||
|
||
key provider 返回的 owned key 在成功和失败路径都会覆零;Artifact 解封函数继续覆零
|
||
内部 key、nonce、ciphertext、auth tag、AAD 和 plaintext byte buffer。JavaScript
|
||
已经解析出的字符串/对象无法提供物理内存擦除保证,因此 adapter 不得不必要地保留
|
||
input 引用,后续 Secret-bearing adapter 仍需独立内存与进程隔离评审。
|
||
|
||
deadline 在解封前检查,并用一次只在当前调用存续期间存在的 timer race 约束 caller;
|
||
adapter 返回后再复验时钟。race 超时不被宣传为已取消底层 I/O;首个 adapter
|
||
只有一次只读点查且无副作用,迟到结果会被丢弃。未来 process/MCP/HTTP/write adapter
|
||
必须提供真正的 cancellation/fencing contract,不能照搬本实现。
|
||
|
||
### 3. `qinglong.run.get` 是严格只读、Project-scoped 的首个 adapter
|
||
|
||
受审 Definition 固定:
|
||
|
||
- Tool:`qinglong.run.get@1.0.0`;
|
||
- effect/risk:`read/low`;
|
||
- required permission:`run.read`;
|
||
- input:仅 `{runId}`,最长 128;
|
||
- timeout:5 秒;
|
||
- adapter:`builtin.qinglong.run-get@1.0.0`;
|
||
- authority:仅 `database.read`;
|
||
- Profile:由 binding 显式选择,首版可用于 edge、standalone 和 cluster-control。
|
||
|
||
该 Definition 仍必须作为 `qinglong` Package namespace 的普通 Definition 进入 current
|
||
Project snapshot;平台代码不会把它秘密插入 registry。binding factory 只接受与受审
|
||
Definition 完全一致的 snapshot entry。
|
||
|
||
adapter 只取得 `RunRepositoryReader.findRunById`,不取得 transaction、mutation、
|
||
filesystem、network、process、Secret 或 Artifact authority。返回值只包含:
|
||
|
||
- found;
|
||
- Run/Task revision identity;
|
||
- status/version/event sequence/priority;
|
||
- execution origin/owner;
|
||
- create/queue/start/finish 时间。
|
||
|
||
request ID、trigger identity、input/output ref、executor handle、PID、lease、error 和
|
||
其他敏感或可扩权字段不得进入 output。missing 与 cross-Project Run 都返回相同的
|
||
`{found:false}`。
|
||
|
||
### 4. 首个 post-start recovery 只证明“只读可安全重试”
|
||
|
||
`inspectTrustedToolExecutionRecovery` 只读取 durable barrier 并复验 exact executable
|
||
binding。对 `retry_safe_read` adapter 返回
|
||
`qinglong/trusted-tool-execution-recovery-evidence@v1`,其中 disposition 为
|
||
`retry_safe`、reason 为 `read_only_no_side_effects`,并绑定 barrier/adapter digest
|
||
与 inspection time。
|
||
|
||
inspection 不读取 input Artifact、不解析 key、不解密、不调用 adapter,也不声称已有
|
||
exact output。重试可能观察到更新后的 Run 状态,但不会重复外部副作用。结果
|
||
Artifact/receipt 的持久化、exact response replay 和 StepRun completion 同事务仍是后续
|
||
门禁。
|
||
|
||
## 低配与集群影响
|
||
|
||
- workspace importer 仍为 21,不新增依赖;
|
||
- Edge/Standalone 空闲时零新增资源;每次调用只多一次 barrier 点查、一次 Artifact
|
||
点查、一次 key resolve、一次 Run 点查、一个调用期 deadline timer 和有界 AES/JSON
|
||
工作;
|
||
- Cluster 复用现有 PostgreSQL repositories,不增加表、role、Pool、队列或服务;
|
||
- adapter registry 是请求/组合根显式构造的最多 128 项小集合,不是跨 Project 全局
|
||
cache,也不建立 watcher;
|
||
- recovery inspection 不触碰密文和 Run repository。
|
||
|
||
## 被否决方案
|
||
|
||
1. **在 plan/admission 中保存函数或 module path**:绕过受信 composition 和供应链
|
||
审查。
|
||
2. **执行函数同时接受 binding registry 与 executable registry**:形成可漂移的双信任
|
||
源。
|
||
3. **没有 barrier 时直接读取 Artifact 执行**:允许越过同事务 start gate。
|
||
4. **只比 Artifact ID**:不能证明 digest、Project、action、requester、Tool 和当前
|
||
Definition。
|
||
5. **把内置 Definition 隐式插入每个 Project registry**:破坏 Project snapshot 的
|
||
完整 source vector。
|
||
6. **为首个 adapter 新建 package**:没有独立部署、依赖或权限生命周期。
|
||
7. **用 Promise race 宣称可取消任意 adapter**:底层 I/O 仍可能继续,不能作为副作用
|
||
fence。
|
||
8. **把只读 recovery evidence 当完成 receipt**:无法重放 exact output。
|
||
|
||
## 验证
|
||
|
||
- runtime-core type/build gate 通过;
|
||
- runtime-core 全量 319/319,新增 6 项覆盖:
|
||
1. durable start 后才解封并执行真实 Run repository 点读;
|
||
2. missing barrier 和错误 key 在 adapter 前失败关闭,owned key 覆零;
|
||
3. stalled read 对 caller 有 deadline 上限且 owned key 仍覆零;
|
||
4. missing/cross-Project 输出不可区分且不泄露低敏投影外字段;
|
||
5. recovery inspection 不读取 Artifact/key、不调用 adapter;
|
||
6. Definition/binding 漂移拒绝,root 不导出 execution authority;
|
||
- source gate 证明通用执行门不导入 filesystem、process、network、worker thread;
|
||
- package 数和依赖树不变。
|
||
|
||
## 后续门禁
|
||
|
||
1. 在 edge/standalone 与 cluster-control 产品 composition 中请求驱动装配
|
||
`qinglong` built-in Definition、binding、repository、key provider 和 start→execute
|
||
调用链;
|
||
2. 新增 immutable result Artifact/receipt,并与 StepRun terminal transition、
|
||
RunEvent、Trace/Audit completion 在双方言中原子绑定;
|
||
3. 为 key lost、Artifact/关系损坏和 result ambiguity 提供 inspect/manual recovery;
|
||
4. 增加 crash/response-loss 的 SQLite、PostgreSQL 真库和 physical HA 故障注入;
|
||
5. 完成物理 Edge idle/fault/scale 证据后,才允许产品 execution admission 开放。
|