6.5 KiB
ADR-0214:Owner 围栏化、有界的 Local RoleBinding 查询
- 状态: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-200、D-203、D-204
- 关联 ADR:ADR-0028、ADR-0185、ADR-0208、ADR-0210、ADR-0213
背景
ADR-0208 的 policy.role-binding.put|revoke 要求 target 的 current version,但产品
入口不能读取当前 RoleBinding。operator 仍需直接查询 SQLite,既绕过认证和审计,也
容易把 append-only 历史行误当成当前授权。
RoleBinding 查询与 ADR-0213 的 Project 拓扑查询不是同一种 authority:
- Project create/archive/restore/inspect/list 是实例级拓扑管理,只属于实例 authority Project Owner;
- RoleBinding 是 Project-scoped 权限管理,每个 Project 的当前 Owner 应能自治查询 自己 Project,而不能因此枚举其他 Project。
决策
1. 扩展既有 ql3-policy,不新增 package、migration 或常驻资源
在现有 project-policy-administration 四层 subpath 中新增:
policy.role-binding.inspect;policy.role-binding.list。
查询只读取 v37 已有的 append-only RoleBinding 表并写已有 security audit 表。 workspace 保持 22 个 package,不新增依赖、daemon、timer、watcher、listener、缓存 或端口。
2. 每个 Project 的当前强认证 Owner 只能查询自己的 Project
local-admin 对 request projectId 固定请求 Owner-only project.manage。SQLite 在
同一个 BEGIN IMMEDIATE 事务内复用 put/revoke 的最终 authority helper,重新验证:
- authenticated credential、Identity、有效期和 pepper provenance;
- 目标 Project active/version;
- actor 最新 active Owner RoleBinding/version。
验证后才读取目标或列表并原子写 allowed audit。admin/operator/viewer 和未绑定 User
均不能查询。secondary Project Owner 可以查询其自己的 Project,但不能把自身
project.manage 用于另一个 Project。
这条路径不要求 ADR-0211 的实例 authority anchor;错误地加入该要求会破坏 Project-scoped 自治。
3. inspect 只返回 target 的最新 revision
policy.role-binding.inspect 接受 Project、target、request ID 和单一 audit event
ID。SQLite 按 version 降序只取一条:
- 从未存在时返回
found:false; - 当前 active 时返回 role;
- 当前 revoked 时不返回 role;
- 历史 active revision 不得覆盖最新 revoked revision。
成功输出只包含 Project、target、version、state、可选 role 和 created timestamp。 不返回 mutation ID、changedBy、credential、pepper、数据库路径或内部 audit。
4. list 只列出每个 subject 的最新 revision
policy.role-binding.list 要求:
limit为 1–64;state为active|revoked|all;role为owner|admin|operator|viewer|all;- 可选 cursor 精确包含
{subjectType, subjectId}。
SQLite 先以相关子查询选择每个 (projectId, subjectType, subjectId) 的 max version,
再应用 state/role filter,按 (subjectType ASC, subjectId ASC) 查询 limit + 1。
只有确有下一条时才返回最后一条可见 binding 的 cursor。role != all 自然排除
role 为 null 的 revoked binding。
禁止返回历史 revision、offset、任意排序、模糊搜索、客户端 SQL 片段或无界数组。
每一页是独立授权、独立审计的 current-head snapshot,不是跨 command 的长事务。 翻页期间发生 put/revoke 时,要求严格同一时点清单的 operator 必须从第一页重查。
5. 查询与审计使用同一个最终事务
query command 不携带 mutation ID、expected version 或 failure-audit ID,只携带单一 audit event ID。成功查询与 allowed audit 原子提交;credential/Project/Owner fence 在服务授权后漂移时事务回滚,CLI 以同一 event ID 写低敏 denial audit。
不采用方案
直接暴露 ProjectPolicyRepository.resolve
拒绝。它只解析一个 subject 的 Policy snapshot,不提供 actor Owner authority、 credential fence 或同事务 audit,也会把内部 repository 变成产品 transport。
返回全部 append-only 历史
拒绝。产品 mutation 需要 current version,历史列表容易误判撤权状态且输出无界。 历史审计应由未来专门的 bounded audit/query contract 提供。
只允许实例 authority Project Owner 查询所有 RoleBinding
拒绝。这会破坏 Project-scoped RBAC 自治,并迫使 secondary Project 的日常成员管理 依赖实例根 Owner。
另建 RoleBinding query package 或管理 daemon
拒绝。查询与 mutation 共用 Project authority、SQLite、CLI 和交付闭包;拆包或常驻 服务增加供应链碎片和路由设备空闲成本。
影响
正向影响:
- put/revoke 的 expected version 不再要求直接 SQL;
- append-only 历史被明确折叠为每个 subject 的 current head;
- Project Owner 自治与实例拓扑 authority 保持分离;
- 64 条硬上限约束 CPU、内存和输出;
- put/revoke/query 共用同一个最终 Owner fence helper;
- SQLite contract 保持 v37,workspace 保持 22 包。
代价与限制:
- 每次查询写一条 security audit,并短暂取得 SQLite write reservation;
- 当前不提供历史 revision、RoleBinding approval 或 break-glass 恢复;
- Cluster 继续使用独立 PostgreSQL/RBAC management transport。
验证
- GitNexus:SQLite append method 1 个直接上游、CLI request normalizer 3 个上游, repository/class/service factory 均为 LOW,未命中已索引执行流程;
- 真实 SQLite/Owner CLI 18/18:
- inspect 只返回 revoked current v3,不回退历史 active;
- missing 返回
found:false; - 两页 keyset 覆盖四个最新 subject,无重复或静态遗漏;
- active viewer filter 只返回当前 viewer;
- secondary Project Owner 可查询自己的两个 Owner;
- secondary Owner 查询 foreign Project 被拒绝并写 denial audit;
- Project admin 查询被
permission_missing拒绝; - limit 65 在打开数据库前拒绝;
- credential 在服务授权后漂移时,最终 inspect 事务拒绝且不提交 allowed audit;
- Project lifecycle/query、RoleBinding mutation、防锁死和容量回归;
- local-admin 与 local-owner-cli strict TypeScript 通过;
- runtime-core/local-sqlite 完整 TypeScript 仍只受锁定依赖未物化影响,本切片未出现 类型错误;
- 不新增 migration、package、生产依赖或 deployment contract 版本。