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

126 lines
5.3 KiB
Markdown
Raw Permalink 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-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 门在锁定依赖恢复后重跑。