Files
qinglong/docs/adr/ADR-0214-owner-fenced-bounded-local-role-binding-query.md
T

6.5 KiB
Raw Blame History

ADR-0214Owner 围栏化、有界的 Local RoleBinding 查询

  • 状态:Accepted
  • 日期:2026-07-29
  • 关联 RFCQL-RFC-0001 D-05、D-27、D-37、D-65、D-72、D-73、D-175、 D-198、D-200、D-203、D-204
  • 关联 ADRADR-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,重新验证:

  1. authenticated credential、Identity、有效期和 pepper provenance
  2. 目标 Project active/version
  3. 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 为 164
  • stateactive|revoked|all
  • roleowner|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 保持 v37workspace 保持 22 包。

代价与限制:

  • 每次查询写一条 security audit,并短暂取得 SQLite write reservation
  • 当前不提供历史 revision、RoleBinding approval 或 break-glass 恢复;
  • Cluster 继续使用独立 PostgreSQL/RBAC management transport。

验证

  • GitNexusSQLite 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 版本。