7.6 KiB
ADR-0035:Approved 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.decide、project.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_factor、hardware或local_console; now - authenticatedAtMs <= 5 分钟。
single_factor、service、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 已可用。
未选择的方案
- owner/admin 登录即可恢复:single-factor session 不足以授权未知副作用终结,拒绝。
- 复用
approval.decide:审批新动作与裁决已发生但未知的动作风险不同,拒绝。 - 只在应用层检查 Policy:检查与提交之间存在 revoke/archive 竞态,拒绝。
- 终态提交后异步写审计日志:崩溃会产生不可证明的人工裁决,拒绝。
- 把完整 JWT/MFA 证明写数据库:扩大 Secret 与隐私暴露面,拒绝。
- 旧单用户环境自动提升为 local console:UID、TTY 或私有文件本身不能证明可信 ceremony,拒绝。
- 授权事实缺失时自动补写:无法还原历史认证与 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 目标门禁均通过。