mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-20 16:07:11 +08:00
feat(ql3): add opaque cluster environment bundle delivery
This commit is contained in:
@@ -120,6 +120,34 @@
|
||||
migration receipt、HA promotion replay 和固定低性能设备证据仍是后续门禁。ADR-0491 因此继续
|
||||
Proposed。
|
||||
|
||||
- D-401/ADR-0496(前置切片已验收,D-401 整体仍进行中):在真正执行 Cluster Task/Trigger mutation 前,先关闭一个原架构会泄漏
|
||||
Legacy Env 名称的缺口。`qinglong/command@v1` 与 Cluster execution revision 只增加可选、同
|
||||
Project、固定 version 的 `environmentBundleRef`;PostgreSQL Task/plan 历史只保存该引用,不保存
|
||||
bundle schema、Env name/value、key ID 或 provider path。新的纯数据
|
||||
`qinglong/environment-bundle@v1` 固定 1~256 项、单值 16 KiB、最终 name+value 64 KiB、carrier
|
||||
96 KiB,并拒绝重复/保留名称、NUL、未知字段和非 canonical authority。ADR-0495 计划中的
|
||||
`effectiveBindingCount` 实现 cap 也从误用的 100,000 source-row 上限收紧到 256。
|
||||
|
||||
Remote Worker Secret delivery 升级为 v2,将最多 64 个普通 `secretRefs` 与最多一个
|
||||
`environmentBundleRefs` 分栏且禁止角色重叠;普通值仍为 16 KiB/项、64 KiB 合计,bundle carrier
|
||||
为 96 KiB,response cap 为 256 KiB。PostgreSQL 在同一个 Session/Lease/Attempt/offer/revision
|
||||
authority fence 中比较两组完整引用;mounted-files provider 只对 bundle authority 放宽单文件
|
||||
carrier 上限,普通 Secret 仍为 16 KiB。Worker 从 durable inbox 内部装配 capability,经共享
|
||||
mTLS client 获取 bundle,只在 Artifact 分配与 spawn 前的内存 materializer 中展开,并对 Task
|
||||
原有环境名称冲突、256 项和最终 64 KiB 再次失败关闭。
|
||||
|
||||
本切片不增加 package、dependency、PostgreSQL migration/table/role、daemon、timer 或 watcher,
|
||||
Local execution 对 bundle 继续拒绝并沿用 D-397 的逐 Secret SQLite authority。它只建立 D-401
|
||||
mutation 的安全数据面前置,不声明 Task/Trigger 已改写、Secret 已由 direct Vault/KMS custody、
|
||||
receipt 已完成或 migration receipt 已在 HA promotion 后重放;这些仍由下一切片关闭。Runtime Core
|
||||
`586/586`、Worker `135/135`、Cluster Control `273 total / 271 pass / 2 skip`、Cluster PostgreSQL
|
||||
`359 total / 356 pass / 3 skip`、18-package clean build/test 与 backend
|
||||
`1568 total / 1566 pass / 2 skip` 均为 0 fail。package/dependency/Edge/service bridge/Local image 与基础
|
||||
Edge/Standalone artifact audit 全部 compatible,workspace 保持 18 packages、无单文件或浅层 package;
|
||||
基础 Edge/Standalone 为 `2,650,564 / 2,650,642 bytes`、324 files、58 modules。PostgreSQL 18.6
|
||||
arm64 physical HA 通过 146 gates、timeline `1→2`,报告 SHA-256 为
|
||||
`0ee2199d0a52a02025bff017a07477d707353d12aefc3811f474e3775f2bb86b`。
|
||||
|
||||
- D-396/ADR-0490(已验收):Run History 不再只有永久 `manual_external`,但也没有被错误实现为 Legacy 日志到 3.0 Run ledger 的回灌。
|
||||
新的 Local adapter 以 ADR-0482 sealed capture bundle 作为 append-only 保全资产:Legacy history 必须逐事实选择 `retain_both`,Target history
|
||||
必须选择 `retain_target`;receipt 只绑定 signed review、application、bundle fingerprint、领域 inventory 与有界 fact counts,不保存表名、Run ID、
|
||||
@@ -5700,7 +5728,7 @@
|
||||
| D-110 | package delivery inbox 必须原位成为唯一 Worker execution authority,不得复制到 legacy/第二 journal。状态以 revision CAS 经过 `accepted → starting_acknowledged → launching → started → running_acknowledged`;callback token 原文只在内存,spawn 前仅持久化 sequence/digest。只有 Executor tagged `rejected` 可证明 no-spawn 并上报 start-failure;异常、未知结果、spawn 后持久化不确定和重启可见 `launching` 全部进入 recovery,绝不自动二次 spawn。Processor 只消费注入 port,不新增 package、timer、数据库或 cluster 依赖 | Accepted(ADR-0111/0121:单 journal transition/page、ACK/spawn barrier、环境闭合、completion capability 擦除、ambiguous spawn recovery、restart no-respawn 与默认关闭的 production execution composition 已实现;完整 Worker 产品生命周期仍关闭) | adapter 双写不能原子覆盖两个文件 authority,旧/新 Offer digest 也不等价;原位状态机把 delivery receipt 与执行事实明确分开,同时让低配节点只维护原有文件/锁资源,让大节点按 Worker 实例扩展 |
|
||||
| D-111 | Remote activation response 必须使用 `qinglong/remote-run-activation@v1` exact schema 与 16 KiB 上限,完整返回 Run/Attempt/Lease/callback fence,拒绝未知字段和空终态 snapshot。Offer 与 Activation 必须共用同一受限 Worker ingress HTTPS client、Agent、mTLS credential provider 与错误/字节预算,不得复制 TLS authority;Worker/event identity 由 path/服务端生成,不在 body 扩权。headless lifecycle 必须默认关闭、显式取得单 journal owner、无自有 timer,并在完整分页 startup reconciliation 前禁止 Pull;任何 recovery fact 都停止接收新工作。不得新增 package | Accepted(ADR-0112/0121:versioned wire、Cluster projection、共享单 Agent client、稳定凭据指纹池键、真实 TLS 1.3 mTLS、authority mismatch 负向测试、显式 lifecycle、分页 reconciliation、coalescing、drain-before-release 与完整 execution graph 已实现;外层 Session/credential/deploy gate 仍关闭) | 裸内部 object 会把 repository 漂移暴露到 wire;两套 HTTPS client 会让路由器重复 socket/证书状态;自动 timer 会让禁用不再等于零资源;扫描未收敛就 Pull 会在 ambiguous spawn 后继续扩大故障面。单 contract、单 socket authority 和 caller-driven tick 同时适配路由设备与实例级水平扩展 Worker |
|
||||
| D-112 | Worker execution materializer 必须从 canonical Offer 提取最小 provider identity,不得把 lease/callback/credential capability、command 或完整 Offer 交给 Secret/Artifact provider。Secret ref 去重后最多 64 个,response 以 ref 显式配对且 exact;单值 16 KiB、解析后环境总量 64 KiB。必须 Secret 全部闭合后才分配 Attempt Artifact;所有失败路径执行幂等 cleanup。materializer、Executor、journal 与 running ACK 必须使用同一 `logArtifactId`。不得持久化 Secret 明文、新增 package/数据库/timer | Accepted(ADR-0113/0114/0121:最小 identity projection、Secret-before-Artifact、exact ref pairing、值/总量预算、dispose、log ID handoff、认证 HTTPS provider、Executor/upload composition 与负向测试已实现;具体 KMS/Vault 和 retention 仍关闭) | 完整 Offer 会扩散 bearer capability;位置数组会静默错配 Secret;先建日志会在 Secret 缺失时制造 orphan;Executor 自建日志 ID 会让控制面与实际输出分叉。最小显式配对和单日志身份同时限制路由器写放大并允许集群 Worker 用 provider 实现水平扩展 |
|
||||
| D-113 | Remote Worker Secret delivery 必须使用 `qinglong/remote-secret-delivery@v1` exact batch wire:最多 64 KiB request、128 KiB response、64 个唯一 Project-bound ref、单值 16 KiB且值总量 64 KiB。控制面必须在 Attempt advisory lock 下用 PostgreSQL 时钟复验认证 Worker/Session、Run/Attempt starting、Lease token/generation/version/expiry、offer、pinned revision、execution digest 与完整 ref 集合,成功后才调用 plaintext provider;旧 Session/Lease/version/Attempt 重放必须拒绝,同一有效 fence 允许网络幂等重试。Worker 通用 provider 不接收 lease capability,由 inbox-bound HTTPS adapter 内部装配并复用单 Agent mTLS client。runtime repository 通过受保护 service 注入,`ql3_worker_ingress` role 不扩权;不得持久化/记录 Secret、新增 package/schema/timer | Accepted(ADR-0114/0121:wire、runtime authority repository、受保护 service、ingress route、Worker HTTPS provider、共享 Agent、默认 4 KiB兼容、production execution composition 与 replay/leak/size/fence 负向测试已实现;具体 KMS/Vault 和外层产品部署仍关闭) | 逐 Secret 请求会放大路由器 TLS/JSON 成本,只按 Project/ref 验证会允许旧 Attempt 取密,把 token 交给通用 provider 或扩大 ingress 数据库权限都会扩散 authority。单批次完整围栏同时给低配节点确定资源上限,并允许集群 Worker 以无状态服务和外置 Secret backend 水平扩展 |
|
||||
| D-113 | Remote Worker Secret delivery 当前使用 `qinglong/remote-secret-delivery@v2` exact batch wire:最多 64 KiB request、256 KiB response、64 个唯一普通 Project-bound ref 与一个独立 environment-bundle ref;普通单值 16 KiB且总量 64 KiB,bundle carrier 96 KiB,两个角色不得重叠。控制面必须在 Attempt advisory lock 下用 PostgreSQL 时钟复验认证 Worker/Session、Run/Attempt starting、Lease token/generation/version/expiry、offer、pinned revision、execution digest 与完整双 ref 集合,成功后才调用 plaintext provider;旧 Session/Lease/version/Attempt 重放必须拒绝,同一有效 fence 允许网络幂等重试。Worker 通用 provider 不接收 lease capability,由 inbox-bound HTTPS adapter 内部装配并复用单 Agent mTLS client。runtime repository 通过受保护 service 注入,`ql3_worker_ingress` role 不扩权;不得持久化/记录 Secret、新增 package/schema/timer | Accepted(ADR-0114/0121/0496:普通 Secret v1 围栏、v2 typed bundle、runtime authority repository、受保护 service、ingress route、Worker HTTPS provider、共享 Agent、默认 4 KiB兼容、production execution composition 与 replay/leak/size/fence 负向测试已实现;具体 KMS/Vault 和外层产品部署仍关闭) | 逐 Secret 请求会放大路由器 TLS/JSON 成本,只按 Project/ref 验证会允许旧 Attempt 取密,把 token 交给通用 provider 或扩大 ingress 数据库权限都会扩散 authority。单批次完整围栏同时给低配节点确定资源上限,并允许集群 Worker 以无状态服务和外置 Secret backend 水平扩展 |
|
||||
| D-114 | Worker 本地日志必须在现有 worker-runtime 内使用 offer-scoped opaque `wlog-*` 单一身份和私有固定 shard;root/shard/file 强制 `0700/0700/0600`,`O_APPEND|O_NOFOLLOW` 后复验 owner、单 link、fd/path inode 与目录 identity。Edge/Node 分别使用 4/64 MiB Attempt 上限与 32/256 MiB reserve,单 write 最多 1 MiB;capacity 不明、symlink、identity 漂移、非法 chunk 和 quota 必须 fail closed。output 只能在 durable launching barrier 后 take 一次,调用 Executor 即转移 ownership;tagged reject 幂等 close,unknown spawn 禁止 Processor 误关。不得新增 package/schema/timer;本地 spool 未获 upload 与 control-plane ACK 前不得删除 | Accepted(ADR-0115–0117/0120/0121:文件 allocator/writer、Profile policy、capacity admission、append/fdatasync、单次 handoff、受审 POSIX Executor、truncation fact、固定 64 KiB source、认证中央 upload、S3-compatible immutable store 与 production execution-plane composition 已实现;range read/retention 仍默认关闭) | 日志 ID 与 writer 分离会产生不可路由字节,barrier 前交接或 finally 无条件 close 会制造泄漏/截断,无界输出会耗尽路由器磁盘。单实现加显式 Profile policy 同时适配低配设备和集群 Worker;上传/保留必须通过 receipt 闭环,不能从本地 close 推断完成 |
|
||||
| D-115 | Worker POSIX 执行必须复用 local-process 的受审 launcher/receipt/durable identity,不复制实现。`launching` 必须在 spawn 前持久化 exact start time、log ID 与 callback sequence/digest;launcher journal、receipt、Executor result 和 inbox 必须一致,使 spawn 后/started 前崩溃可从 authenticated receipt 恢复。completion receipt 的 base64url capability 必须 canonical decode 为 32 bytes 后 constant-time 校验原始 digest 并清零;提交顺序固定为流式 Artifact upload + byte/digest ACK → lease-fenced completion → durable inbox terminal ACK → receipt cleanup,本地 spool 只能由后续 retention 删除。timeout 未有 durable evidence 时 fail closed。实现留在现有 worker-runtime subpath,不新增 package/schema/timer | Accepted(ADR-0116/0117/0118/0121:reviewed-fd Executor、durable pre-spawn time、launching crash recovery、认证流式 Artifact upload、PostgreSQL completion、数据库 deadline、lease-control stop、用户取消 intent、非执行状态 convergence、cleanup ordering 与具体 production execution stack 已实现;本地 spool retention 仍关闭) | 把启动时间延后保存会留下不可认证崩溃窗口;对编码字符串 hash 会产生错误 authority;先完成后上传或上传后删 spool 都会丢日志。显式可重放顺序让 Edge 以固定内存工作,也允许 Node/集群提高容量而共享同一状态机 |
|
||||
| D-116 | Remote Worker Artifact/completion 必须复用同一 TLS 1.3 mTLS Worker ingress client 与 Agent。Artifact 使用 `qinglong/remote-worker-artifact-upload@v1`:4-byte big-endian header length、最多 4 KiB exact JSON header、最多 64 MiB 原始内容、固定 media type、精确 Content-Length、identity encoding;普通 JSON 上限不得因此放宽。控制面必须在读取内容前认证 path-bound Worker/Session,在共享 Attempt fence 下授权后才写入跨副本可见的 immutable digest store,禁止 controller 本地文件冒充 cluster store;完成前必须重新 inspect 同一 Artifact 并精确匹配 ID/length/SHA-256/truncation。`qinglong/remote-worker-completion@v1` 必须在一个 PostgreSQL transaction 中按既定顺序锁定 authority,允许从 durable `starting|running` 崩溃窗口收敛,以数据库时间和 cancellation/timeout 优先级原子完成 Lease、Attempt、Run 与双 Event;服务端生成 Event ID,raw lease token 不入库/事件,只有完整终态与 Event payload 一致才允许 exact replay。缺少共享 Artifact store 的生产组合必须默认关闭;不得新增 workspace package/schema/timer/队列 | Accepted(ADR-0117/0120/0121:runtime wire、streaming ingress、Worker HTTPS adapters、共享 store port、S3-compatible checksum/conditional-copy adapter、Artifact re-inspection、PostgreSQL terminal transaction、真实 TLS 1.3、真实 MinIO SSE-S3、完整 fence/replay 与 concrete Worker execution composition 已实现;range read/retention 仍关闭) | JSON base64 会复制 64 MiB 日志并抬高路由器 RSS;controller 本地盘在多副本下不可见且会产生假 ACK;先信任 Worker digest 或把 upload 与终态放进同一长事务会分别允许内容漂移或跨网络持锁。认证前置、流式固定上限、共享 immutable store 与短终态事务同时适配低配 Worker 和水平扩展控制面 |
|
||||
@@ -6982,7 +7010,7 @@ Dispatcher 只能把任务分配给满足 required 条件的 Worker。当没有
|
||||
- ADR-0111 已让该 package inbox 原位承担唯一 execution authority:同一 offer 文件用 revision CAS 持久化 starting ACK、spawn crash barrier、started handle 与 running ACK;materialized environment 必须与 immutable revision 精确闭合,completion token 只在内存且局部副本随后清零。Executor 只有 tagged `rejected` 才能上报 start-failure,异常或未知 spawn 进入 recovery;重启可见 `launching` 不调用任何 Executor。该 Processor 仍由显式 subpath 暴露,不创建 timer 或生产连接。
|
||||
- ADR-0112 已把 starting/running/start-failure 接入 exact versioned response,并让 Offer、Activation 与后续 Secret delivery 复用同一 TLS 1.3 mTLS client、credential authority 和单 keep-alive Agent;显式 headless lifecycle 在完整 inbox reconciliation 收敛前不 Pull,且不创建自有 timer。
|
||||
- ADR-0113 已实现 Secret-before-Artifact 的有界 materializer:provider 只得到 capability-free 最小 identity,Secret ref 去重并 exact 配对,解析环境总量 64 KiB;同一 `logArtifactId` 从 allocator 交给 Executor、journal 与 running ACK,所有失败路径幂等 cleanup。
|
||||
- ADR-0114 已把批量 Secret provider 接入真实认证边界:`qinglong/remote-secret-delivery@v1` 以 64 KiB request/128 KiB response 复验 Session/Lease/Attempt/revision 完整围栏,runtime repository 在数据库权威锁内通过后才调用 plaintext provider;Worker 从 canonical inbox 内部装配 token,通用 provider 不接收 capability。`ql3_worker_ingress` 数据库权限、package 数、schema 与 timer 均不增加。
|
||||
- ADR-0114/0496 已把批量 Secret provider 接入真实认证边界:`qinglong/remote-secret-delivery@v2` 以 64 KiB request/256 KiB response 复验 Session/Lease/Attempt/revision 和普通/bundle 双 ref-set 完整围栏,runtime repository 在数据库权威锁内通过后才调用 plaintext provider;Worker 从 canonical inbox 内部装配 token,通用 provider 不接收 capability。普通 Secret 仍为 16 KiB/项与 64 KiB 合计,唯一 bundle carrier 为 96 KiB。`ql3_worker_ingress` 数据库权限、package 数、schema 与 timer 均不增加。
|
||||
- ADR-0115 已在现有 Worker package 内实现 offer-scoped `wlog-*` 私有文件 spool:Edge/Node 显式容量策略、append/reopen、symlink/owner/inode/link 防护、单 write/总量硬上限,并在 durable launching barrier 后一次性交接 output ownership;tagged reject 与 unknown spawn 的 close 语义分离。它不冒充具体 Executor、上传 ACK 或 retention。
|
||||
- ADR-0116 已让 Worker 复用 `@qinglong/local-process` 的受审 verified-fd launcher、completion receipt 与 durable identity,不复制进程实现或新增 package。`launching` 现在在 spawn 前同时持久化 exact start time、log ID 和 callback digest,因此 spawn 后/started 前崩溃可由回执严格恢复;日志 source 以最多 64 KiB chunk 读取并验证 truncation fact,完成协调器固定执行 upload byte/digest ACK → lease-fenced completion → inbox terminal ACK → receipt cleanup,且从不删除本地 spool。
|
||||
- ADR-0117 已补齐认证中央链路:Worker 复用单一 mTLS Agent,以 4 KiB header + 最多 64 MiB 原始内容流式上传;control ingress 在 body 前认证并用共享 Attempt fence 授权,只向跨副本 immutable Artifact store 写入。completion 前重新 inspect exact digest/length/truncation,再在一个 PostgreSQL transaction 中从 `starting|running` 收敛 Lease、Attempt、Run 与双 Event,支持 Event-authenticated exact replay。Artifact range read 与 retention 仍默认关闭;具体 production execution composition 由 ADR-0121 补齐。
|
||||
|
||||
@@ -5,6 +5,10 @@
|
||||
- 关联 RFC:QL-RFC-0001 D-24、D-72、D-85、D-109、D-111、D-112、D-113
|
||||
- 关联 ADR:ADR-0058、ADR-0073、ADR-0108、ADR-0109、ADR-0110、ADR-0113
|
||||
|
||||
> 2026-08-24:ADR-0496 为 Cluster Legacy Env 增加 typed environment bundle,wire 已升级为
|
||||
> `qinglong/remote-secret-delivery@v2`。本 ADR 的 v1 普通 Secret 围栏、顺序与预算语义继续有效;
|
||||
> v2 的双 ref-set、96 KiB bundle carrier 和 256 KiB response cap 以 ADR-0496 为准。
|
||||
|
||||
## 背景
|
||||
|
||||
ADR-0113 已固定 Secret-before-Artifact 的 Worker materializer,但只留下抽象 provider。
|
||||
@@ -20,11 +24,12 @@ Attempt 或被篡改 execution revision 都可能重放同一 Secret capability
|
||||
|
||||
### 1. 使用一个 exact versioned 批量协议
|
||||
|
||||
协议固定为 `qinglong/remote-secret-delivery@v1`。单次 request 最多 64 KiB、64 个唯一
|
||||
`qlsecret:v1` reference;response 最多 128 KiB,单值最多 16 KiB、值总量最多 64 KiB。
|
||||
response 必须按请求顺序返回 exact `{secretRef,value}`,并回绑 Run、Attempt、offer 与
|
||||
execution digest;未知字段、缺项、乱序、重复、跨 Project reference、NUL、超限或身份
|
||||
漂移全部 fail closed。
|
||||
当前协议为 `qinglong/remote-secret-delivery@v2`;原 v1 是普通 Secret-only 的历史基线。
|
||||
单次 request 最多 64 KiB,分为最多 64 个唯一普通 `secretRefs` 与最多一个
|
||||
`environmentBundleRefs`,两组不得重叠;response 最多 256 KiB。普通单值最多 16 KiB、值总量
|
||||
最多 64 KiB,bundle carrier 最多 96 KiB。response 必须按两组请求顺序分别返回 exact
|
||||
`{secretRef,value}`,并回绑 Run、Attempt、offer 与 execution digest;未知字段、缺项、乱序、
|
||||
重复、跨 Project reference、NUL、超限或身份漂移全部 fail closed。
|
||||
|
||||
Worker 继续复用 ADR-0112 的单一 `WorkerIngressHttpsClient`、TLS 1.3 mTLS credential
|
||||
provider 与最多一个 keep-alive socket。现有 offer/activation 的默认 request 上限保持
|
||||
@@ -39,13 +44,13 @@ provider 与最多一个 keep-alive socket。现有 offer/activation 的默认 r
|
||||
- Run、Attempt、Project、Task 与 pinned Task revision;
|
||||
- offer ID 与 execution digest;
|
||||
- Lease generation、token 与 expected version;
|
||||
- 完整且有序的 SecretRef 集合。
|
||||
- 完整且有序的普通 SecretRef 与 environment bundle SecretRef 两组集合。
|
||||
|
||||
runtime authority repository 在 Attempt advisory transaction lock 下使用 PostgreSQL 时钟,
|
||||
要求 Run=`dispatching`、Attempt=`starting`、executor=`remote_worker`、Session 为当前
|
||||
`online|draining` 且未过期、Lease=`leased` 且未过期,并逐项比较 Attempt/Session/Lease
|
||||
中的 worker、generation、offer、token digest 与 version。随后重新规范化 immutable
|
||||
`task_execution_revisions.plan_json`,要求 execution digest 与 SecretRef 集合完全一致。
|
||||
`task_execution_revisions.plan_json`,要求 execution digest 与两组 SecretRef 集合完全一致。
|
||||
任一漂移都在调用明文 provider 前拒绝。
|
||||
|
||||
同一仍然有效的 starting fence 可以因网络丢包进行幂等重试;它不产生“已消费”事实。
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# ADR-0491:有界 Secret/Config Reconciliation 与任务环境绑定
|
||||
|
||||
- 状态:Proposed(D-397 已实现 Legacy Env inspection、私有有界 row plan、durable plan publication、独立 signed decision、逐项 Automation adoption provenance、Local SQLite 原子 application publisher、Owner prepared/apply/rollback 编排、ADR-0492 completion v3;ADR-0494 完成 Cluster mounted-files provider live 子门,ADR-0495 完成 content-free Cluster plan ledger baseline;真实 Edge 空间证据、Cluster Task/Trigger mutation/receipt、HA replay 与直接外部 custody gate 尚未完成)
|
||||
- 状态:Proposed(D-397 已实现 Legacy Env inspection、私有有界 row plan、durable plan publication、独立 signed decision、逐项 Automation adoption provenance、Local SQLite 原子 application publisher、Owner prepared/apply/rollback 编排、ADR-0492 completion v3;ADR-0494 完成 Cluster mounted-files provider live 子门,ADR-0495 完成 content-free Cluster plan ledger baseline,ADR-0496 完成 opaque environment bundle 的 Worker 内存展开与 HA-validated 数据面;真实 Edge 空间证据、Cluster Task/Trigger mutation/receipt、promotion 后 receipt replay 与直接外部 custody gate 尚未完成)
|
||||
- 日期:2026-08-23
|
||||
- 决策:D-397
|
||||
- 关联:ADR-0073、ADR-0074、ADR-0092、ADR-0094、ADR-0480、ADR-0482、ADR-0483、ADR-0484、ADR-0485、ADR-0486、ADR-0487、ADR-0488、ADR-0490
|
||||
@@ -147,4 +147,4 @@ D-397 当前八切片已经实现:absent、unsupported、Edge over-budget、2.
|
||||
|
||||
ADR-0494 已完成 Cluster `mounted-files` provider live 子门:真实三节点 K3s 中两个 management replica、direct exact-key executor 和两个跨节点 provider observer 完成 PostgreSQL durable approval/binding、Kubernetes atomic projection rotation、无 Secret API 权限/ServiceAccount token、只读 `0440`、内容脱敏及删除后 fail-closed;v2 私有报告 24/24 gates 为 true,并保持 v1 verifier 兼容。该门不增加 Edge 闭包,也不等于直接 Vault/KMS/HSM custody。
|
||||
|
||||
转为 Accepted 前仍必须完成:固定低性能 Edge 设备的真实空间/写放大/断电恢复证据,以及 Cluster Legacy Env migration 的逐项 Task/Trigger current-head revalidation、revision mutation/receipt、外部 custody adapter 和 HA promotion 后 receipt replay。ADR-0495 已完成专用 PostgreSQL SERIALIZABLE plan ledger baseline,但它只保存摘要、计数和 pinned SecretRef,不执行 Task/Trigger DML,也不接触 Secret material。ADR-0492 已完成本机 completion schema 演进和 completed-head 后 rollback material 回收,ADR-0493 又让没有 Legacy 身份输入的 fresh v52 目标身份经 signed `retain_target` 正确形成 no-effect,并精确消除六张已知目标表的 `unknown` 误判。Legacy `Auths/Users` 或真正未知表仍保持 manual;本切片的 Local Owner 编排、ADR-0494 的 mounted-files gate、ADR-0495 的 plan ledger 或 PostgreSQL HA 证据都不得冒充完整 Cluster migration 与外部密钥托管。
|
||||
转为 Accepted 前仍必须完成:固定低性能 Edge 设备的真实空间/写放大/断电恢复证据,以及 Cluster Legacy Env migration 的逐项 Task/Trigger current-head revalidation、revision mutation/receipt、外部 custody adapter 和 HA promotion 后 receipt replay。ADR-0495 已完成专用 PostgreSQL SERIALIZABLE plan ledger baseline,但它只保存摘要、计数和 pinned SecretRef,不执行 Task/Trigger DML,也不接触 Secret material。ADR-0496 已验收只保存 pinned bundle ref、通过 fenced remote delivery 取回 typed carrier、在 Worker 内存展开的安全数据面,并通过 146-gate PostgreSQL HA;它仍不执行 migration DML 或生成 migration receipt。ADR-0492 已完成本机 completion schema 演进和 completed-head 后 rollback material 回收,ADR-0493 又让没有 Legacy 身份输入的 fresh v52 目标身份经 signed `retain_target` 正确形成 no-effect,并精确消除六张已知目标表的 `unknown` 误判。Legacy `Auths/Users` 或真正未知表仍保持 manual;本切片的 Local Owner 编排、ADR-0494 的 mounted-files gate、ADR-0495 的 plan ledger、ADR-0496 的数据面或 PostgreSQL HA 证据都不得冒充完整 Cluster migration 与外部密钥托管。
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
# ADR-0496:Cluster 不透明环境 Bundle 的有界交付与 Worker 内存展开
|
||||
|
||||
- 状态:Accepted(D-401 的安全执行数据面前置切片;Cluster Task/Trigger mutation 与 migration receipt 仍未完成)
|
||||
- 日期:2026-08-24
|
||||
- 决策:D-401 前置切片
|
||||
- 关联:ADR-0091、ADR-0092、ADR-0104、ADR-0113、ADR-0114、ADR-0491、ADR-0494、ADR-0495
|
||||
|
||||
## 背景
|
||||
|
||||
ADR-0495 的 Cluster plan 只保存一个同 Project、固定 version 的 SecretRef,并明确禁止把
|
||||
Legacy Env 名称、值、密文或 key ID 写入 PostgreSQL。现有 `command@v1` 环境模型却要求
|
||||
每个 Secret binding 同时保存 `{name,secretRef}`;如果 D-401 直接把 Legacy Env 逐项追加到
|
||||
Task revision,`task_definition_revisions.spec_json` 和
|
||||
`task_execution_revisions.plan_json` 会永久保存全部 Legacy Env 名称,违反 ADR-0491。
|
||||
|
||||
现有 Remote Worker Secret delivery 还假设一个 SecretRef 对应一个最多 16 KiB 的普通值。
|
||||
Legacy active environment 的合法上限是 256 项、单值 16 KiB、最终 name+value 总计 64 KiB,
|
||||
因此不能把一个最多约 96 KiB 的 JSON carrier 冒充普通 Secret,也不能在 Worker 启动前写
|
||||
ConfigMap、Pod environment、临时 export 文件或 command。
|
||||
|
||||
## 决策
|
||||
|
||||
### 1. Task 与执行修订只保存一个固定版本引用
|
||||
|
||||
`qinglong/command@v1` 增加可选 `environmentBundleRef`。它必须是 canonical、同 Project 且
|
||||
显式固定 version 的 `qlsecret:v1` reference。Task semantic registry、profile-neutral compiler、
|
||||
Cluster execution revision digest 和 PostgreSQL plan JSON 都只携带这个引用;不携带 bundle
|
||||
schema、Env 名称、值、行数、key ID 或 provider path。
|
||||
|
||||
这是 `command@v1` 与 `qinglong/command-execution@v1` 的严格可选扩展:没有该字段的历史
|
||||
revision 按原字节与原摘要解释,不执行回填或静默重写。Local execution compiler 当前拒绝带
|
||||
bundle 的 Task;现有 Local D-397 继续使用逐 Secret SQLite 原子绑定,不能意外获得 Cluster
|
||||
external-custody authority。
|
||||
|
||||
### 2. Bundle 是独立、纯数据、严格有界的 carrier
|
||||
|
||||
`qinglong/environment-bundle@v1` 固定为:
|
||||
|
||||
```json
|
||||
{"schema":"qinglong/environment-bundle@v1","entries":[{"name":"NAME","value":"opaque"}]}
|
||||
```
|
||||
|
||||
- entry 为 exact `{name,value}`,按 name canonical 排序;
|
||||
- 1~256 项,名称符合 shell 共同子集、不得使用 `QL3_`、不得重复;
|
||||
- 单值最多 16 KiB,最终 name+value 总计最多 64 KiB;
|
||||
- UTF-8 JSON carrier 最多 96 KiB,拒绝 NUL、accessor、symbol、未知字段和无效 JSON;
|
||||
- parser 是 profile-neutral 纯函数,不读取文件、网络、数据库、时钟或 Secret provider。
|
||||
|
||||
ADR-0495 的 `effectiveBindingCount` 实现上限同时从错误复用的 100,000 source-row cap 修正为
|
||||
256,使 plan contract 与 ADR-0491 的真实执行预算一致。
|
||||
|
||||
### 3. Secret delivery v2 把普通 Secret 与 bundle 分权
|
||||
|
||||
wire 升级为 `qinglong/remote-secret-delivery@v2`。请求与数据库 authority 分别携带:
|
||||
|
||||
- `secretRefs`:最多 64 个普通 SecretRef;
|
||||
- `environmentBundleRefs`:最多 1 个 bundle SecretRef;
|
||||
- 两组不得重叠,合计不得为空。
|
||||
|
||||
response 分别返回 `values` 与 `environmentBundles`。普通值继续保持单项 16 KiB、总计
|
||||
64 KiB;bundle carrier 最多 96 KiB;response cap 从 128 KiB 提升到 256 KiB,request cap
|
||||
仍为 64 KiB。Run、Attempt、Session、Lease、offer、execution digest 和完整有序的两组 ref
|
||||
仍在同一个 PostgreSQL authority transaction 中复验,bundle 不获得更弱的授权路径。
|
||||
|
||||
mounted-files provider 对普通 Secret 继续执行 16 KiB 上限,只对 authority 明确标记的唯一
|
||||
bundle ref 允许 96 KiB。它仍逐请求重读 projection、无缓存、timer、watcher 或 Kubernetes API
|
||||
权限;provider 不解析或记录 Env 内容。
|
||||
|
||||
### 4. Worker 只在进程上下文准备阶段展开
|
||||
|
||||
Worker 从 durable inbox 重建 exact Offer,由同一个 mTLS client 获取 v2 response。materializer
|
||||
先解析普通 bindings,再在内存中解析唯一 bundle,并拒绝 bundle 内重复名称、与 Task 原有
|
||||
environment 名称冲突、畸形 response、超过 256 项或最终 64 KiB 的环境。全部验证完成后才
|
||||
分配 Artifact,随后把扁平 `{name,value}` 交给 Executor;失败时释放 provider material,且不
|
||||
spawn。
|
||||
|
||||
Bundle 明文不得进入 inbox、Offer、PostgreSQL、Artifact、日志、audit、diagnostic、错误消息或
|
||||
临时文件。JavaScript string 不能可靠清零,因此实现不缓存 bundle;transport Buffer 在解析后
|
||||
清零,并在 execution context dispose 后释放引用。
|
||||
|
||||
### 5. Profile 与部署成本
|
||||
|
||||
实现复用 `runtime-core`、`worker-runtime`、`cluster-control` 和 `cluster-postgres`,不新增 workspace
|
||||
package、生产依赖、数据库 migration、表、role grant、daemon、线程、timer 或 watcher。
|
||||
Edge/Standalone 默认 import graph 不导入 Cluster provider/transport;纯 bundle parser 只在
|
||||
实际调用时按 96 KiB carrier 上限付费。Cluster 可继续通过 mounted Secret、CSI、Vault/KMS
|
||||
sidecar 或后续 direct adapter 横向扩展。
|
||||
|
||||
## 被拒绝的替代方案
|
||||
|
||||
### 把每个 Env 名称写入 Cluster Task environment
|
||||
|
||||
拒绝。Secret 值虽不在数据库,名称仍会进入 Task 历史、execution revision、备份与 HA 副本。
|
||||
|
||||
### 使用保留名称的普通 Secret binding 并让 Worker 猜 JSON
|
||||
|
||||
拒绝。payload sniffing 会让普通 Secret 意外获得 bundle 语义,也无法在 authority 层区分
|
||||
16 KiB 与 96 KiB 的不同预算。
|
||||
|
||||
### 把 bundle 放进 ConfigMap、Pod env 或 Job command
|
||||
|
||||
拒绝。它把 Secret material 交给 Kubernetes API、etcd、进程元数据或命令审计面,并破坏
|
||||
一次性、Attempt-bound 的 remote delivery fence。
|
||||
|
||||
### 为 bundle 单独新增服务或 workspace package
|
||||
|
||||
拒绝。它没有独立部署责任;新 endpoint/package 会增加低配设备与集群的维护面,现有 Secret
|
||||
delivery authority 已能安全承载 typed payload。
|
||||
|
||||
## 当前验证与后续门禁
|
||||
|
||||
当前实现已完成纯 bundle contract、Task/Cluster execution optional ref、v2 wire、PostgreSQL
|
||||
exact ref-set authority、mounted-files typed budget、Worker HTTPS transport 与内存展开;测试覆盖
|
||||
canonicalization、跨 Project/未固定 version、普通/bundle role overlap、bundle-only Offer、名称冲突、
|
||||
最终环境预算及普通 Secret 仍为 16 KiB。Runtime Core `586/586`、Worker `135/135`、Cluster Control
|
||||
`273 total / 271 pass / 2 conditional skip / 0 fail`、Cluster PostgreSQL
|
||||
`359 total / 356 pass / 3 conditional skip / 0 fail`,18-package clean build/test 退出 0;完整 backend
|
||||
为 `1568 total / 1566 pass / 2 conditional skip / 0 fail`。
|
||||
|
||||
package boundary、精确 Cluster dependency、122-module Edge import、service-manager bridge import、
|
||||
Local image 与基础 Edge/Standalone artifact audit 全部 compatible;workspace 保持 18 packages、
|
||||
`singleSourcePackages=[]`、`shallowSourcePackages=[]`。基础 Edge/Standalone 制品为
|
||||
`2,650,564 / 2,650,642 bytes`、324 files、58 loaded modules,峰值 RSS 增量分别为
|
||||
`11,190,272 / 11,173,888 bytes`,没有把 Cluster provider/transport 带入低配设备默认闭包。
|
||||
PostgreSQL 18.6 arm64 physical HA 通过 146 gates,timeline `1→2`,报告 SHA-256 为
|
||||
`0ee2199d0a52a02025bff017a07477d707353d12aefc3811f474e3775f2bb86b`。
|
||||
|
||||
本 ADR 只关闭 D-401 的执行数据面前置条件,不声明 Legacy Env migration 已完成。下一切片仍须
|
||||
在同一个 Automation Manager SERIALIZABLE transaction 中逐项复验 Task/Trigger current head,
|
||||
追加只含 `environmentBundleRef` 的 Task revision、重定向 Trigger revision,并写 content-free
|
||||
receipt;随后补 direct external custody 和 PostgreSQL promotion 后 exact replay。
|
||||
Reference in New Issue
Block a user