feat: harden privileged identity execution governance
Build and Test Quality Gate / Contract Web typecheck and build (pull_request) Successful in 3m37s
Build and Test Quality Gate / Python tests and Contract API migration (pull_request) Failing after 8m39s
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 5m44s

This commit is contained in:
2026-08-09 20:51:28 +08:00
parent f32afb363c
commit 13a3d46e93
27 changed files with 8123 additions and 125 deletions
@@ -783,3 +783,262 @@ Contract API 单进程全量回归在 10 分钟命令上限处被终止且未返
本地运行 SQLite 库升级前为 0040,outbox 行数、跨操作事件错配和非法租约计数均为 `0`;停止 Contract API 后已生成 `contract-api-20260802-before-0041-20260803-012502.sqlite3` 备份,再升级到 0041。补充 SQLite 触发器后执行 0041 downgrade/upgrade 循环,确认复合索引、复合外键、租约 CHECK 和两条关联拒绝触发器均存在;Contract API 重启后 `8110` 健康检查返回 `200`,Web `3021` 与 AI Platform `8111` 未重启。
本阶段没有新增或改变任何 API、DTO、Web 页面或 worker transport,不增加人工 retry/requeue/ack,不启用 SMTP/工单/Webhook,不开放 execute、rollback、merge、split、recover、grant、revoke、support access 或其他身份执行入口。审批完成仍只表示治理审批状态,不代表身份数据已变更。
## 24. 外发安全契约与消费者幂等检查点(2026-08-03)
本检查点冻结真实 transport 上线前的 provider-neutral 契约。现有数据库 outbox payload 继续作为内部事务记录,但 transport 不再接触 `ClaimedPrivilegedIdentityMessage` 或内部 payload;发送边界只允许接收经过 allowlist 重建、摘要和签名的安全 envelope。
### 24.1 Egress envelope
固定 contract version 为 `privileged-identity-egress.v1`,字段仅包括:
- contract version、key ID;
- HMAC 派生的外部 delivery key;
- effect type、event type、operation type;
- status before/after;
- occurred at;
- body digest 和 signature。
不得外发 message/event/operation ID、内部 idempotency key、trace ID、sequence number、目标引用或指纹、snapshot/hash、actor、tenant/user/account ID、reason、ticket、evidence、审批评论、邮箱、凭据或原始错误。实现从内部 payload 逐字段 allowlist 重建 envelope,不得通过删字段方式转发原对象。
外部 delivery key 使用不少于 32 bytes 的独立、稳定幂等密钥和专用 HMAC domain 从内部幂等键派生;body 使用排序、无空白的 ASCII JSON 规范化并计算 SHA-256 digest;signature 使用另一把不少于 32 bytes、带 key ID 的可轮换签名密钥。签名轮换不得改变同一消息的 delivery key,避免待重试消息被消费者视为新副作用。缺失/短密钥和非法 key ID 在 claim 前失败,日志不得输出两类密钥、签名或 envelope 原文。
### 24.2 Receipt 与消费者幂等
transport 必须返回类型化 receipt:`accepted`、`duplicate`、`retryable_failure` 或 `permanent_failure`。发送方同时核对 delivery key 和 body digest;只有匹配的 accepted/duplicate 可标记 delivered。retryable 使用既有指数退避;permanent、未知 receipt、key/digest 不匹配直接 dead-letter,数据库只记录稳定错误码,不记录响应正文。
参考消费者以 `(consumer_id, delivery_key)` 为唯一 receipt:
- 首次合法 envelope 在业务副作用成功后保存 digest 并返回 accepted;
- 同 key/同 digest 返回 duplicate,不重复副作用;
- 同 key/不同 digest 返回永久 `idempotency_conflict`;
- 未知 schema major、签名失败、载荷超限永久拒绝;
- 超时、限流或临时依赖不可用归类 retryable。
内存参考实现仅用于契约测试。生产消费者必须在同一个持久化事务中提交 receipt 与业务副作用,才能覆盖“副作用成功但 ACK 丢失”后的安全重投;不得把内存去重或进程内锁解释为生产 exactly-once。
### 24.3 Worker 边界和验收证据
- worker 已移除 `run_once(deliver=...)` 测试回调旁路;启用时必须显式注入 transport 与 delivery contract。
- 默认 disabled transport 继续返回 0 且不 claim;enabled transport 缺 contract 在 claim 前失败。
- payload hash 在构建 envelope 前继续校验;契约字段非法永久 dead-letter。
- accepted/duplicate 确认 delivered;retryable 进入 retry_scheduled;permanent 或 receipt mismatch 进入 dead-letter。
- 新增契约/worker 定向测试 `8/8`,覆盖强密钥、字段红线、固定生命周期值、带时区时间、稳定 HMAC/signature、key 轮换、重复确认、幂等冲突、ACK 丢失、签名/schema 拒绝、claim 前配置失败、retry 恢复和 receipt mismatch。
- 特权身份操作 `20/20`、SQLite 0041 迁移 `2/2` 通过。真实 PostgreSQL 隔离 schema `1/1` 通过,确认并发 claim、租约恢复前后 envelope 完全一致、内部关联值不外发、重试/dead-letter、0041 完整性和测试 schema 清理。
本阶段未新增迁移、API、Web 或运行入口,未连接真实 transport。SMTP、工单和通用 Webhook 继续禁止;后续 provider 必须具备固定目标 allowlist、TLS/服务认证、durable ACK、消费者持久化 inbox、重试/背压、DLQ 保留、访问审计和密钥轮换。身份执行器、人工 outbox retry、support access 和所有破坏性身份写入口继续关闭。
## 25. 统一执行器只读 preflight 与固定锁计划检查点(2026-08-03)
本检查点只建设未来统一执行器的验证契约,不建设或开放执行器。新增平台管理员只读 `GET /api/v1/identity/privileged-operations/{operation_id}/execution-preflight`;没有新增 execute、rollback、merge、split、recover、grant、revoke、support access 或其他身份写 API。响应固定返回 `locks_acquired=false` 和 `executor_enabled=false`,页面不得提供执行按钮。
### 25.1 只读复核规则
- 复核操作存储状态、有效到期状态和两名不同的当前有效平台管理员审批;请求人审批不计入 quorum。
- 复核审批有效期、审批 preview hash、审批时保存的平台权限 revision,以及审批人当前账号、成员和平台权限状态。
- 使用提交时保存的原始 `preview_expires_at` 重新构建 preview 并比较 hash;新增加法迁移 `20260803_0042` 保存该字段。历史记录保持 `NULL`,不猜测回填,稳定阻断码为 `preview_verification_material_missing`。
- 逐项比较批准时的 expected revision 与当前身份实体 revision,并再次运行当前目标状态校验。任一漂移或当前状态非法均使 `ready=false`。
- preflight 不调用过期物化,不追加事件/outbox,不修改 operation/approval 或身份实体,也不获取真实 `FOR UPDATE` 锁。到期只体现在 `effective_status=expired` 和 blocker 中。
稳定 blocker 包括 `operation_expired`、`operation_not_approved`、`approval_quorum_missing`、`approval_expired`、`approval_preview_mismatch`、`requester_approval_present`、`approver_not_current`、`approval_permission_revision_mismatch`、`preview_verification_material_missing`、`preview_verification_material_invalid`、`current_state_invalid`、`preview_hash_mismatch`、`expected_revisions_missing`、`expected_revisions_invalid`、`expected_revisions_mismatch`、`revision_mismatch` 和 `unsupported_lock_resource`。
### 25.2 固定锁计划
preflight 只返回未来执行事务必须遵循的描述性锁计划,锁模式固定标注为 `for_update`,但本接口不真正取锁。同一资源类型按 `entity_id` 排序,跨类型顺序固定为:
1. `privileged_operation`
2. `privileged_approval_set`
3. `identity_account`
4. `workspace`
5. `tenant_user`
6. `workspace_membership`
7. `identity_account_credential`
8. `tenant_user_credential`
未来执行器必须在单一 PostgreSQL 事务内按该顺序重新取锁并重新执行全部检查;不得把本次 GET 的 `ready=true` 当作可复用授权票据。`ready` 只表示查询时刻检查通过,不表示身份数据已更改、执行 API 已获准上线或锁已被持有。
### 25.3 展示、验收与边界
- Web 详情只读展示检查结论、稳定 blocker、revision 对比、固定锁计划、原始 preview 有效期和两个关闭标志;客户端只发 GET。
- SQLite 特权操作专项 `23/23`、安全投递契约 `6/6`、迁移专项 `2/2`(组合 `31/31`)、Web 全量源码测试 `37/37`、页面专项 `5/5`、TypeScript、Python compileall 和 Alembic 单一 head `20260803_0042` 已通过。
- 真实 PostgreSQL 隔离 schema 专项 `1/1` 通过:双审批后 ready;目标 revision 漂移后返回 `revision_mismatch`;两次查询前后 operation、approval、event、outbox 及账号、工作空间、用户、成员关系全字段快照完全一致;随机测试 schema 已清理。
- OpenAPI 只增加一个 GET,preflight 路径不存在 POST/PUT/PATCH/DELETE;全局破坏性身份写路径仍为 0。
本检查点不是执行器上线批准。下一步在任何真实身份写入前,仍需独立设计并评审事务执行状态机、固定锁的真实获取、幂等 execution attempt、失败补偿、outbox 副作用、support access 在线鉴权与自动到期,以及独立 rollback 预览/审批/演练。真实 transport 也继续受第 24 节门禁约束。
本地运行库已在停止 Contract API 后从 `0041` 升级到 `0042`;停服后的可校验备份为 `.runtime/db-backups/contract-api-20260802-before-0042-20260803-041600.sqlite3`。升级后 Contract API `8110` 重启并返回 `200`,Web `3021` 与 AI Platform `8111` 保持运行;运行态 OpenAPI 中 preflight 方法仅为 GET,身份域破坏性路径数量为 `0`。
## 26. 审批 SLA 临期与超期只读队列检查点(2026-08-03)
本检查点增加平台运营可见性,不改变治理状态机。新增 `GET /api/v1/identity/privileged-approval-sla/overview` 和 `GET /api/v1/identity/privileged-approval-sla/queue`;两个接口只读取 stored `pending_approval` 操作、审批和审批人当前有效性,不调用过期物化、claim、retry 或身份执行逻辑。
### 26.1 SLA 口径与最小披露
- 默认观察窗口为最近 `7` 天创建的待审批操作,可配置 `1..31` 天;默认临期阈值为 `3600` 秒,可配置 `60..86400` 秒。
- `expires_at <= generated_at` 为 `breached`;未超期且进入阈值为 `due_soon`;其余为 `on_track`。队列固定按 `breached -> due_soon -> on_track`、`expires_at`、`operation_id` 排序。
- 有效审批计数沿用 preflight 规则:审批未过期、preview hash 一致、审批时 permission revision 未漂移,且审批人当前账号、用户、成员关系和平台权限仍有效。响应不披露审批人。
- overview 仅返回生成时间、观察窗口、临期阈值、总量与三档计数。queue 仅返回不透明 operation ID、operation type、三档状态、有效/所需/剩余审批数、创建/到期时间和距到期秒数。
- 禁止返回 reason、ticket、target/evidence refs、审批评论、actor、邮箱、姓名、账号/用户/成员 ID、preview、revision、payload 或凭据类字段。
### 26.2 只读与运营边界
- 超期只报告 `breached`,不把 operation 落库为 `expired`,不追加 event/outbox,也不修改 approval 或身份实体。
- 两个 GET 仅允许当前有效平台管理员,并纳入 `identity.privileged_approval_sla.read` 敏感 `S3` 反滥用策略;不存在 POST/PUT/PATCH/DELETE、人工处置或身份动作。
- Web 治理页增加审批时效摘要、状态筛选、分页队列、有效/剩余审批数和到期提示。页面只调用 GET,不增加 approve/reject、execute、retry 或 requeue 动作。
- 观察窗口外的记录仍保留在治理操作、事件和审计证据中;本检查点不删除、归档或改写历史记录。运营值班应扩大窗口复核历史积压,不得把窗口过滤解释为记录不存在。
### 26.3 验收证据
- SQLite 特权身份、反滥用、迁移和安全投递组合 `45/45` 通过;其中 SLA 定向覆盖权限、三档边界、窗口、稳定排序、分页、过滤、有效审批语义、最小披露和查询前后治理快照完全一致。
- 真实 PostgreSQL 隔离 schema `1/1` 通过,覆盖 `breached/due_soon/on_track`、最小字段扫描、查询前后 operation/approval/event/outbox 快照一致性及测试 schema 清理。
- Web 全量源码测试 `38/38`、SLA 所在页面专项 `6/6`、TypeScript、Python compileall、Alembic 单一 head `20260803_0042` 和 `git diff --check` 通过。
本阶段不新增迁移,不启用 transport、身份执行器、support access 或任何 execute/rollback/merge/split/recover/grant/revoke 写入口。`breached` 是运营告警状态,不是已执行、已拒绝或已物化过期状态。
## 27. 跨时间窗长期审批积压只读可见性检查点(已验收)
本检查点只补齐审批 SLA 的跨时间窗运营可见性,不改变 operation/approval 状态机,不物化 `expired`,不引入任何身份执行或处置写入口。目标是让值班人员区分“当前观察窗口内的待审批风险”和“窗口外仍未完成的长期积压”,避免把窗口过滤误解为数据不存在。
### 27.1 只读契约与全量/窗口口径
- overview 同时返回当前查询时刻、查询口径、临期阈值、窗口天数,以及窗口内 pending 数、全量 pending 数、窗口外 pending 数、窗口外已超期数和最老 pending 年龄。全量指标只统计存储状态仍为 `pending_approval` 的操作,不调用 `list_operations` 或 `_materialize_expired_operations`。
- queue 提供 `scope=window|all`。`scope=window` 沿用窗口天数筛选,`scope=all` 查询所有仍处于 `pending_approval` 的记录;两者均不修改操作、审批、事件、outbox 或身份实体。默认维持 `scope=window`,非法 scope 返回稳定 `422`,响应回显实际 scope。
- `breached` 只表示当前查询时刻 `expires_at <= generated_at`;它不是状态迁移、执行授权、拒绝结果或已写入的 `expired`。`due_soon` 的边界必须在 API、Web 和测试中保持一致。
- 队列仍按风险级别、到期时间、不可变 operation ID 稳定排序并有界分页;全量查询不得泄露 reason、ticket、target/evidence、actor、审批评论、邮箱、姓名、账号/成员身份、preview、revision、payload 或凭据字段。
### 27.2 Web、权限与反滥用
- 仅当前有效平台管理员可读取 overview/queue;普通个人用户、企业普通成员、企业管理员、已撤销平台权限的旧会话和未登录请求分别返回既有认证/授权错误,不因 scope 切换扩大权限。
- Web 只提供“窗口内/全部积压”分段控件、摘要指标和只读分页队列;客户端只发 GET,不出现 approve/reject、execute、retry、requeue、cancel 或身份动作按钮。
- 两个 GET 归入敏感 `S3` 反滥用策略,限流与审计只记录路由、查询口径、阈值和不透明关联 ID,不记录原始目标、reason、ticket、审批评论或身份详情。
### 27.3 验收证据与明确非目标
- SQLite/API 测试覆盖窗口内/窗口外/全量计数、空队列、超期和临期边界、scope 非法值、稳定排序分页、权限拒绝、最小披露及 GET 前后 operation/approval/event/outbox/身份快照不变。
- 真实 PostgreSQL 隔离 schema 专项必须复核全量与窗口查询的一致计数、并发只读安全、快照不变和测试 schema 清理;不得以 SQLite 结果替代真实数据库证据。
- Web 页面专项、Web 全量源码测试、TypeScript、Python compileall、Alembic 单一 head 和 `git diff --check` 均需单列结果;运行态 OpenAPI 中本检查点只新增 GET,破坏性身份写路径保持 `0`。
- 本检查点不新增迁移,不新增 execute/rollback/merge/split/recover/grant/revoke/support access 或人工审批/消息处置写接口;不启用 transport,不连接 SMTP、工单、Webhook,不改变 retention、归档和删除策略。身份执行器继续关闭。
验收结果(2026-08-03):SQLite 特权身份、反滥用、迁移和安全投递组合 `45/45`,真实 PostgreSQL `192.168.3.90:5432/yuqei_contract` 隔离 schema 专项 `1/1`,Web 全量源码测试 `39/39`、页面专项 `7/7`,TypeScript、Python compileall、Alembic 单一 head `20260803_0042` 与 `git diff --check` 均通过。运行态 OpenAPI 中 overview/queue 均仅注册 GET,身份域 execute/rollback/merge/split/recover/grant/revoke/support-access/retry/requeue/ack 破坏性写路径为 `0`。已登录浏览器在桌面和 `390x844` 窄屏下完成 scope 切换、文本容纳、无页面水平溢出和控制台 error `0` 验证;Contract API、Web 与 AI API 继续运行,身份执行器和 transport 保持关闭。
## 28. 追加式执行尝试账本与失败状态机检查点(已验收)
本检查点建立未来统一执行器所需的内部审计账本,但不实现执行器、身份写入、补偿动作或任何运行时写接口。账本只记录内部调用方显式登记的尝试和状态事件;当前状态从最后一条不可变事件派生,不写回 operation、approval、身份实体或 outbox。
### 28.1 追加式存储与幂等契约
- `privileged_identity_execution_attempts` 保存不可变尝试根:`attempt_id`、`operation_id`、递增 `attempt_no`、幂等键、规范化请求/preview/preflight 的 SHA-256、脱敏 `trace_id` 和创建时间。注册仅接受已审批且未过期、请求哈希和 preview 哈希与原 operation 一致的记录。
- `privileged_identity_execution_attempt_events` 保存不可变状态事件:事件类型、前后状态、稳定错误码、脱敏快照及其 SHA-256、确定性 event payload hash、`trace_id` 和发生时间。相同尝试内 `(attempt_id, sequence_no)` 与 `(attempt_id, event_payload_hash)` 均唯一。
- 相同幂等键和相同材料返回原 attempt;相同幂等键绑定不同材料返回稳定 `409`。相同事件材料重放返回原状态,不增加事件行。PostgreSQL 注册在所属 operation 行上串行化 attempt number 分配;事件追加按 attempt 行串行化。
- attempt 根和事件在 SQLite/PostgreSQL 均由数据库触发器禁止 UPDATE/DELETE;外键使用 `NO ACTION`。应用层自动建表路径使用同等保护,不能以绕过 Alembic 的方式获得可变账本。
### 28.2 精确状态机、错误与快照边界
- 唯一合法转换为 `prepared -> running|aborted`、`running -> succeeded|failed|compensation_required`、`compensation_required -> compensated|compensation_failed`。`succeeded`、`failed`、`compensated`、`compensation_failed`、`aborted` 均为终态,不得继续迁移。
- event type 与 `status_after` 固定映射;调用方必须声明预期 `status_before`,持久化层以数据库中最后事件复核,漂移返回稳定冲突,不猜测或自动跳转状态。
- 失败只接受有限稳定错误码,不接收原始异常、堆栈、Provider 响应、凭据或自由文本。审计快照使用字段和类型双重 allowlist,只允许状态、操作类型、计数、哈希、稳定结果码及布尔标志;target、evidence、actor、邮箱、姓名、身份详情和凭据字段会被拒绝。
- `trace_id` 只接受安全格式;不合规值转为不可逆指纹。账本不保存或披露原始请求、preflight、快照外材料和身份写入结果。
### 28.3 只读可见性与运行边界
- 运行时只新增 `GET /api/v1/identity/privileged-operations/{operation_id}/execution-attempts`,仅当前有效平台管理员可读,继续受 `identity.privileged_operations.read` 敏感 `S3` 策略保护。
- 响应只包含 attempt ID、operation ID、序号、派生状态、事件数、最近稳定错误码、脱敏 `trace_id`、创建时间和最近事件时间,并固定 `executor_enabled=false`。不返回幂等键、request/preview/preflight hash、快照、原始错误、target/evidence、actor 或凭据。
- Web 只展示执行尝试历史的加载、空态、不可用态和最小字段,不提供 execute、retry、compensate、abort、rollback 或身份动作按钮;客户端只发 GET。
- OpenAPI 中 execution-attempts 只有 GET。身份域 execute/rollback/merge/split/recover/grant/revoke/support-access/retry/requeue/ack 等破坏性写路径保持 `0`;内部登记和事件追加方法不得注册为 FastAPI route。
### 28.4 验收证据与后续门槛
- SQLite 特权身份、反滥用、迁移和安全投递组合 `47/47` 通过;其中执行账本专项覆盖注册/事件重放、幂等冲突、敏感快照和原始错误拒绝、精确终态、补偿/中止合法路径、数据库不可变保护、最小披露、权限和 GET 前后治理/账本快照不变。
- Alembic head 为 `20260803_0043`;迁移专项 `2/2` 覆盖表、列、索引、外键、唯一/检查约束、SQLite 触发器,以及 `head -> 0042 -> head` 降级/重升级。所有 PostgreSQL 标识符均不超过 63 字符。
- 真实 PostgreSQL `192.168.3.90:5432/yuqei_contract` 隔离 schema `1/1` 通过,覆盖同幂等键并发只创建一个 attempt/prepared event、相同状态事件并发只追加一次、唯一约束和 UPDATE/DELETE 拒绝;随机 schema 已清理,凭据未落盘。
- Web 全量源码测试 `40/40`、页面专项 `8/8`、TypeScript 和 Python compileall 通过。运行态浏览器验收需在 Contract API 升级到 `0043` 并重启后复核桌面/窄屏、空态或已有尝试、控制台错误和 OpenAPI。
下一阶段仍不开放 execute API。优先实现内部固定锁真实获取和锁内二次 preflight 复核契约,证明锁顺序、锁超时、revision 漂移、审批/权限失效和事务释放;该阶段只允许无身份 mutation 的验证事务。身份执行器、真实 support access、补偿执行、rollback 和人工账本写入口继续关闭,直到身份事务原子性、失败补偿、outbox 副作用和安全评审全部独立验收。
## 29. 固定锁真实获取与锁内二次 preflight 检查点(已验收,2026-08-09)
本检查点把第 28 节的追加式账本与第 25 节的描述性锁计划连接为内部验证事务,但不开放执行器、不写入身份实体,也不注册 execute API。
### 29.1 内部验证事务契约
- 新增内部 `recheck_execution_preflight_under_lock()`,仅允许当前有效平台管理员上下文调用;方法不在 FastAPI 路由、OpenAPI 或 Web 客户端中暴露。
- PostgreSQL 事务先设置受界限的 `lock_timeout`,再按 operation、approval set、identity account、workspace、tenant user、workspace membership、global credential、legacy credential 的固定 rank 获取行锁;同类资源按 ID 排序。
- 为防止审批人或调用者权限在锁内失效,实际锁集合包含调用者及所有审批人的账号、工作空间、用户和成员关系保护行;公开 GET 锁计划仍只披露原始 preview 资源。
- 所有锁获取完成后显式失效 ORM identity map,再执行审批、到期、preview hash、permission revision、expected/current revision 和当前目标状态二次复核;锁等待后的读取不得复用锁前缓存。
- 事务只执行 SELECT、`SET LOCAL lock_timeout` 和内存校验,返回前验证 session 没有新增、脏或删除对象;事务无论 ready 或 blocker 均提交空事务并释放全部锁。
- 锁超时返回稳定 `423 execution_lock_timeout`,超时事务回滚并释放已经取得的 operation 和身份资源锁;非法超时参数返回稳定 `422 execution_lock_timeout_invalid`。
### 29.2 验收证据与边界
- 真实 PostgreSQL `192.168.3.90:5432/yuqei_contract` 隔离 schema `1/1` 通过,覆盖真实 `FOR UPDATE` 顺序、相反 source/target 并发无死锁、50ms 锁超时、超时后锁释放、锁内 revision 漂移、审批过期、审批人失权以及事务前后治理和身份快照不变;隔离 schema 已清理,凭据未落盘。
- Web 全量源码测试 `41/41`、页面专项 `9/9`、TypeScript、Python compileall 和 `git diff --check` 通过;运行态 execution-attempts 仅 GET,身份破坏性写路径为 `0`。
- 桌面运行态和 `390x844` 窄屏治理页均无水平溢出,详情页显示“执行尝试历史”和“只读 · 执行器关闭”,控制台无应用 error;浏览器窄屏覆盖已恢复默认 viewport。
下一阶段仍不开放 execute API。优先建设确定性身份变更计划与逆向补偿计划的内部生成和哈希,再在隔离 PostgreSQL 中做事务演练、故障注入、原子回滚和 outbox 一致性验证;任何真实身份 mutation、support access、transport、rollback 或人工账本写入口继续关闭。
## 30. 确定性身份变更与逆向补偿计划检查点(已交付,2026-08-09)
本检查点只建立未来统一执行器可消费的内部计划材料,不执行身份 mutation,不写数据库,不投递 outbox,也不注册 execute API。support access 仍明确关闭。
### 30.1 计划契约
- 新增内部 `privileged_identity_plans` 模块和 `build_privileged_identity_change_plan()` 纯函数,覆盖 `account_merge`、`account_split`、`membership_recovery`、`platform_admin_grant`、`platform_admin_revoke` 五类身份变更;`support_access_grant` 返回稳定的 `support_access_plan_disabled`。
- 输入复用既有 `operation_type`、`target_refs`、`expected_revisions` 和固定 `lock_plan`。target refs 必须精确匹配操作字段,未知、缺失、重复、同账号、非法 partition、非法 recovery type、敏感字段和 stale/blocked lock 均 fail closed。
- 计划步骤和资源类型按固定操作模板生成;锁按 `privileged_operation`、approval set、资源 rank 和同类 resource ID 排序。输入字典、revision 和 lock 序列重排不改变输出。
- 计划快照不保留原始 target ref 值,只保留每个字段的 SHA-256 指纹;expected revision、锁当前/期望 revision 和固定步骤均纳入正向 `plan_hash`。补偿步骤严格按正向步骤逆序,并产生独立 `compensation_plan_hash`。
- repository 仅增加内部 `build_internal_change_plan()`,要求 fresh ready preflight;该方法无 FastAPI 绑定,禁止写入 operation、身份表、event、outbox 或 execution ledger。
### 30.2 验收证据与后续门槛
- 计划专项测试 `9/9` 通过,覆盖五类操作、正向/逆向顺序、顺序无关确定性 hash、未知/不完整/敏感 target refs、support access 关闭、stale/缺失 lock 拒绝、hash 篡改检测,以及 approved operation 的 repository 只读集成。
- Contract API Python `compileall` 和 `git diff --check` 通过;未新增迁移,运行态 OpenAPI 路由集合不变,身份执行器和所有破坏性写接口继续关闭。
下一阶段才可在隔离 PostgreSQL 中演练计划校验、故障注入、事务原子回滚和 outbox 一致性;在该专项与独立安全评审完成前,不得开放任何 execute、rollback、transport、support access 或补偿执行入口。
## 31. 隔离 PostgreSQL 事务、故障注入与 outbox 一致性检查点(已验收,2026-08-09)
本检查点仅在测试创建的随机 PostgreSQL schema 内演练未来身份事务边界,不增加运行时执行器、数据库迁移或 API。测试结束后精确删除 schema,真实运行数据和当前 SQLite 运行库均不参与演练。
### 31.1 演练契约
- 使用第 30 节的 `build_internal_change_plan()` 先生成并校验确定性计划,确认 `executor_enabled=false`、正向计划 hash 和补偿计划 hash 均存在;计划构建本身保持只读。
- 在同一 PostgreSQL 事务内演练目标平台权限投影、追加 operation event 和对应 outbox message;成功提交后,同一 operation 的 event 与 outbox 数量保持一一对应。
- 第一故障点位于身份投影 flush 之后、event/outbox 之前;注入异常后身份、operation、approval、event、outbox 和 execution ledger 全字段快照恢复到事务前状态。
- 第二故障点位于 event 与 outbox 均已 flush 之后;注入异常后身份投影、event 和 outbox 同时回滚,不允许孤立 event、孤立 outbox 或部分身份变更。
- 演练直接复用真实 PostgreSQL 外键、唯一约束和事务语义,但只作用于随机隔离 schema。测试代码没有注册 execute/rollback/compensate API,也没有把测试故障注入器接入运行时依赖图。
### 31.2 验收证据与后续门槛
- `192.168.3.90:5432/yuqei_contract` 隔离 PostgreSQL 专项 `1/1` 通过;覆盖计划校验、身份更新后故障、event/outbox 后故障、原子回滚、成功提交的一一对应关系和事务后快照。
- 专项执行后 `privileged_governance_%` schema 残留数量为 `0`;连接凭据只通过临时环境变量使用,未写入仓库、测试文件或文档。
- 本阶段仅新增测试覆盖,不增加 migration、生产 worker、transport、support access、人工账本写入口或 FastAPI 路由;身份执行器仍关闭。
下一阶段按顺序建设 support access 的在线授权检查、自动到期和紧急撤销,但仍先保持内部/受控验证,不开放通用身份代入或密码/会话共享;完成后必须单独进行身份执行与支持访问安全评审,再决定是否具备任何受限发布条件。
## 32. support access 在线校验、自动到期与紧急撤销检查点(已验收,2026-08-09)
本检查点只冻结未来支持访问的 validation-only 安全策略,不签发访问令牌、会话或 impersonation context,不接入 FastAPI、Web、worker 或 transport。所有校验结果固定 `access_granted=false`,即使当前材料满足策略也只返回 `policy_satisfied=true` 和 `support_access_executor_disabled`。
### 32.1 validation-only 策略
- 仅接受已审批、两名有效审批、preview hash 当前有效且不超过 24 小时的 support access 材料;scope 只能来自 `audit.read`、`contract.comment`、`contract.read`、`review.read`、`template.read`。
- 原始 `subject_reference` 不进入策略快照,仅保存 SHA-256 指纹;账号、工作空间、scope、审批时 revision、起止时间和 operation ID 共同绑定 `policy_hash`。未知字段、额外 revision、缺失 revision、重复 revision、非法 scope 和非法时区均 fail closed。
- 每次检查都重新验证调用账号/工作空间、请求 scope、当前账号与工作空间状态、current revision、起效时间、到期时间、策略 hash 和撤销 hash;账号/工作空间停用或 revision 漂移立即阻断。
- `checked_at >= expires_at` 自动返回 `expired`,不依赖后台任务物化状态;紧急撤销使用固定原因码、不可改写的 `revocation_hash`,相同材料重放幂等,不同材料重写被拒绝。
- 模块不接受 activation flag,不存在可把 `access_granted` 改为 true 的参数;没有密码、cookie、token、session、用户代入或跨工作空间身份上下文。
### 32.2 验收证据与边界
- support access 策略专项 `15/15` 通过,覆盖确定性材料、scope 去重、最小披露、账号/空间/scope/状态/revision 在线阻断、到期边界、紧急撤销幂等与不可改写、审批门槛、24 小时上限和 policy/revocation hash 篡改。
- 与确定性身份计划和 repository 只读集成组合 `24/24` 通过;Python compileall 和 `git diff --check` 通过。
- 源码扫描确认策略构建、检查和撤销函数未被 API 或 `main.py` 引用;本阶段没有 migration、route、Web 控件、grant/revoke 写入口或持久化支持访问记录。
下一步必须进行独立安全评审,确认 validation-only 策略不能被误当作授权结果,并继续检查 TOCTOU、时钟来源、撤销传播、持久化状态、强认证、会话绑定、最小 scope 和审计留痕。评审通过前真实 support access 和身份执行器均不得启用。
## 33. 特权身份与 support access 独立安全评审(No-Go,2026-08-09)
独立评审报告见 `V2-privileged-identity-security-review-2026-08-09.md`。评审确认当前锁、计划、隔离事务和 validation-only 策略可以继续作为开发基础,但不构成真实身份执行或 support access 的发布批准。
P0 阻断项包括:durable grant/revocation 与跨节点一致性缺失、检查和资源访问之间 TOCTOU 未闭合、可信服务端/数据库时间未绑定、强认证与会话绑定缺失、紧急撤销传播和 kill switch 未演练、逐请求不可变审计与告警缺失、统一 deny-by-default enforcement point 缺失,以及隐私/法律门禁未完成。
运行态源码和 OpenAPI 复核:support access validation-only 模块没有 API/`main.py` 引用,`access_granted=true` 路径为 `0`,危险身份写路由为 `0`,plan 路由为 `0`,execution-attempts 仍仅 GET。评审结论保持:真实身份执行器、support access、transport、共享会话和人工绕过入口全部关闭。
@@ -0,0 +1,54 @@
# AI 合同平台 V2 特权身份与支持访问安全评审
> 日期:2026-08-09
> 范围:固定锁复核、确定性身份计划、隔离 PostgreSQL 事务演练、support access validation-only 策略
> 发布结论:**No-Go,真实身份执行器与 support access 不得启用**
## 1. 已验证的安全属性
- 运行态不存在 execute、rollback、merge、split、recover、grant、revoke、support-access、retry、requeue 或 ack 路由;execution-attempts 仍为 GET-only。
- 固定锁和锁内二次 preflight 已在真实 PostgreSQL 隔离 schema 验证,revision 漂移、审批过期、审批人失权和锁超时均 fail closed。
- 确定性计划只保存 target ref 指纹,正向/补偿步骤和双 hash 可检测篡改;support access 不生成身份 mutation plan。
- 隔离事务演练覆盖身份更新后故障和 event/outbox 后故障,身份、event、outbox 同时回滚;随机 schema 清理后残留为 0。
- support access 策略只允许固定 read/comment scope,逐次检查主体、工作空间、scope、状态、revision、时间、policy hash 和 revocation hash;所有结果固定 `access_granted=false`。
## 2. P0 发布阻断项
1. **无运行时可信授权源**:当前策略材料是纯内存输入,没有持久化 grant、撤销记录、单一真相版本或跨实例一致性协议。任何接入都可能读取陈旧授权。
2. **TOCTOU 未闭合**:validation-only 检查与未来资源访问不在同一受控事务/策略执行点;检查后账号、工作空间、scope、revision 或撤销状态仍可能变化。
3. **可信时间未定义**:`checked_at` 由内部调用方传入,尚未绑定数据库时间或受保护的服务端时钟;不能作为真实自动到期依据。
4. **强认证与会话绑定缺失**:没有 step-up/MFA、设备与会话绑定、短时凭证、不可转移 audience、网络/设备约束或重认证窗口。
5. **撤销传播缺失**:紧急撤销只有纯函数语义,没有 durable revoke、缓存失效、跨节点传播 SLA、拒绝列表或 kill switch 演练。
6. **逐请求审计缺失**:没有对每次支持访问记录资源、scope、决策、策略版本、操作者、时间、结果和关联工单的不可变审计/outbox;也没有异常访问告警。
7. **授权执行面缺失**:没有专用 policy enforcement point,无法证明所有合同、审查、模板和审计读取路径均经过同一 deny-by-default 检查。
8. **隐私与法律门禁未完成**:支持人员可见字段、最小数据集、数据驻留、客户通知、保留期、导出/下载限制和具名法务批准尚未验收。
## 3. 后续实现准入标准
- 加法迁移建立不可变 grant/revocation 状态、版本与审计模型;任何 UPDATE/DELETE、过期或撤销竞争均有数据库约束和并发测试。
- enforcement point 使用服务端/数据库可信时间,在每个受保护请求上在线读取或验证当前 grant、scope、revision、撤销和会话绑定;默认拒绝,依赖不可用时不得降级放行。
- 支持人员使用独立平台身份和强认证,不共享用户密码、cookie、refresh token 或现有会话;不得伪装成用户主体。
- 短时凭证具备固定 audience、workspace、scope、grant ID、策略版本和不可转移绑定;自动到期和紧急撤销在所有节点达到明确 SLA。
- 每次授权检查和资源访问生成最小披露、不可变、可关联的审计事件;告警、人工复核、导出管控、保留和删除策略通过运营演练。
- 独立渗透/越权测试覆盖跨 workspace、scope 提升、时钟偏移、缓存陈旧、撤销竞态、重放、并发和依赖故障;全部 P0 阻断关闭后再进行发布评审。
## 4. 回归清单
- 有效材料仍不得在 validation-only 模式返回 `access_granted=true`。
- 到期边界使用 `checked_at >= expires_at`,时区缺失或时钟异常 fail closed。
- 账号/工作空间停用、revision 漂移、主体或 scope 不匹配立即拒绝。
- policy hash、grant ID、revocation code/hash、scope 顺序、TTL 或时间篡改均拒绝。
- 相同撤销重放幂等,不同材料不能改写历史撤销。
- API/OpenAPI 不出现 support access、grant、revoke、execute 或 rollback 写入口。
- 真实 PostgreSQL 演练只使用随机 schema,异常和成功路径后均精确清理,凭据不得落盘。
- Web 不出现执行、代入、重试、撤销或共享会话控件。
## 5. 证据摘要
- Contract API 全量基线:`250 passed, 1 skipped`;真实 PostgreSQL stage 19 专项随后单独 `1/1` 通过。
- 确定性计划:`9/9`;计划/投递/反滥用组合:`54/54`。
- 隔离 PostgreSQL 原子性演练:`1/1`,残留 schema:`0`。
- support access validation-only:`15/15`;与计划层组合:`24/24`。
- Python compileall、`git diff --check`、三服务健康检查通过;运行态危险身份路由 `0`、plan 路由 `0`。
本报告批准继续开发安全基础设施,不批准启用真实身份执行、支持访问、transport、共享会话或人工绕过入口。
@@ -584,3 +584,132 @@ legal_name: string | null
当前单一 Alembic head 为 `20260803_0041`;Python compileall 和 `git diff --check` 通过。本阶段不新增 API、DTO、Web 或 transport 逻辑,身份执行器、真实 support access、人工 outbox retry 及 execute/rollback/merge/split/recover/grant/revoke 继续关闭。下一阶段仍须先完成 transport 选择、消费者幂等契约与外发关联风险评审,不得把数据库完整性加固解释为真实副作用或身份写入获准上线。
本地运行库已在停服备份后由 0040 升级到 0041;升级前 outbox、跨操作错配和非法租约计数均为 `0`,备份文件为 `.runtime/db-backups/contract-api-20260802-before-0041-20260803-012502.sqlite3`。补充 SQLite 触发器后已完成 0041 downgrade/upgrade 循环,复合索引、复合外键、租约 CHECK 和两条关联拒绝触发器复核通过;Contract API 重启并在 `8110` 返回 `200`,Web `3021` 与 AI Platform `8111` 保持运行,现有 Web 会话未因本次迁移重启。
### 9.33 第十四阶段:外发安全契约与消费者幂等参考实现(已交付,2026-08-03)
本阶段完成 9.32 要求的 transport 前置契约,但不选择或连接真实 provider。目标是冻结外发最小字段、认证和 ACK 语义,并用无网络参考消费者验证 at-least-once 重投不会重复业务副作用;它不是 transport、通知或身份执行器上线阶段。
1. **P0-EGRESS-1 最小安全 envelope(完成)**:新增 `privileged-identity-egress.v1`,只包含 contract/key ID、外部 delivery key、effect/event/operation type、状态转换和发生时间。禁止外发 message/event/operation ID、内部幂等键、trace ID、目标指纹、快照及哈希、actor、工作空间/用户 ID、reason、ticket、邮箱和凭据。
2. **P0-EGRESS-2 域隔离认证(完成)**:外部 delivery key 由独立、稳定的幂等密钥对内部幂等键执行域隔离 HMAC;规范化 envelope 生成 SHA-256 body digest,再由带 key ID 的可轮换签名密钥签名。两类密钥均少于 32 bytes 时在消息 claim 前拒绝启动;签名轮换会改变 key ID/signature,但同一消息的 delivery key 在租约重领和 worker 重启后保持不变。
3. **P0-EGRESS-3 类型化 receipt(完成)**:transport 契约从 `deliver(message) -> None` 收口为 `deliver(envelope) -> receipt`,结果固定为 `accepted/duplicate/retryable_failure/permanent_failure`。只有 delivery key 与 digest 同时匹配的 accepted/duplicate 才确认 delivered;retryable 进入既有退避,permanent、未知 receipt 或 ACK 不匹配进入 dead-letter,并仅保存稳定错误码。
4. **P0-EGRESS-4 消费者幂等参考(完成)**:参考消费者按 `(consumer_id, delivery_key)` 保存 receipt;相同 key/digest 返回 duplicate,不重复副作用;相同 key/不同 digest 永久拒绝为 `idempotency_conflict`。签名失败、未知 schema major 和载荷超限均永久拒绝;可重试依赖失败返回稳定 `consumer_unavailable`。该内存实现只用于契约和 ACK 丢失测试,生产消费者必须将 receipt 与业务副作用置于同一持久化事务。
5. **P0-EGRESS-5 封闭测试旁路(完成)**:移除 worker 的 `run_once(deliver=...)` 回调旁路,所有启用场景必须显式注入类型化 transport 和 delivery contract。默认 transport 继续 disabled,`run_once()` 返回 0 且不 claim;未配置 contract 的 enabled transport 在 claim 前失败。
6. **P0-EGRESS-6 回归证据(完成)**:新增契约与 worker 定向 `8/8`,特权身份操作 `20/20`,SQLite 0041 迁移 `2/2`,真实 PostgreSQL 隔离 schema `1/1`。PostgreSQL 覆盖并发认领、租约恢复前后 envelope/delivery key/digest/signature 稳定、敏感关联字段不外发、重试/dead-letter、0041 完整性和 schema 清理。
明确非目标:不增加迁移、API 或 Web 页面;不连接 SMTP、工单、Webhook 或其他真实网络端点;不新增人工 retry/requeue/ack;不回填历史消息;不开放 execute、rollback、merge、split、recover、grant、revoke、真实 support access 或任何身份写入口。发送语义仍为 at-least-once,不宣称 exactly-once。
下一阶段候选必须单独评审:一是审批 SLA/临期与超期只读队列,二是统一身份执行器的只读 preflight 和固定锁计划。任何真实 transport 仍须先完成固定目标 allowlist、TLS/服务认证、持久化消费者 inbox、背压/保留、访问审计、密钥轮换与外部 provider 验收;SMTP 不直接消费治理 outbox。
### 9.34 第十五阶段:统一执行器只读 preflight 与固定锁计划(已交付,2026-08-03)
本阶段完成未来统一执行器的只读验证契约,不增加 execute API,不获取真实数据库写锁,不写账号、工作空间、成员关系、凭据、权限、operation、approval、event 或 outbox。
1. **P0-PREFLIGHT-1 可验证 preview 材料(完成)**:新增加法迁移 `20260803_0042`,新操作保存原始 `preview_expires_at`;历史操作保持 `NULL`,不推测回填并返回 `preview_verification_material_missing`。
2. **P0-PREFLIGHT-2 审批时效与权限复核(完成)**:复核 operation 状态/到期、两名不同有效审批人、请求人隔离、approval expiry、preview hash、审批时 permission revision,以及审批人当前账号、用户、成员和平台权限状态。
3. **P0-PREFLIGHT-3 revision 与当前状态复核(完成)**:重新构建原始 preview,比较 expected/current entity revision,并再次执行当前目标校验;缺失/非法 revision 材料、revision scope 不一致、漂移、缺失资源或当前状态非法均返回稳定 blocker,`ready=false`。
4. **P0-PREFLIGHT-4 固定锁计划(完成)**:描述性顺序固定为 operation、approval set、identity account、workspace、tenant user、workspace membership、global credential、legacy credential;同类按 ID 排序。GET 始终返回 `locks_acquired=false`,未来执行事务不得复用 GET 结果代替锁内复核。
5. **P0-PREFLIGHT-5 只读 API 与 Web(完成)**:只增加 `GET .../{operation_id}/execution-preflight`;页面展示 readiness、blockers、revision 和锁计划,同时明确 `executor_enabled=false`。没有执行、回滚或身份动作按钮。
6. **P0-PREFLIGHT-6 自动化证据(完成)**:SQLite 特权操作 `23/23`、安全投递契约 `6/6`、迁移 `2/2`(组合 `31/31`)、Web 全量源码测试 `37/37`、页面 `5/5`、TypeScript、Python compileall、Alembic head `20260803_0042` 和真实 PostgreSQL 隔离 schema `1/1` 通过。PostgreSQL 验证 ready、revision 漂移、查询前后治理/身份全字段快照不变并清理测试 schema。
验收语义:`ready=true` 仅表示查询时刻审批、preview、权限、revision 和当前状态检查均通过,不是执行授权票据,不表示锁已持有或身份已变更;`approved` 仍只表示治理审批完成。preflight 不物化过期状态、不追加事件/outbox,OpenAPI 中该路径无 POST/PUT/PATCH/DELETE,全局破坏性身份写路径仍为 0。
后续 P0 backlog:独立评审真实事务执行状态机、execution attempt 幂等记录、固定锁真实获取、锁内二次复核、失败补偿、outbox 副作用、support access 在线鉴权/自动到期和独立 rollback 治理。在这些门槛完成前,身份执行器、真实 support access、execute/rollback/merge/split/recover/grant/revoke 和人工 outbox 写入口继续关闭。
运行环境已在停服备份后由 `0041` 升级到 `0042`,备份为 `.runtime/db-backups/contract-api-20260802-before-0042-20260803-041600.sqlite3`;Contract API `8110` 重启健康,Web `3021` 和 AI Platform `8111` 保持运行。运行态 OpenAPI 复核 preflight 只有 GET,身份域破坏性路径数量为 `0`。
### 9.35 第十六阶段:审批 SLA 临期与超期只读队列(已交付,2026-08-03)
本阶段完成剩余 P1 运营可见性切片,不新增迁移或写入口,不物化超期状态。
1. **P1-SLA-1 只读 overview(完成)**:新增 `GET /identity/privileged-approval-sla/overview`,按可控观察窗口和临期阈值返回 `on_track/due_soon/breached` 总量,不返回操作正文或身份信息。
2. **P1-SLA-2 稳定分页队列(完成)**:新增 GET-only queue,支持三档状态筛选和有界分页;按风险、到期时间、operation ID 确定排序,返回有效/所需/剩余审批数和时间字段。
3. **P1-SLA-3 当前有效审批语义(完成)**:审批过期、preview hash 不一致、permission revision 漂移或审批人当前失权均不计入有效审批;不披露审批人、评论或身份字段。
4. **P1-SLA-4 严格只读(完成)**:查询不调用 `_materialize_expired_operations`,超期只报告 `breached`;operation、approval、event、outbox 与身份数据不变,不 claim、retry 或产生外部副作用。
5. **P1-SLA-5 Web 与 S3 门禁(完成)**:治理页增加摘要、筛选和分页队列,客户端仅发 GET;两个路由纳入敏感 `S3` 反滥用策略,没有审批、处置、执行或消息动作。
6. **P1-SLA-6 自动化证据(完成)**:SQLite 组合 `45/45`、真实 PostgreSQL 隔离 schema `1/1`、Web 全量 `38/38`、页面 `6/6`、TypeScript、Python compileall、Alembic head `20260803_0042` 和 diff-check 通过。
运营口径:默认观察最近 `7` 天创建的 pending approval,临期阈值 `1` 小时;可将窗口扩至 `31` 天。窗口外记录继续存在于操作、事件和审计证据中,值班人员应定期扩大窗口复核历史积压。`breached` 只表示当前查询时刻已超过审批期限,不等于数据库状态已物化为 `expired`。
后续仍不得直接开放身份执行。下一阶段优先评审 execution attempt 的追加式幂等记录和失败状态机,或补齐长期积压跨窗口汇总;任一方向均不得引入真实身份写入、support access 或 execute API,除非另行完成锁内复核、补偿、outbox、回滚和安全评审。
### 9.36 第十七阶段:跨时间窗长期审批积压只读可见性(已交付,2026-08-03)
本阶段补齐第十六阶段窗口外长期待审批记录的运营盲区,只扩展既有 overview/queue 的只读查询口径,不增加迁移、状态物化、处置动作或身份写入口。
1. **P1-SLA-AGE-1 全量与窗口外 overview(完成)**:overview 在既有窗口内三档计数之外,返回 `all_pending_total`、`outside_window_pending_count`、`outside_window_breached_count` 和 `oldest_pending_age_seconds`。所有指标基于 stored `pending_approval` 直接只读统计,不调用 `list_operations` 或 `_materialize_expired_operations`。
2. **P1-SLA-AGE-2 queue scope(完成)**:queue 新增 `scope=window|all`,默认 `window`,响应回显 scope;`all` 可查看窗口外长期积压,继续支持三档筛选、有界分页及风险/到期时间/operation ID 的确定性排序。非法 scope 返回 `422`。
3. **P1-SLA-AGE-3 最小披露与权限(完成)**:两个 scope 均只向当前有效平台管理员返回既有最小字段;禁止 reason、ticket、target/evidence、actor、审批人/评论、邮箱、姓名、账号/成员 ID、preview/revision、payload 和凭据字段。个人用户、企业成员/管理员及失权管理员不能读取全量积压。
4. **P1-SLA-AGE-4 严格只读与 S3(完成)**:overview/queue 查询前后 operation、approval、event、outbox 和身份全字段快照一致,不物化超期、不 claim/retry、不产生外部副作用;两个 GET 继续受 `identity.privileged_approval_sla.read` 敏感 `S3` 策略保护。
5. **P1-SLA-AGE-5 Web 分段口径(完成)**:治理页提供“最近 7 天/全部积压”分段控件,展示全量、窗口外、窗口外超期和最老积压年龄;scope 切换重置分页并只发 GET,不增加 approve/reject、execute、retry、requeue 或其他处置按钮。
6. **P1-SLA-AGE-6 自动化证据(完成)**:SQLite/API 组合 `45/45`、真实 PostgreSQL 隔离 schema `1/1`、Web 全量 `39/39`、页面专项 `7/7`、TypeScript、Python compileall、Alembic 单一 head `20260803_0042` 和 `git diff --check` 通过。运行态 OpenAPI 中两个 SLA 路由均为 GET-only,身份破坏性写路径为 `0`;桌面与 `390x844` 浏览器回归通过,控制台 error 为 `0`。
验收口径:`all_pending_total = window_pending_total + outside_window_pending_count`;`outside_window_breached_count <= outside_window_pending_count`;空队列的最老年龄为 `null`,非空时为非负秒数。`scope=all` 只扩大待审批记录的时间范围,不扩大字段、租户、角色或动作权限。`breached` 仍只是查询时刻的运营告警分类,不等于数据库状态已写成 `expired`。
明确非目标:不新增数据库迁移,不回填、归档、删除或修改历史 operation/approval/event/outbox;不启用 transport、support access、身份执行器或人工消息/审批处置;不新增 execute/rollback/merge/split/recover/grant/revoke。下一阶段真实身份执行仍需独立完成 execution attempt 幂等、固定锁真实获取、锁内二次复核、失败补偿、outbox 副作用和 rollback 安全评审。
### 9.37 第十八阶段:追加式执行尝试账本与失败状态机(已交付,2026-08-03)
本阶段只建设未来统一执行器的内部持久化和只读观测基础,不实现身份 mutation,不启用执行器,不新增运行时 attempt 写入口或 execute API。
1. **P0-ATTEMPT-1 不可变 attempt 根(完成)**:新增 `privileged_identity_execution_attempts`,保存 operation 关联、递增 attempt number、幂等键、request/preview/preflight SHA-256、脱敏 `trace_id` 和创建时间。仅已审批且未过期、request/preview 与 operation 一致的内部调用可以登记。
2. **P0-ATTEMPT-2 追加式状态事件(完成)**:新增 `privileged_identity_execution_attempt_events`,保存确定性事件 ID、sequence、固定 event/status 映射、稳定错误码、脱敏前后快照及哈希、event payload hash 和 `trace_id`。current status 只从最后事件派生,不新增可变 current-status 列。
3. **P0-ATTEMPT-3 精确失败状态机(完成)**:合法转换冻结为 `prepared -> running|aborted`、`running -> succeeded|failed|compensation_required`、`compensation_required -> compensated|compensation_failed`;所有其他转换拒绝,五个终态不可继续迁移。
4. **P0-ATTEMPT-4 并发幂等与数据库保护(完成)**:相同幂等材料重放不增行,不同材料冲突;相同 event payload 重放不增行。数据库唯一约束保护 attempt number、idempotency key、event sequence 和 event payload;SQLite/PostgreSQL 触发器禁止 attempt/event UPDATE 与 DELETE,外键为 `NO ACTION`。
5. **P0-ATTEMPT-5 脱敏快照与最小披露(完成)**:快照只接受固定状态、类型、计数、哈希、结果码和布尔字段;原始错误、target/evidence、actor、邮箱、身份详情和凭据被拒绝。只读 GET 仅返回 attempt 摘要,固定 `executor_enabled=false`,不返回幂等键、哈希或快照。
6. **P0-ATTEMPT-6 GET-only Web 可见性(完成)**:治理详情页读取 execution attempts 并展示序号、派生状态、事件数、稳定错误码、`trace_id` 和时间;具备加载/空/不可用状态,无 execute/retry/compensate/abort/rollback 按钮。OpenAPI execution-attempts 只有 GET,内部 register/append 未暴露为 route。
7. **P0-ATTEMPT-7 自动化证据(完成)**:SQLite/API 组合 `47/47`、迁移专项 `2/2`、真实 PostgreSQL 隔离 schema `1/1`、Web 全量 `40/40`、页面专项 `8/8`、TypeScript、Python compileall 和 `git diff --check` 通过。Alembic 单一 head 升至 `20260803_0043`;PostgreSQL 并发覆盖相同幂等键和相同事件仅落一份,隔离 schema 已清理,凭据未落盘。
验收口径:attempt 根与事件只能追加,不能 UPDATE/DELETE;状态、事件数、最后错误码和最近时间从事件派生;重放不增加行;非法转换、原始错误和敏感快照返回稳定错误;GET 前后 operation、approval、operation event、outbox、身份实体及 execution ledger 全字段快照一致;普通用户和失权管理员不可读;响应不含幂等键、request/preview/preflight hash、快照、raw error、target/evidence、actor 或凭据。
明确非目标:本阶段不获取真实身份资源锁,不执行 lock 内二次 preflight,不修改全局账号、工作空间投影、成员、凭据或平台权限,不投递 attempt outbox,不执行补偿/rollback,不启用 support access、transport 或 identity executor,不新增 execute/rollback/merge/split/recover/grant/revoke/retry/requeue/ack API。下一阶段优先建设内部固定锁真实获取与锁内二次复核,仍保持验证事务无身份写入且不提供 execute API。
### 9.38 第十九阶段:固定锁真实获取与锁内二次复核(已交付,2026-08-09)
本阶段只把既有只读 preflight 和描述性 lock plan 落为内部 PostgreSQL 验证事务,不开放 execute API,不修改身份实体、operation、approval、event、outbox 或 execution ledger。
1. **P0-LOCK-1 内部锁事务(完成)**:新增内部 `recheck_execution_preflight_under_lock()`;仅当前有效平台管理员上下文可调用,方法未注册为 route。PostgreSQL 先设置 50-10000ms 有界 `lock_timeout`,再按固定 rank 获取 operation、approval set、身份主体和 preview revision 资源行锁,同类按 ID 排序。
2. **P0-LOCK-2 权限主体保护(完成)**:实际锁集合补充调用者和审批人的账号、工作空间、用户及成员关系保护行,避免权限在锁内失效;公开 GET lock plan 仍保持最小披露,不将内部主体扩展伪装为执行授权。
3. **P0-LOCK-3 锁内新鲜复核(完成)**:锁获取后失效 ORM identity map,再复核 operation 到期、审批 quorum/expiry、preview hash、permission revision、expected/current revision 和当前目标状态;ready 结果不是执行票据,`executor_enabled` 仍为 `false`。
4. **P0-LOCK-4 只读与释放(完成)**:事务只允许 SELECT、`SET LOCAL lock_timeout` 和内存校验;检测 session 无新增/脏/删除对象。ready、blocker、422 参数错误和 423 锁超时均不产生身份或治理写入,超时回滚并释放已取得锁。
5. **P0-LOCK-5 并发证据(完成)**:真实 PostgreSQL 隔离 schema `1/1` 覆盖真实 `FOR UPDATE` 顺序、反向账号合并输入无死锁、50ms 锁超时、超时后锁释放、revision 漂移、审批过期、审批人失权和全字段快照不变;随机 schema 已清理,凭据未落盘。
6. **P0-LOCK-6 运行态守卫(完成)**:Web 全量 `41/41`、页面专项 `9/9`、TypeScript、Python compileall 和 `git diff --check` 通过;运行态 execution-attempts 仅 GET,身份破坏性写路径 `0`。桌面和 `390x844` 窄屏页面无水平溢出,详情页显示执行尝试历史及“只读 · 执行器关闭”,控制台无应用 error。
验收口径:锁前检查不得替代锁内复核;锁后读取必须使用新鲜 ORM/数据库状态;锁集合必须按固定 rank 和同类 ID 顺序获取;任何 blocker 或锁超时都 fail closed;验证事务前后 operation、approval、event、outbox、execution ledger 及身份表全字段快照一致。
明确非目标:本阶段不编译或执行真实身份 mutation,不生成或投递 attempt outbox,不启用 support access、transport、补偿执行或 rollback,不新增 execute/rollback/merge/split/recover/grant/revoke/retry/requeue/ack API。下一阶段先建设确定性身份变更计划与逆向补偿计划的内部生成和哈希,再进行隔离 PostgreSQL 事务演练、故障注入、原子回滚和 outbox 一致性验证。
### 9.39 第二十阶段:确定性身份变更与逆向补偿计划(已交付,2026-08-09)
1. **P0-PLAN-1 纯内存计划模型(完成)**:新增不可变 Pydantic 计划模型和纯函数,覆盖五类身份变更;不增加 FastAPI 路由、execute API、迁移或身份写路径。
2. **P0-PLAN-2 输入边界与最小披露(完成)**:严格校验 operation type、target refs、expected revisions 和固定锁计划;拒绝未知/不完整/敏感字段、重复 revision、同账号 partition、非法恢复类型、stale 或 blocked lock。计划不记录原始 target ref 值,仅保存字段指纹。
3. **P0-PLAN-3 确定性步骤与锁序(完成)**:五类操作各有固定正向步骤;锁按既有 rank 与同类 ID 稳定排序;输入 map/list 顺序变化不影响 plan hash。
4. **P0-PLAN-4 逆向补偿与双 hash(完成)**:补偿步骤严格为正向步骤逆序,正向 `plan_hash` 与独立 `compensation_plan_hash` 均由 `privileged_payload_hash` 计算;校验函数可检测步骤、锁或 hash 篡改。
5. **P0-PLAN-5 内部接入与证据(完成)**:repository 增加无路由 `build_internal_change_plan()`,仅从 fresh ready preflight 构造计划;专项 `9/9`(含 repository 前后全字段快照不变)、Python compileall、`git diff --check` 通过。
验收口径:相同 operation/target/revision/lock 材料必须产生完全相同的步骤和双 hash;任何 blocker、revision/lock 漂移或敏感数据均不得生成计划;计划构建前后治理、身份、event、outbox 和 execution ledger 全字段快照不变;support access、transport、execute、rollback 和补偿执行继续关闭。下一阶段进入隔离 PostgreSQL 事务演练、故障注入、原子回滚和 outbox 一致性验证。
### 9.40 第二十一阶段:隔离 PostgreSQL 事务演练与 outbox 原子性(已交付,2026-08-09)
1. **P0-TXN-1 随机 schema 演练(完成)**:只在 `192.168.3.90:5432/yuqei_contract` 的随机隔离 schema 中运行真实 PostgreSQL 事务;运行库、公共 schema 和生产身份数据不参与。
2. **P0-TXN-2 计划前置校验(完成)**:演练前从已审批 operation 生成第 20 阶段确定性正向/补偿计划,确认执行器关闭和双 hash 有效,不把 plan 当作运行授权票据。
3. **P0-TXN-3 双故障点原子回滚(完成)**:分别在身份投影 flush 后、event/outbox 前,以及 event/outbox 均 flush 后注入异常;两种情况下身份、治理、event、outbox 和 execution ledger 全字段快照均恢复到事务前状态。
4. **P0-TXN-4 outbox 同事务一致性(完成)**:成功演练在同一事务提交身份投影、operation event 和 outbox;同一 operation 的 event/outbox 数量一一对应,失败演练不留下孤立 event、孤立 outbox 或部分身份状态。
5. **P0-TXN-5 安全边界与证据(完成)**:真实 PostgreSQL 专项 `1/1` 通过,结束后 `privileged_governance_%` schema 残留 `0`。仅新增测试,不增加迁移、运行时 executor、execute/rollback/compensate API、transport、support access 或人工账本入口;凭据未落盘。
验收口径:隔离演练允许在随机测试 schema 内提交和回滚测试身份投影,但不代表真实身份执行获准上线;计划 hash 不替代锁内二次 preflight;任何故障必须让身份、event 和 outbox 同时回滚。下一阶段按顺序推进 support access 在线鉴权、自动到期、紧急撤销和独立安全评审,通用代入、共享密码/会话及真实支持访问继续禁止。
### 9.41 第二十二阶段:support access validation-only 安全策略(已交付,2026-08-09)
1. **P0-SUPPORT-1 审批与范围绑定(完成)**:只接受 approved、两名有效审批和 preview hash 当前有效的材料;账号、工作空间、固定 allowlist scope、revision、起止时间和 operation ID 绑定确定性 policy hash,原始 subject reference 仅保留指纹。
2. **P0-SUPPORT-2 在线状态检查(完成)**:每次检查账号/工作空间匹配、scope、当前 active 状态和 current revision;主体不匹配、停用或 revision 漂移均返回稳定阻断码。
3. **P0-SUPPORT-3 自动到期(完成)**:不依赖后台物化,`checked_at >= expires_at` 立即失效;TTL 强制不超过 24 小时,所有时间必须带时区。
4. **P0-SUPPORT-4 紧急撤销(完成)**:固定原因码和 revocation hash 绑定撤销材料;相同撤销幂等,不同撤销重写拒绝,在线检查先验证 policy/revocation hash 再判定 revoked。
5. **P0-SUPPORT-5 执行器继续关闭(完成)**:所有结果固定 `access_granted=false`,模块没有 activation flag、token/session/impersonation 或 API 绑定。专项 `15/15`、与计划层组合 `24/24`、compileall 和 diff-check 通过;无迁移、路由或持久化 grant/revoke 写入口。
验收口径:`policy_satisfied=true` 只表示 validation-only 材料在检查时刻满足策略,不是访问授权;任何调用方不得把该字段映射为放行。下一步必须完成独立安全评审和真实 enforcement 设计,至少覆盖 TOCTOU、可信时钟、撤销传播、持久化、强认证、会话绑定、审计与最小 scope;评审通过前真实 support access 继续关闭。
### 9.42 第二十三阶段:特权身份与 support access 独立安全评审(已完成,No-Go,2026-08-09)
评审报告:`V2-privileged-identity-security-review-2026-08-09.md`。当前实现获准继续作为 validation-only 和隔离演练基础,但真实身份执行与 support access 发布被拒绝。
P0 后续 backlog:建立 durable grant/revocation 与跨节点一致性;绑定可信数据库/服务端时间;在统一 deny-by-default enforcement point 内闭合 TOCTOU;实现 step-up/MFA、设备/会话和短时凭证绑定;演练撤销传播 SLA 与 kill switch;建立逐请求不可变审计/outbox、异常告警和隐私/法律审批。上述全部验收前不得增加 support access、execute、rollback、grant、revoke、impersonation、共享密码/会话或人工绕过入口。