Files
qinglong/docs/adr/ADR-0035-approved-action-manual-recovery-authorization.md
T

7.6 KiB
Raw Blame History

ADR-0035Approved Action 人工恢复授权与强认证事实

  • 状态:Proposed
  • 日期:2026-07-19
  • 关联:QL-RFC-0001、ADR-0028、ADR-0029、ADR-0030、ADR-0031、ADR-0033

上下文

ADR-0033 只允许人工把 post-start unknown action 显式终结为 succeeded、failed 或 blocked,禁止把旧 dispatch 重置后再次执行。这个操作会改变审计事实,也可能掩盖一个已经发生但无法验证的外部副作用,因此普通登录、可变 username、Agent 身份或仅在应用层判断一次角色都不足以授权。

人工服务先做 Policy 判断、随后 Repository 写终态时还存在撤权竞态:如果 owner/admin binding 或 Project version 在两步之间变化,旧的 allow 结果不能继续生效。授权记录若在终态提交之后另写,又会留下“动作已经被人裁决,但无法证明当时认证与授权上下文”的崩溃窗口。

因此人工恢复必须把稳定 User、近期强认证、Project Policy version fence 和低敏授权事实绑定到同一个终态事务。

决策

1. approval.recover 是独立的 Project permission

人工恢复只请求 approval.recover,不复用 approval.decideproject.manage 或通配 Tool 权限。固定角色矩阵为:

  • owner、admin:允许;
  • operator、viewer:拒绝;
  • 未绑定、已撤销或 archived Project:默认拒绝。

首版继续使用静态 Policy Core;未来接 OPA 或自定义策略时也必须返回 Project/RoleBinding version fence,不能只返回一个布尔 allow。

2. 只接受仍有效的稳定 User principal

应用服务接收 ADR-0029 的 exact-shape AuthenticatedPrincipal,并在读取 recovery storage 前验证:

  • subject.type=user
  • principal 在服务时钟上满足 authenticatedAtMs <= now < expiresAtMs
  • assurance 为 multi_factorhardwarelocal_console
  • now - authenticatedAtMs <= 5 分钟

single_factorservice、Agent、API App、MCP Client、Worker 和 System 均不能进行人工恢复。legacy twoFactorActivated 仍不是 MFA 事实;只有未来可信 authentication adapter 验证真实 ceremony 后才能产生上述 assurance。本 ADR 不定义 MFA 协议、恢复码或 local-console issuer。

3. Policy 判断必须携带可提交的 version fence

服务从 recovery snapshot 取得 immutable Project ID,以稳定 User 请求 approval.recover。只有 allow 且 Project version、当前 RoleBinding version 都存在时才构造授权事实。

SQLite Repository 在同一个 BEGIN IMMEDIATE 中重新读取最新 Project 与该 User 的 RoleBinding version:任一 version 不一致即以 fence rejection 失败,且在写 recovery resolution 或 execution 终态前退出。这样 revoke、role change、Project archive 或其他 version mutation 无法穿透已经计算出的 allow。

4. 0024 授权事实与人工终态原子提交

ApprovedActionRecoveryAuthorizationFacts 与 human resolution 一对一,保存:

  • dispatch、Project、manual mutation ID 和稳定 User ID
  • authentication ID、强 assurance、认证时间;
  • Policy 判断使用的 Project/RoleBinding version
  • 授权时间;
  • 对以上 canonical 字段计算的 SHA-256 fact digest。

表只允许 User 和三种强 assurance,要求正 version、认证时间不晚于授权时间且间隔不超过五分钟,并通过外键绑定 recovery resolution 与 Project。它不保存 cookie、JWT、MFA secret、recovery code、请求 header 或无界理由。

Repository 在一个事务内依次复验 execution/recovery fence、复验 Policy version、插入 human resolution、推进 execution/control 终态并插入授权事实。任一约束、trigger 或 commit 失败全部回滚,不能留下“有终态无授权事实”或“有授权事实无终态”。automatic evidence resolution 不创建人工授权事实。

5. 幂等重放必须包含相同授权事实

相同 manual mutation 只有在 decision、reason、evidence、User、时间和 authorization fact 全部精确一致时返回原结果。字段漂移、digest 不匹配或一条旧 human resolution 缺少 0024 fact 时不补写、不重新解释,稳定返回 terminal/conflict 语义。

服务在重放前仍验证当前 principal 和当前 Policy;历史 allow 事实只用于审计与精确幂等,不成为永久授权 capability。

6. 当前保持 production unreachable

本切片已经实现 permission matrix、强认证应用服务、0024 migration、SQLite 原子 Repository 和 contract tests,但没有注册到 app、loader、legacy service、API、CLI、MCP 或 UI。生产启用仍需要:

  • 真实 MFA/hardware/local-console authentication adapter 与 credential 生命周期;
  • 人工恢复列表、低敏证据预览、二次确认、rate limit 和审计 API/UI
  • 对外错误屏蔽,避免未授权调用者区分 dispatch 是否存在;
  • profile-aware recovery lifecycle、指标、告警与积压门禁;
  • PostgreSQL adapter 与多副本 revoke/resolve 竞争 contract
  • edge 设备和 cluster 节点上的故障注入与资源门禁。

影响

正面影响:

  • 高风险 unknown 终结不能由 Agent、普通 session 或 operator 越权执行;
  • 授权撤销竞态由数据库 version fence 关闭;
  • 认证、授权与终态形成一个可验证、低敏、原子的审计事实;
  • edge 只增加一次人工操作时的短事务和单行事实,不增加 watcher、timer 或常驻连接;
  • cluster adapter 可以复用相同语义,通过 PostgreSQL row lock 实现多副本一致裁决。

代价与风险:

  • 单用户部署若没有可信强认证入口,将不能使用人工恢复;这是安全门禁,不应降级为密码确认;
  • 每条人工 resolution 增加一行保留事实和关联索引;
  • authentication ID 虽非 Secret,仍属于安全审计标识,API 默认不得向普通读取者暴露;
  • 当前只有 SQLite adapter,不能据此宣称 cluster-control 已可用。

未选择的方案

  1. owner/admin 登录即可恢复single-factor session 不足以授权未知副作用终结,拒绝。
  2. 复用 approval.decide:审批新动作与裁决已发生但未知的动作风险不同,拒绝。
  3. 只在应用层检查 Policy:检查与提交之间存在 revoke/archive 竞态,拒绝。
  4. 终态提交后异步写审计日志:崩溃会产生不可证明的人工裁决,拒绝。
  5. 把完整 JWT/MFA 证明写数据库:扩大 Secret 与隐私暴露面,拒绝。
  6. 旧单用户环境自动提升为 local console:UID、TTY 或私有文件本身不能证明可信 ceremony,拒绝。
  7. 授权事实缺失时自动补写:无法还原历史认证与 Policy snapshot,拒绝。

验证要求

  • owner/admin 允许,operator/viewer/未绑定/revoked/archived 拒绝;
  • 非 User、过期 principal、single-factor/service 或超过五分钟的强认证在 storage read 前拒绝;
  • Project/RoleBinding 在 Policy allow 后变化时,Repository 在任何终态写入前 fence rejection
  • resolution、execution/control 终态与 authorization fact 同事务提交,fact 插入失败全部回滚;
  • fact exact-shape、强 assurance、时间窗口、version、digest 或 identity 任一漂移均 fail closed
  • 精确 manual mutation 重放返回同一结果,变形重放不得覆盖原事实;
  • automatic evidence resolution 不依赖或伪造人工授权事实;
  • migration/schema ownership 覆盖表、索引、约束和外键;
  • production reachability 搜索证明服务和 Repository 未被旧 app/loader/API/service 路径导入;
  • Node 22 兼容门禁与固定 Node 24 目标门禁均通过。