Files
qinglong/docs/adr/ADR-0212-owner-fenced-local-project-lifecycle.md

7.2 KiB
Raw Permalink Blame History

ADR-0212Owner 围栏化 Local Project 生命周期

  • 状态:Accepted
  • 日期:2026-07-29
  • 关联 RFCQL-RFC-0001 D-05、D-27、D-37、D-65、D-72、D-73、D-175、 D-198、D-201、D-202
  • 关联 ADRADR-0028、ADR-0074、ADR-0086、ADR-0185、ADR-0207、 ADR-0208、ADR-0211

背景

ADR-0208 让既有 Project 的 RoleBinding 有了强认证产品入口,但 Project 本身仍只能 通过迁移、测试 fixture 或直接 SQL 建立。部署者无法用受支持方式:

  • 创建第二个业务 Project
  • 原子建立首个可接管 Owner
  • 暂停一个 Project 的全部业务授权;
  • 在保留历史、RoleBinding 和资源的前提下恢复 Project。

Project lifecycle 是实例级管理动作,不应由任意 secondary Project Owner 执行。 如果创建 Project 与创建首 Owner 分成两个事务,进程在中间崩溃会留下无人可管理的 active Project。若直接覆盖 Project 行,又会失去响应丢失重放和审计事实。

决策

1. 扩展既有 ql3-policy,不新增 workspace package

继续使用以下既有边界:

  • @qinglong/runtime-core/local-project-policy-administration:命令、结果和错误契约;
  • @qinglong/local-sqlite/project-policy-administration:最终事务 authority
  • @qinglong/local-admin/project-policy-administration:强认证 Owner 服务;
  • @qinglong/local-owner-cli/project-policy-command:私有 command file 与低敏输出;
  • ql3-policy:一次命令、一次进程的产品 binary。

新增三个 operation

  • policy.project.create
  • policy.project.archive
  • policy.project.restore

workspace 仍为 22 个 package。该领域与 RoleBinding 共享认证、Policy、SQLite authority、CLI 和部署消费者,不满足 ADR-0185 的新交付/进程/依赖隔离条件;把它 拆成单文件 package 只会增加 importer、lockfile 和供应链维护成本。

2. 只有实例 authority Project 的当前强认证 Owner 可以变更生命周期

command 同时携带 authorityProjectId 和目标 projectId。服务层先要求调用者在 authority Project 上通过 Owner-only project.manage。SQLite 在 BEGIN IMMEDIATE 取得写锁后再次复验:

  1. authenticated credential、Identity、有效期、pepper binding/catalog/material
  2. ADR-0211 的实例 authority anchor
  3. authority Project active/version
  4. actor 最新 RoleBinding version/state/owner role。

secondary Project Owner 即使能通过自己 Project 的 policy,也会在最终 authority anchor 检查失败关闭。直接调用 repository 不能绕过。

3. Project create 与首 Owner、审计、mutation ledger 原子提交

policy.project.create 要求:

  • expectedCurrentVersion = 0
  • 唯一且规范的 Project ID、name、slug
  • UUIDv4 mutation 和独立 failure-audit UUID
  • Profile 容量仍有余量。

同一事务插入:

  1. version 1、active 的 Project
  2. actor 自身 version 1、active/owner 的 RoleBinding
  3. allowed security audit
  4. immutable Project administration mutation。

任一步失败全部回滚。成功输出只含 Project ID、name、slug、status、version 和 inserted|existing

4. archive/restore 是版本化状态转换,不删除历史

  • archiveactive@N → archived@(N+1)
  • restorearchived@N → active@(N+1)
  • 两者都使用 expected-version CAS
  • authority Project 禁止 archive
  • archived Project 由现有 Project Policy 默认拒绝业务权限;
  • RoleBinding、Task、Run、Secret、Package 和审计历史不删除;
  • restore 后沿用既有 RoleBinding,重新进入正常 Policy 判定。

本切片不提供 hard delete。需要资源清理时必须另建引用感知、可恢复的 retention 协议,不能把 archive 冒充删除。

5. mutation ledger 提供精确响应丢失重放

SQLite v37 新增 QingLong3ProjectAdministrationMutations,冻结 operation、 authority Project、目标 Project 快照、前后版本、actor、首 Owner version、audit 和 时间。相同 mutation 且语义完全一致返回 existing;相同 mutation 用于不同操作、 Project、metadata、版本、actor 或 audit 时返回 mutation conflict。

Project 当前行是可变 headmutation ledger 是不可变历史,两者不能合并成同一事实。

6. Profile 容量是硬上限

  • Edge:最多 16 个 Project
  • Standalone:最多 128 个 Project。

容量在最终写事务内计数,达到上限时不写 Project、Owner、allowed audit 或 mutation。 archive 不释放名额,因为对象和引用仍然存在。容量仅影响人工管理命令,不增加 daemon、timer、watcher、listener、缓存或空闲写入。

不采用方案

为 Project lifecycle 新建 package

拒绝。它与 RoleBinding 共用同一产品、authority 和交付闭包,没有独立消费者或进程 权限,违反 ADR-0185 的 package budget。

允许任意 Project Owner 创建其他 Project

拒绝。Project-scoped RBAC 不能隐式授予实例拓扑 authority,否则 secondary Owner 可无限扩展实例作用域。

先创建 Project,再单独授予 Owner

拒绝。崩溃窗口会留下 active 但无人可管理的 Project。

archive 时删除 RoleBinding 或业务数据

拒绝。删除会破坏审计、历史 Run 可解释性和可恢复性,也无法安全判断所有引用。

只更新 Project 当前行,不保存 mutation ledger

拒绝。COMMIT response loss 后无法区分未执行、已执行和被其他命令覆盖。

影响

正向影响:

  • 本机部署首次具备受支持的多 Project 创建、暂停和恢复路径;
  • 创建后立即存在可登录、可交接的 Owner;
  • archived Project 自动复用既有默认拒绝 Policy;
  • secondary Project Owner 与 direct repository caller 均不能扩大实例拓扑;
  • Edge/Standalone 资源上限明确,空闲成本为零;
  • workspace 保持 22 包,没有新增第三方依赖。

代价与限制:

  • Local SQLite contract 从 v36 提升到 v37,镜像和 rollout 写契约必须同步升级;
  • Project list/inspect 由 ADR-0213 补齐;当前仍没有 rename、authority transfer 或 hard-delete 产品命令;
  • authority Project 无法 archive;恢复该根对象需要未来独立 break-glass ceremony
  • Cluster 仍使用 PostgreSQL/RBAC 管理面,不能复用本机 command file。

验证

  • GitNexus 对共享 SQLite readiness 入口评估为 CRITICAL:15 个直接调用者、35 个上游 符号;v37 必须跑迁移、冷启动、rollout 和既有安全回归;
  • ql3-policy 真实 SQLite/CLI 10/10
    • create/exact replay
    • 首 Owner、audit、mutation 原子事实;
    • archive 后业务 policy 拒绝、restore 后恢复;
    • mutation drift、ID/slug 冲突;
    • authority Project archive 拒绝;
    • secondary Project Owner 越权拒绝;
    • Edge 第 17 个 Project 在零部分写入下拒绝;
    • 既有 RoleBinding、防锁死和 credential fence 回归;
  • SQLite database 13/13、rollout safety 7/7、Identity/Credential 聚焦 4/4
  • local-admin 与 local-owner-cli strict TypeScript 通过;
  • Local image/OCI labels、CI image contract 和 deployment evidence 必须固定为 v37
  • 完整 workspace、制品/RSS 和 PostgreSQL HA 门在锁定依赖恢复后重跑。