feat(ql3): persist manual primary activation

This commit is contained in:
whyour
2026-08-19 05:13:58 +08:00
parent 9981f47851
commit 36035ac43e
15 changed files with 1191 additions and 43 deletions
@@ -5,6 +5,7 @@
- 关联 RFCQL-RFC-0001 D-361、PR-4、PR-5
- 关联 ADRADR-0002、ADR-0449、ADR-0450、ADR-0451、ADR-0453
- AmendsADR-0453 的首次目标实例 canary 操作缺口
- Amended byADR-0455 的 Profile-bounded durable runtime activation receipt
## 上下文
@@ -36,13 +37,12 @@ Edge 可能是 128 MiB 路由设备,不能为 canary 新增 daemon、watcher
7. `approve` 是唯一写 live manifest 的操作。它要求显式 `approvedBy`,审批最短一分钟、最长 24 小时;先复核 plan 时的 absent/disabled 基线,disabled 基线先
no-replace 归档,再以同目录 fsync 临时文件和最终摘要复核发布 schema v2 manifest。结果状态固定为 `activation_approved`/`primary_selected`,并明确
`requiresRestart=true``runtimeActivationObserved=false`;配置选择不得冒充 HTTP worker 已完成 startup reconciliation 和 router activation。
8. 应用重启后,实际运行态仍由现有 bootstrap 的 `selected → reconciled → activated` audit 证明。独立 `audit:manual-primary-canary:ql3` 只读复核 plan、source、gate、
qualification、selection receipt 与 live manifest,可要求 `prepared|qualified|selected|off|rolled-back``off` 只证明 loader 当前关闭,`rolled-back` 还要求本 session 的
intent/completion 摘要链完整。auditor 刻意不提供 `primary_active` 结论。
8. 应用重启后,实际运行态 bootstrap 的 `selected → reconciled → durable active receipt → activated` 证明。ADR-0455 已让独立 auditor 复核 receipt 与 Linux
boot/PID/start-time 联合身份,并新增 `active` 要求;`off|rolled-back` 同时要求不存在仍 current 的 runtime。非 Linux portable receipt 不能获得 current 结论。
9. `rollback` 只接受四个固定 reason 与有界 operator。它先 no-replace 发布 intent,绑定当前 enabled manifest SHA-256 和目标 disabled SHA-256,再复核当前摘要并原子替换
live manifest,最后发布 completionintent 或 completion response loss 可用相同参数重放。disabled 已有效但不同于本 session 的目标时拒绝覆盖。
10. 审批过期后,既有 loader 必须立即 fail-closed 为 off;状态/auditor 报 `approvalExpired=true``rolloutMode=off`operator 仍应执行显式 rollback,把磁盘事实
收敛为 schema v2 disabled manifest,然后重启应用;过期不能被当作自动续期自动删除 authority
10. 审批过期后,loader 对下一次 bootstrap 必须 fail-closed 为 off;状态/auditor 报 `approvalExpired=true``rolloutMode=off`无 watcher 的既有进程不会仅因磁盘
审批过期而自动卸载 router,故 `off` 审计会在 current receipt 仍存活时失败。operator 必须显式 rollback 并停止/重启应用;过期不能被当作自动续期自动删除或已停止证明
## 被拒绝的替代方案
@@ -87,4 +87,4 @@ Edge 可能是 128 MiB 路由设备,不能为 canary 新增 daemon、watcher
维护者仍需在一台真实目标 Edge 或 Standalone 实例执行 [Manual Primary Canary 操作手册](../operations/ql3-manual-primary-canary.md),并保留 capture、terminal、resource、
gate、qualification、selection、bootstrap activated audit 与 rollback completion。其他 origin 必须建立自己的 admission authority 与 gate;固定物理路由/flash/断电、
运行态 durable activation receipt 和多写者 config authority 仍是独立后续工作
多写者 config authority 仍是独立后续工作;运行态 durable activation receipt 已由 ADR-0455 补齐,但首次真实目标实例执行仍待运维
@@ -0,0 +1,66 @@
# ADR-0455Profile-bounded Manual Primary 运行态激活凭据
- 状态:Accepted(实现完成;首次真实目标实例执行仍待运维)
- 日期:2026-08-19
- 关联 RFCQL-RFC-0001 D-362、PR-4、PR-5
- 关联 ADRADR-0002、ADR-0453、ADR-0454
- AmendsADR-0454 的运行态 durable activation receipt 缺口
## 上下文
ADR-0454 把 `primary_selected` 与运行态 `activated` 明确分开,但后者仍只存在于进程日志。日志可能没有持久 sink、可能被轮转,也无法在进程退出后区分“曾经激活”与“当前实例仍在运行”。仅记录 PID 又会受到 PID 重用、宿主重启和容器 namespace 变化影响。
Edge 可能只有 128 MiB 内存与低耐久 flash,不能为运行态证明增加 watcher、心跳 timer、数据库表、遥测 sidecar 或无限增长事件日志。Standalone 即使运行在集群节点上,也仍是本机 Profile,不能把宿主形态当成 cluster-control authority。
## 决策
1. 默认 HTTP worker 仅在 accepted schema v2 manifest 精确选择 `manual=primary` 且 Profile 为 `edge|standalone` 时,惰性加载凭据 adapter。disabled、rejected 和非 Primary 路径不 import adapter、不读写凭据。
2. 当前状态固定为 config root 下唯一的 `qinglong3-manual-primary-runtime.json`schema 为 `qinglong/manual-primary-runtime-receipt@v1`。它是 observed-state projection,不是 rollout authority`qinglong3-rollout.json` 仍是唯一期望配置。
3. 凭据只包含随机 activation ID、Profile、manifest revision、manifest 原始 SHA-256、激活/更新时间、`active|stopping|stopped|failed` 状态、进程身份和 domain-separated 自摘要。文件不包含 Task、Run、Cron、命令、路径、用户、日志、Secret 或错误正文。
4. 文件必须位于当前 UID 拥有、非 symlink、group/world 不可写的真实 config root,以 `0600` 临时 inode、file fsync、原子 rename 和 best-effort directory fsync 发布。大小硬上限为 8 KiB;读取使用 `O_NOFOLLOW` 并复验 owner、mode、普通文件、exact shape 与自摘要。
5. Linux 当前性使用 `/proc` 的 boot ID、PID、process-group ID 和 start-time ticks 联合身份,避免 PID 重用。已有 `active|stopping` 凭据只有在该联合身份确定 `exited|identity_mismatch` 后才能由下一实例替换;仍为 `running` 或无法反证时 fail closed。非 Linux 只写 portable PID,允许开发启动,但独立 auditor 必须返回 `unsupported`,不得宣称 current。
6. 激活顺序固定为 reconciliation → 三个 lifecycle → router install → durable `active` → structured `activated` audit。写 `active` 失败时必须立即撤销 router、停止已启动 lifecycle,并保持原激活错误;因此日志中的 `activated` 必然晚于 durable receipt。
7. 干净停止先写 `stopping`,再撤销 router 并按 timeout → cancellation → completion 停止 lifecycle,最后写 `stopped`。任一停止或凭据转换失败时尝试写 `failed`,停止调用仍返回第一个错误;`stopping|stopped|failed` 都不能被审计为 active。重复 stop 共用同一 Promise,不重复释放资源或写状态。
8. 独立 canary auditor 新增 `--require=active`:必须同时复核 plan、qualification、selection、live enabled manifest、receipt binding 和 Linux 联合进程身份。报告分开输出 `runtimeActivationObserved``runtimeActivationCurrent`、receipt state 与 process state,不输出 PID、boot ID 或 activation ID。
9. `--require=off|rolled-back` 现在还要求 `runtimeActivationCurrent=false`。manifest 已回滚或审批已过期但旧 worker 尚未停止时,loader 对下一次 bootstrap 的决策虽为 off,当前内存 router 仍可能继续拥有 manual;auditor 必须拒绝把这种状态表述为已关闭。无 watcher 的部署必须显式停止/重启后再完成 off/rollback 审计。
10. 本凭据不是跨 UID、共享卷或多写者共识锁。同一 UID 可改写 config root,启动前的 read/inspect/write 也不替代部署系统的单实例约束;共享 config、多主机签名或强互斥需要独立 authority,不能由本地 receipt 推断。
## 被拒绝的替代方案
### 用 enabled manifest 表示 active
拒绝。manifest 可能尚未被当前 worker 读取,reconciliation 可能失败,审批可能过期,回滚后旧进程也可能尚未重启。
### 每秒刷新心跳文件
拒绝。它增加永久 timer、写放大和 flash 磨损,仍需处理 suspend、时钟跳变与调度延迟。进程联合身份提供按需审计,不需要周期写盘。
### 每次启动追加一份不可变 receipt
拒绝。无限历史会在路由设备上持续增长,而且仍需另一个 current pointer。单个有摘要的 observed-state projection 足以表达本切片状态;不可变 selection 与 rollback 链继续由 ADR-0454 保存。
### 非 Linux 使用 `kill(pid, 0)` 宣称 current
拒绝。它不能防 PID 重用,也不能绑定宿主 boot。portable receipt 只证明 bootstrap 曾写入,不提供 current 结论。
## 资源、安全与部署影响
- 默认关闭路径零新增 I/O、timer、watcher、listener、连接和数据库操作。
- 激活写一次,干净停止最多再写两次;常驻内存只有一个小 receipt 对象,不随任务数增长。
- 不新增 workspace package、生产依赖、migration、PostgreSQL/Kubernetes 对象或部署端口。
- receipt 是 owner-private 运维证据,不应发布到公开 artifactauditor stdout 只输出低敏状态。
- approval expiry 不会在已运行进程内自动卸载 router。该限制是无 watcher 决策的直接结果,必须通过部署系统的显式 restart/stop 收敛。
## 验证
- 聚焦 `25/25` 覆盖 active→stopping→stopped、摘要篡改、live identity 冲突、stale generation 接管、portable identity 不冒充 Linux current、receipt 写失败撤销 ownership、停止顺序、canary `--require=active` 以及 live runtime 阻断 rolled-back。
- `build:back` 与完整 backend 通过:`1,487 pass / 0 fail / 2 conditional skip`。首次沙箱执行仅有 Vault loopback 因 `listen EPERM` 失败;允许 loopback 后原命令完整重跑为上述结果。
- 18-package clean build/test 退出 0。首次沙箱执行仅有 worker-runtime 三个 TLS/mTLS loopback contract 因相同 `listen EPERM` 失败;允许 loopback 后完整 clean 原命令重跑通过。
- Edge import、cluster dependency、package boundary、service-manager bridge 四项架构审计零 findingpackage boundary 仍为精确 18`singleSourcePackages=[]``shallowSourcePackages=[]`
- `14/14` Local Profile artifact audit 全部通过且字节与 D-361 基线一致:基础 Edge/Standalone `2589998/2590076`、adopted `2809293/2809416`、application `3632877/3632997`、application-api `3800430/3800574`、AI `3069251/3069341`、application+AI `4493151/4493283`、MCP `7315930/7316038`
- 隔离 frozen-dependency Linux arm64 Node 24 Docker 门通过:128 MiB router stress peak `87,339,008` bytes256 MiB Edge release peak `145,506,304` bytes`memory.events max/oom/oom_kill` 增量均为 0;所有 workload 通过。该证据不是物理路由或 flash/断电证明。
- 本 ADR 不修改依赖树、PostgreSQL schema/migration 或 Kubernetes 拓扑,因此 PostgreSQL HA 不因本切片重复执行。
## 后续
仍需在真实 Edge/Standalone 目标实例执行 prepare→真实 manual cohort→qualify→approve→restart→`--require=active`→rollback→restart→`--require=rolled-back` 完整仪式。物理 flash/断电、PID namespace、同 UID 恶意并发、共享配置目录和签名式多写者 authority 继续作为独立 Gate。
+1
View File
@@ -458,6 +458,7 @@
| [ADR-0452](./ADR-0452-atomic-flattened-backend-build-publication.md) | 原子且扁平兼容的 Backend 构建发布 | Accepted |
| [ADR-0453](./ADR-0453-origin-scoped-legacy-shadow-capture-authority-and-primary-gate.md) | Origin-scoped Legacy Shadow 捕获权威与 Primary 门禁 | Accepted(首次真实目标实例 manual canary 待执行) |
| [ADR-0454](./ADR-0454-target-instance-manual-primary-canary-ceremony.md) | 目标实例 Manual Primary Canary 与显式回滚仪式 | Accepted(首次真实用户目标实例执行待运维) |
| [ADR-0455](./ADR-0455-profile-bounded-manual-primary-runtime-activation-receipt.md) | Profile-bounded Manual Primary 运行态激活凭据 | Accepted(首次真实目标实例执行待运维) |
## 规则