mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-23 03:18:09 +08:00
170 lines
7.2 KiB
Markdown
170 lines
7.2 KiB
Markdown
# ADR-0212:Owner 围栏化 Local Project 生命周期
|
||
|
||
- 状态:Accepted
|
||
- 日期:2026-07-29
|
||
- 关联 RFC:QL-RFC-0001 D-05、D-27、D-37、D-65、D-72、D-73、D-175、
|
||
D-198、D-201、D-202
|
||
- 关联 ADR:ADR-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 是版本化状态转换,不删除历史
|
||
|
||
- archive:`active@N → archived@(N+1)`;
|
||
- restore:`archived@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 当前行是可变 head;mutation 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 门在锁定依赖恢复后重跑。
|