mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-21 18:08:20 +08:00
feat(ql3): persist manual primary activation
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
- 关联 RFC:QL-RFC-0001 D-361、PR-4、PR-5
|
||||
- 关联 ADR:ADR-0002、ADR-0449、ADR-0450、ADR-0451、ADR-0453
|
||||
- Amends:ADR-0453 的首次目标实例 canary 操作缺口
|
||||
- Amended by:ADR-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,最后发布 completion;intent 或 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-0455:Profile-bounded Manual Primary 运行态激活凭据
|
||||
|
||||
- 状态:Accepted(实现完成;首次真实目标实例执行仍待运维)
|
||||
- 日期:2026-08-19
|
||||
- 关联 RFC:QL-RFC-0001 D-362、PR-4、PR-5
|
||||
- 关联 ADR:ADR-0002、ADR-0453、ADR-0454
|
||||
- Amends:ADR-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 运维证据,不应发布到公开 artifact;auditor 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 四项架构审计零 finding;package 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` bytes,256 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。
|
||||
@@ -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(首次真实目标实例执行待运维) |
|
||||
|
||||
## 规则
|
||||
|
||||
|
||||
Reference in New Issue
Block a user