Files
qinglong/docs/adr/ADR-0208-authenticated-local-project-role-policy-cli.md
T

149 lines
6.6 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-0208:强认证 Local Project Role Policy CLI 与防锁死交接
- 状态:Accepted
- 日期:2026-07-29
- 关联 RFCQL-RFC-0001 D-05、D-27、D-37、D-65、D-72、D-73、D-175、
D-197、D-198
- 关联 ADRADR-0028、ADR-0074、ADR-0086、ADR-0185、ADR-0193、ADR-0207
## 背景
Fresh Setup 与首 Owner ceremony 已创建 `default` Project 和首个 Owner
ADR-0207 也让 Secret 管理具备正式 CLI。但 Project RoleBinding 仍只有 repository
和 Policy Engine,没有强认证产品入口。部署者不能把 viewer/operator/admin 权限
交给另一个已登记主体,也不能以受支持方式交接 Owner。
直接把 `ProjectPolicyRepository.append()` 暴露给 CLI 不可接受:
- 它不验证 actor 的 `project.manage`
- RoleBinding、allowed audit 和 credential/Policy fence 不在同一事务;
- admin 持有 `policy.manage`,若产品误用该 permission,可把自己提升为 owner
- 只数 Owner binding 会允许把 Owner 交给没有 active credential 的 User,随后撤销
原 Owner,形成不可恢复的假交接。
## 决策
### 1. 复用现有四个 package,不新增 importer
- `@qinglong/runtime-core/local-project-policy-administration` 只定义原子 repository
contract 和低敏错误;
- `@qinglong/local-sqlite/project-policy-administration` 是一次命令、一个连接的短生命
周期 composition
- `@qinglong/local-admin/project-policy-administration` 拥有认证后领域服务;
- `@qinglong/local-owner-cli/project-policy-command``ql3-policy` 提供私有 command
file 产品入口。
workspace 继续是 22 个 package。常驻 `local-application`、Profile、Worker 和
Cluster 均不能导入两个 mutation authority。依赖审计只允许 exact
`projectPolicyCommand.ts` 使用它们。
### 2. v1 只管理既有 Project 的 RoleBinding
开放两个 operation
- `policy.role-binding.put`:创建或更新 active binding
- `policy.role-binding.revoke`:追加 revoked binding。
两者都要求 target、expected current version、UUIDv4 mutation、request ID 和独立
failure-audit UUID。成功只返回 Project、target、version、state、可选 role 和
`inserted|existing`,不返回 credential、token、pepper、路径或内部 Policy 细节。
本切片不创建/归档 Project,不注册 Identity,不签发 credential,不提供远程 HTTP
入口,也不把常规 `ProjectPolicyRepository.append()` 变成产品 API。
### 3. 只有当前强认证 Owner 可以修改 RoleBinding
CLI 使用现有 private `0600` command file、Owner credential presentation、
pepper provenance 和 POSIX proof 建立 `local_console` Principal。服务固定请求
`project.manage`,不能改为 `policy.manage`
- owner 允许;
- admin/operator/viewer 拒绝;
- admin 不能借管理 RoleBinding 把自己升级为 owner
- deny/approval/unavailable 先写低敏 audit,再返回固定错误。
### 4. 最终写事务内复验三重 fence
SQLite 在 `BEGIN IMMEDIATE` 已取得写锁后按顺序复验:
1. authenticated credential version/state、User status、有效期、pepper state 与
material digest
2. Project active/version
3. actor 最新 RoleBinding version/state/owner role
4. target RoleBinding expected version 与 mutation replay
5. Owner 连续性。
随后 RoleBinding revision 与 allowed security audit 在同一事务插入。任何 fence、
版本、mutation、audit 或约束失败都回滚。相同 mutation 的语义重放返回
`existing`;时间戳不参与 response-loss 的语义相等判断。
### 5. Owner 交接必须同时防止“最后一个 Owner”和“不可登录 Owner”
- active owner 只能授予 `user` subject
- owner grant 在同一事务确认目标 Identity active,并至少存在一条当前有效的 active
API credential、pepper binding 和 active/retired pepper catalog
- 撤销或降级当前 active User owner 前,必须仍有另一条最新 active User owner
binding
- 因此正确流程是先为目标 User 完成 Identity/credential ceremony,再 grant owner
用目标 credential 验证登录,最后 revoke 原 owner。
这不证明目标 credential 的外部 presentation file 仍在;operator 仍必须在交接前
用目标身份完成一次真实认证。
## 低配路由器与集群影响
- 管理命令仅人工调用时加载,结束后关闭单 SQLite authority
- 不新增 migration、表、daemon、timer、watcher、listener、socket 或第三方依赖;
- Edge/Standalone 常驻 artifact closure 不导入 local-admin policy service 或 Owner
CLI
- Cluster 继续使用独立 PostgreSQL/RBAC/管理 host 设计,不能复用本地文件
credential CLI。
## 不采用方案
### 暴露常规 `ProjectPolicyRepository.append`
缺少 actor Policy、credential fence 和同事务 audit,且会扩大任意 caller 的写权限。
### 使用 `policy.manage`
admin 本来拥有该 permission;允许其签发 owner 会形成直接提权路径。产品固定使用
owner-only `project.manage`
### 只禁止删除最后一行 Owner
RoleBinding 是 append-only,必须检查每个 subject 的最新 revision;历史 owner 行
不能计入当前 owner 数。
### 只检查第二个 Owner binding
无 active Identity/credential 的 Owner 不能接管系统。将其计入连续性会制造锁死。
## 验收证据
- `ql3-policy` 真实 SQLite/CLI 专项 6/6
- grant、exact replay、role update、revoke
- admin 自提升拒绝;
- 最后一个 active User owner 撤销拒绝;
- 无 active credential 的 owner target 拒绝;
- 有 credential 的 owner handover 后允许撤销原 owner
- credential 在预检后 revoke 时事务回滚。
- dependency/source boundary 34/34,包含 mutation authority 的 exact-file 正负向
契约;
- runtime-core contract、SQLite repository、local-admin service 和 Owner CLI 的
strict targeted TypeScript 编译通过;
- workspace 仍为 22 package,无新增 dependency 或 migration。
完整 workspace build、artifact 预算和 PostgreSQL HA 门仍需在锁定依赖恢复后重跑;
不能用本专项门替代。
## 后续
- Identity register/disable 与 credential issue/rotate/revoke 的本机产品入口;
- Project lifecycle/query 已由 ADR-0212/0213 完成,RoleBinding current query 已由
ADR-0214 完成;仍需 rename、authority transfer 与 RoleBinding history query
- bounded local audit query 已由 ADR-0215 完成;仍需 export/retention/signing/alert
- RoleBinding change approval/break-glass 和恢复流程;
- Cluster PostgreSQL 对等管理 transport
- 固定 Edge 设备上的 CLI RSS、闪存写入和断电恢复证据。