Files
qinglong/docs/adr/ADR-0495-content-free-cluster-legacy-env-migration-plan-ledger.md
T

145 lines
7.5 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-0495:无敏感内容的 Cluster Legacy Env 迁移计划账本
- 状态:Accepted
- 日期:2026-08-24
- 决策:D-400
- 关联:ADR-0104、ADR-0233、ADR-0259、ADR-0491、ADR-0494
> 2026-08-24ADR-0497/D-402 已完成本 ADR 所列的下一切片:在同一 Automation Manager
> SERIALIZABLE transaction 中复验并迁移 Task/Trigger,重置 schedule fence,并写入逐项
> content-free receipt。本 ADR 的 plan ledger 决策保持不变。
## 背景
ADR-0491 已完成 Local SQLite 上的 Legacy Env 检查、人工裁决、Secret application、
Task/Trigger revision mutation、回滚和 completionADR-0494 又证明了 Cluster
`mounted-files` provider 的在线轮换。Cluster 仍缺少一个独立于 Local SQLite/POSIX
authority 的持久计划边界,无法在真正写 Secret、Task 或 Trigger 之前冻结源证据、目标
revision 集合和幂等 mutation 身份。
该边界不能把 2.x `Envs` 的名称、值、行内容搬进 PostgreSQL,也不能为了“先跑起来”
让 runtime、admin 或通用 root export 取得迁移写权限。小型路由设备不应安装或加载本
Cluster 能力;集群侧也必须有明确的行数、Task、Trigger 和有效载荷预算。
## 决策
### 1. 账本只保存不可逆摘要、计数和目标引用
新增 profile-neutral `qinglong/cluster-legacy-env-migration-plan@v1`。计划只包含:
- plan、mutation、Project 身份;
- reconciliation bundle、reviewed decision、candidate set 三个 SHA-256 摘要;
- source/active/disabled/effective binding 计数;
- 同 Project、精确固定 version 的 canonical `SecretRef`
- Task 与 Trigger revision set 摘要和计数;
- effective Secret 总字节数、数据库计划时间和 plan digest。
它不得包含源 Env 名称/值、row body、目标 plaintext/ciphertext、key ID、credential、
Task spec、Trigger spec 或迁移日志。`SecretRef` 是既有目标路由引用,不是源 Env 名称;
其 payload 仍受 canonical encoding、同 Project 和固定 version 校验。
硬上限为 100,000 个源行、100,000 个 Task、500,000 个 Trigger、64 KiB effective
Secret 和 8 KiB plan JSON。active/disabled/source 计数必须守恒,至少存在一个 active
行、effective binding 和 Task。
### 2. PostgreSQL 只提供 append-only v69 计划表
`pg-0070-cluster-legacy-env-migration-plans` 将 control contract 推进到 v69,并新增
`cluster_legacy_env_migration_plans`
- `plan_id` 为主键,`mutation_id``plan_digest` 各自唯一;
- Project 外键使用 `ON DELETE/UPDATE RESTRICT`
- identity、digest、source、target、time、JSON 六组 named constraints
- `plan_json` 必须与列值构造出的完整 canonical JSONB **精确相等**,不是包含关系,
因而任何额外 Env 字段也会被数据库拒绝;
- 不提供 UPDATE、DELETE、TRUNCATE 或后台 GC。
迁移能力值新增 `cluster_legacy_env_migration_plan: 1`。Drizzle schema、readiness
contract、固定 migration checksum 和实际 SQL stream 必须保持同锁步。
### 3. 只有 Automation Manager 可以读取和追加
新表从 PUBLIC 撤销全部权限,只向 `ql3_automation_manager` 授予 `SELECT, INSERT`
runtime、admin、Worker ingress、Approval/Run/Package/Worker Credential 等其他角色均为
零权限;migration owner 只用于 schema 管理与集成验证。
具体 repository 只由
`@qinglong/cluster-postgres/cluster-legacy-env-migration-plan` 显式子路径发布,不进入
package root、runtime 或 admin entrypoint。runtime-core 也只通过
`@qinglong/runtime-core/cluster-legacy-env-migration-plan` 发布数据契约。
### 4. 发布使用数据库时间、SERIALIZABLE 和精确重放
repository 每次使用一个短 `SERIALIZABLE` transaction,设置 statement、lock 和 idle
transaction timeout;先按 `mutation_id` 查 exact replay,再验证 Project 在该可序列化
快照中为 active,使用 `transaction_timestamp()` 生成 `plannedAtMs`,最后追加计划。
相同 mutation 与相同 intent 返回 durable `existing`mutation drift、plan identity
占用、inactive Project 和 named constraint 冲突失败关闭。`40001``40P01``55P03`
最多重试三次。可选 transaction hook 只用于后续在**同一 Automation Manager 事务**中
追加 Task/Trigger revision mutation;本 ADR 不调用它来声称那些 mutation 已实现。
Project 检查使用普通 SERIALIZABLE read,而不是 `FOR SHARE`。后者在 PostgreSQL 中还
要求 UPDATE privilege,会错误扩大 Automation Manager 权限;可序列化顺序与外键已足以
表达“计划发生在并发归档之前”的合法历史。
### 5. 不扩大 Edge/Standalone 闭包
实现复用既有 `runtime-core``cluster-postgres` package,不新增 workspace package、
生产依赖、daemon、timer、watcher、controller 或缓存全集。Edge/Standalone 不导入
PostgreSQL repository 或 migration;计数上限是 Cluster 计划输入预算,不是把集群表或
Task 集合加载进小型设备。
## 被拒绝的替代方案
### 在 PostgreSQL 保存 Env 名称、值、密文或 key ID
拒绝。密文仍会扩大 custody、rotation、backup 和 HA 泄漏面;计划账本只需要摘要和目标
引用即可证明后续 mutation 的输入身份。
### 在同一切片直接改写全部 Task/Trigger head
拒绝。计划发布和实际 DML 是两个可独立审计的 authority 阶段。没有 current-head
revalidation、逐项 mutation receipt 与完整 response-loss 测试前,不得把 plan 冒充迁移。
### 复用 Local SQLite 或 Plugin Package Secret Binding 表
拒绝。Local authority 不属于 ClusterPlugin Package binding 的 installation/generation
身份也不能表达 Legacy Env reconciliation bundle、disabled preservation 和 Task/Trigger
revision set。
### 为一个 contract 和一个 repository 新建 package
拒绝。它们分别属于既有 runtime migration contract 与 Cluster reconciliation storage
能力,新增微包会再次制造用户已经指出的单文件 package 问题。
## 当前验证
2026-08-24 已完成:
- runtime contract 定向测试 4/4
- PostgreSQL repository 定向测试 5/5
- `@qinglong/runtime-core``@qinglong/cluster-postgres` 完整 package 测试零失败;
- PostgreSQL migration/schema/readiness/entrypoint 定向门 92/92
- 真实 PostgreSQL 18.4 应用 70 条 migration 成功;
- 实际 automation-manager create/replay/read 成功,UPDATE 被 `42501` 拒绝;
- 实际 runtime/admin SELECT 被 `42501` 拒绝;
- 带源 `envName` 的 widened JSON 被
`ql3_cluster_legacy_env_plan_json_check``23514` 拒绝。
## 边界与后续门禁
本 ADR 关闭 ADR-0491 的 **Cluster plan ledger baseline**,但不把 ADR-0491 整体转为
Accepted,也不声明:
1. Legacy source scan、sealed bundle 或 signed decision 已在 Cluster 中生产装配;
2. Task/Trigger current head 已逐项复验或 revision mutation 已提交;
3. Secret material 已写入 Kubernetes/Vault/KMS/HSM
4. migration Job、短期身份、receipt 与 promotion 后 exact replay 已完成;
5. PostgreSQL physical failover、CloudNativePG promotion 或多架构结果已由本切片重新证明;
6. 固定低性能路由设备的空间、写放大、断电和恢复证据已完成。
下一切片应在 Automation Manager 同一事务内绑定 current Task/Trigger revision set、逐项
mutation 与 receipt,同时保持计划表 append-only;随后再接入外部 custody 和 HA promotion
后的 exact replay。任何一步都不能回写源 Env 明文或放宽其他数据库角色。