5.3 KiB
ADR-0210:Owner 围栏化 Local Identity/Credential 查询
- 状态:Accepted
- 日期:2026-07-29
- 关联 RFC:QL-RFC-0001 D-05、D-27、D-37、D-65、D-72、D-73、D-175、 D-197、D-198、D-199、D-200
- 关联 ADR:ADR-0074、ADR-0075、ADR-0077、ADR-0086、ADR-0185、 ADR-0207、ADR-0208、ADR-0209
背景
ADR-0209 已提供 Identity/Credential mutation 的产品入口,但每次 mutation 都要求
expectedCurrentVersion。部署者若只能直接查询 SQLite 才能取得该版本,会产生三个
问题:
- 运维流程必须知道存储表结构,产品契约与实现泄漏;
- 普通 credential 行包含 secret digest 和 pepper provenance,直接查询扩大敏感面;
- 查询没有复用 Owner authorization、credential fence 与 Security Audit。
单纯在服务层 authorize 后调用普通 resolve 也不安全。actor credential 或 Owner
RoleBinding 可能在 authorization 与读取之间变化,造成已撤权主体继续读取管理状态。
决策
1. 扩展既有边界,不新增 package 或 migration
能力分别进入既有精确 subpath:
- runtime-core 定义 authorized inspection repository contract;
- local-admin 提供 Owner-only inspection service;
- local-sqlite 实现事务围栏读取与 audit;
- local-owner-cli 在既有
ql3-identity增加产品命令。
不新增第 23 个 package、数据库表、第三方依赖、daemon、timer、watcher、listener 或 远程管理 API。Edge/Standalone 常驻 application closure 不导入该 authority。
2. v1 只提供精确查询
开放:
identity.inspect:按 exact{subject.type, subject.id}查询;credential.inspect:按 exact credential ID 查询。
command 使用 current UID 0600 private file。request 只包含 projectId、target 或
credential ID、requestId 和 UUIDv4 auditEventId。查询不是 mutation,不接受
mutation ID、failure-audit ID 或 expected version。
本切片不提供 list/search/pagination。精确查询足以获得后续 CAS 所需版本,同时避免 为低配设备引入目录扫描、游标状态或大结果集。
3. Owner authorization 与读取在最终事务重新围栏
服务要求 strong User Principal,并固定申请 project.manage。deny、
approval-required 和 policy-unavailable 先写低敏 audit,再返回统一错误。
SQLite 使用 BEGIN IMMEDIATE,在读取前重新验证:
- 本命令认证 credential 的 version/state、Identity、时间窗与 pepper provenance;
- Project active 状态和 Policy fence project version;
- actor 最新 RoleBinding 的 version/state/Owner role。
随后读取最新 Identity 或 credential revision、写入 allowed audit,并一次提交。任一 围栏漂移都回滚读取对应的 allowed audit,并失败关闭。
4. 不存在语义和输出必须低敏
只有完成 Owner authorization 与事务围栏后,精确对象不存在才返回 found:false。
未授权请求不得根据对象是否存在返回不同结果。
允许输出:
- Identity:subject、status、version、created/updated time;
- credential:ID、subject、subject status、state、version、created/not-before/ expires time。
禁止输出 secret digest、pepper key ID、token、credential presentation、数据库路径、 绝对交付路径或内部 mutation/audit 行。
替代方案
直接提供 SQLite 查询文档
拒绝。它把 schema 变成产品 API,绕过权限与 audit,并暴露本来无需展示的敏感列。
authorize 后调用普通 repository resolve
拒绝。authorization 与读取不共享最终数据库 transaction,存在 credential revoke 或 Owner demotion TOCTOU。
新建只读管理 daemon 或 HTTP API
拒绝。当前需求是本机短生命周期运维;常驻进程会增加路由设备内存、端口和攻击面, Cluster 也有独立 transport/authority。
立即提供 Identity/Credential list
暂不采用。list 需要分页、稳定 cursor、容量与枚举策略;精确 inspect 已解决 CAS 版本发现,不应扩大本切片。
影响
正向影响:
- 部署者不再直接访问 SQLite 即可完成 inspect → CAS mutation;
- 查询与 mutation 使用相同 Owner、credential 和 Policy fence;
- 已授权 not-found 有明确机器可读语义,输出不暴露 credential 验证材料;
- 低配设备没有新增常驻成本,workspace 保持 22 package。
代价与限制:
- 每次 inspect 都是短写事务,因为 allowed audit 必须与读取 authority 同事务;
- 相同
auditEventId不作为可重复消费的查询 cursor,重复人工执行应生成新 command; - v1 不支持批量列表,operator 必须知道 exact subject 或 credential ID;
- Cluster 仍需独立实现 PostgreSQL/RBAC 查询 transport。
验证
- strict targeted TypeScript 编译覆盖 runtime contract、local-admin、local-sqlite 与 CLI;
- service 测试证明 Owner allow、admin/non-Owner deny 与 deny audit;
- 真实 SQLite/CLI 测试证明 Identity/Credential 当前 version、已授权
found:false、allowed audit 与低敏 JSON; - TOCTOU 测试在 policy authorize 后追加较新 non-Owner binding,最终 transaction 必须抛出 authorization fence conflict;
- dependency audit 继续证明能力没有进入 Edge/Standalone 常驻 closure;
- 完整 workspace、artifact/RSS 与 PostgreSQL HA 门在锁定依赖恢复后重跑。