mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
112 lines
7.3 KiB
Markdown
112 lines
7.3 KiB
Markdown
# ADR-0116:受审 Worker POSIX Executor 与 Upload-before-completion 闭环
|
||
|
||
- 状态:Accepted(ADR-0117 已补 HTTPS/数据库闭环,ADR-0121 已补生产执行面装配;retention 与完整 Worker 产品生命周期仍默认关闭)
|
||
- 日期:2026-07-23
|
||
- 关联 RFC:QL-RFC-0001 D-14、D-17、D-24、D-26、D-68、D-110、D-114、D-115
|
||
- 关联 ADR:ADR-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` 才返回 rejected;spawn 后 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 observation;launcher 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 completion,command 携带 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/cancellation,ADR-0120 已补具体 S3-compatible store,ADR-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 receipt,callback token
|
||
不进入用户环境。
|
||
2. spawn barrier 漂移零 spawn;spawn 后 identity 失败保持 unknown。
|
||
3. `launching` 持久化 start time,launcher receipt 与最终 state exact;重启不二次 spawn。
|
||
4. completion coordinator 可从无 executor handle 的 durable `launching` 回执恢复。
|
||
5. capability mismatch 在 upload 前拒绝并保留全部证据。
|
||
6. 文件 source 固定 chunk 流式读取并认证 truncation fact,不枚举目录。
|
||
7. 测试证明 upload 完成先于 completion,inbox terminal ACK 先于 receipt remove。
|
||
8. upload 失败时不调用 completion、不删除 receipt,并关闭只读 descriptor。
|
||
9. 未新增 workspace package、数据库、schema、timer 或 Worker 常驻连接。
|