Files
qinglong/docs/adr/ADR-0210-owner-fenced-local-identity-credential-inspection.md

5.3 KiB
Raw Permalink Blame History

ADR-0210Owner 围栏化 Local Identity/Credential 查询

  • 状态: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、D-199、D-200
  • 关联 ADRADR-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,在读取前重新验证:

  1. 本命令认证 credential 的 version/state、Identity、时间窗与 pepper provenance
  2. Project active 状态和 Policy fence project version
  3. actor 最新 RoleBinding 的 version/state/Owner role。

随后读取最新 Identity 或 credential revision、写入 allowed audit,并一次提交。任一 围栏漂移都回滚读取对应的 allowed audit,并失败关闭。

4. 不存在语义和输出必须低敏

只有完成 Owner authorization 与事务围栏后,精确对象不存在才返回 found:false。 未授权请求不得根据对象是否存在返回不同结果。

允许输出:

  • Identitysubject、status、version、created/updated time
  • credentialID、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 门在锁定依赖恢复后重跑。