Files
qinglong/docs/adr/ADR-0116-reviewed-worker-posix-executor-and-upload-before-completion.md
T

112 lines
7.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ADR-0116:受审 Worker POSIX Executor 与 Upload-before-completion 闭环
- 状态:AcceptedADR-0117 已补 HTTPS/数据库闭环,ADR-0121 已补生产执行面装配;retention 与完整 Worker 产品生命周期仍默认关闭)
- 日期:2026-07-23
- 关联 RFCQL-RFC-0001 D-14、D-17、D-24、D-26、D-68、D-110、D-114、D-115
- 关联 ADRADR-0021、ADR-0069、ADR-0111、ADR-0115
## 背景
ADR-0115 交付了 Worker 本地日志 spool 和 output ownership,但仍缺真实进程启动器、可认证
完成回执和 upload-before-delete 顺序。若 Worker 复制一套 launcher,会让本机与远端执行的
安全修复漂移;若先提交 Run 终态再上传日志,控制面成功而网络失败会形成不可恢复的空
Artifact;若上传后立即删除本地文件或回执,inbox CAS 崩溃又会丢失重放证据。
另一个关键窗口是 `launching` 已落盘、进程已经 spawn、`started` 尚未写入时的 Worker 崩溃。
仅在 `started` 保存启动时间,回执无法与 durable spawn authority 做 exact 匹配,恢复只能永久
阻断或错误放宽认证。
## 决策
### 1. 复用 local-process 边界,不新增 package
`@qinglong/worker-runtime/posix-executor` 依赖现有 `@qinglong/local-process`。这是有意的单向
依赖:local-process 拥有受审 POSIX launcher、verified-fd 执行、completion receipt、durable
process identity 和 OS 路径规则;Worker 只拥有 Session/Lease/inbox 策略。它们具有真实的
platform/供应链边界,符合 D-85,不复制 launcher,也不新增只有一两个文件的 package。
Worker Executor 只接受带私有 file-plan capability 的 output。它在 spawn 前重读同一 inbox
精确校验 offer、Run、Attempt、callback sequence/token digest、log Artifact 和启动时间;随后
关闭父写 descriptor,让受审 launcher 以 verified fd 和独立进程组启动用户命令。明确
`no_spawn` 才返回 rejectedspawn 后 identity 捕获失败为 unknown,禁止上层上报 start-failure。
当前 timeout 仍 fail closed:只要 execution revision 携带 `timeoutMs`Executor 在 durable
spawn barrier 前拒绝。没有 durable timeout/cancellation evidence 时不得用进程内 timer 冒充。
### 2. 启动时间属于 durable pre-spawn authority
Processor 在交接 output 前把 `executorStartedAtMs` 与 log ID、callback sequence/digest 一起写入
`launching`。local-process 接受可选的上层 durable start timestamp,但必须验证它是安全整数且
不晚于本地 launch observationlauncher journal、receipt、Executor result 和最终 inbox 必须
使用同一个值。
因此崩溃发生在 spawn 后、`started` CAS 前时,可信 completion receipt 仍可从 `launching`
`launch_outcome_unknown` 恢复,不需要裸 PID、日志内容或宽松时间范围猜测。该状态机变化影响
`processOnce``launch``acceptOffer` 三条流程,必须由 exact transition 与 restart 测试门禁。
### 3. 回执认证只消费原始 capability,不传播它
Worker inbox 只持久化 32-byte callback capability 的 SHA-256。launcher receipt 可短暂保存
base64url 原文,协调器读取后必须 canonical decode 为恰好 32 bytes,以 constant-time 比较其
SHA-256,并精确匹配 Run、Attempt、callback sequence 和 durable start time;局部 buffer 随后
清零。上传、completion command、日志、诊断和 inbox 都不得携带 token 原文。
非法、未来时间或 authority 不匹配的回执不能触发上传或 completion。当前实现保留回执供后续
隔离/运维,不把损坏证据推导成用户任务失败。
### 4. 日志使用固定内存流,不整文件载入
现有 `WorkerFileLogArtifactAllocator` 同时实现只读 source。它按 opaque ID 定位单个文件,使用
`O_NOFOLLOW` 后复验 owner、单 link、权限、fd/path inode 和 root/shard identity;不扫描目录。
内容以最多 64 KiB chunk 单次消费,Edge 与 Node 共用协议而使用各自 4/64 MiB 总上限。
launcher 的 immutable truncation fact 以同样私有路径读取,并精确匹配 Run、Attempt、log ID、
Profile maximum 和 bounded timestamp。fact 缺失表达 `unknown`,不能伪装成未截断。
### 5. 顺序固定为 upload、completion、inbox ACK、receipt cleanup
`@qinglong/worker-runtime/completion-coordinator` 的可重放顺序固定为:
1. 重读并规范化唯一 inbox authority,复验当前 Worker Session 与 Run Lease 未过期;
2. 读取并认证 completion receipt
3. 打开 exact log Artifact,以流式 SHA-256 和 byte count 校验 uploader ACK
4. 只有 durable upload ACK 后才提交 lease-fenced completioncommand 携带 Artifact ID、长度、
digest 和 truncation 状态;
5. completion applied/exact replay 后 CAS 写 `completion_acknowledged`
6. 只有该本地 terminal barrier 已耐久化,才 best-effort 删除 receipt。
本协调器绝不删除本地日志。Artifact 只能由后续 retention 在已证明 upload/control-plane ACK 且
超过策略保留期后清理。任一步网络/CAS 失败都保留可重放的 receipt、inbox 和 spool;上传端与
completion 端必须用 ID/digest/fence 提供精确幂等。
当前交付的是 Worker 侧真实文件/进程实现和严格 adapter port。ADR-0117 已补中央 Artifact store
port、认证 HTTPS upload/completion endpoint 与 PostgreSQL 终态事务;ADR-0118 已补失租停止与
durable timeout/cancellationADR-0120 已补具体 S3-compatible storeADR-0121 已补默认关闭的
production execution-plane composition。retention、Session/credential 产品流程与部署入口仍未装配,
因此完整 Worker Profile 继续默认不可达。
## 被否决的替代方案
1. **为 Worker launcher 新建 package 或复制 shell**:扩大碎片并产生两套安全修复源。
2. **`started` 后才保存 start time**:spawn 后崩溃无法严格认证回执。
3. **对 base64url 字符串直接 hash**:与 Processor 保存的原始 32-byte capability digest 不一致。
4. **completion 后再上传**:终态成功但日志永久留在离线 Worker。
5. **upload 返回成功但不验证消费字节/digest**:空读或部分读可冒充 durable Artifact。
6. **上传后删除 spool**inbox/completion CAS 窗口丢失重试源。
7. **整文件读入 Buffer**64 MiB Node 配额会放大路由器和并发 Worker 峰值内存。
8. **为完成协调器新建 workspace package**:单一 Worker consumer、同部署/依赖/权限闭包,不满足
D-85;使用现有 package subpath。
## 验收证据
1. reviewed-fd Worker launch 产生 exact durable handle、日志和 completion receiptcallback token
不进入用户环境。
2. spawn barrier 漂移零 spawnspawn 后 identity 失败保持 unknown。
3. `launching` 持久化 start timelauncher receipt 与最终 state exact;重启不二次 spawn。
4. completion coordinator 可从无 executor handle 的 durable `launching` 回执恢复。
5. capability mismatch 在 upload 前拒绝并保留全部证据。
6. 文件 source 固定 chunk 流式读取并认证 truncation fact,不枚举目录。
7. 测试证明 upload 完成先于 completioninbox terminal ACK 先于 receipt remove。
8. upload 失败时不调用 completion、不删除 receipt,并关闭只读 descriptor。
9. 未新增 workspace package、数据库、schema、timer 或 Worker 常驻连接。