91 KiB
AI 合同平台 V2 特权身份操作治理方案
日期:2026-08-02
状态:第一阶段治理底座已实现;身份数据执行、回滚与支持访问授权仍未开放
适用范围:账号合并、账号拆分、工作空间成员关系恢复、平台管理员授予/撤销、限时支持访问
1. 结论
当前身份模型已经能够表达全局登录账号、工作空间内用户和两者之间的成员关系,也已经提供只读账号冲突诊断与平台管理员名册,但尚不具备安全执行高风险身份写操作所需的治理闭环。
上述五类操作不得分别建设为可直接调用的写接口。它们必须进入统一的“特权身份操作”控制面:先预览影响,再提交带理由的请求,由两名不同且有效的平台管理员审批,在预览与目标版本仍有效时通过单事务执行,并保留执行前后快照、事件审计与受控回滚能力。任何未经审批即可合并、拆分、恢复、授予、撤销或代入访问的 API 都是 P0 阻断项。
本方案不把 tenant 继续解释为“公司”。在持久化层,TenantModel 和 tenant_id 保持现状,作为工作空间与数据隔离边界;在产品和治理语义中,统一使用 workspace,并通过 workspace_type = system | personal | organization 区分系统、个人和组织工作空间。
2. 当前证据与约束
2.1 已确认的数据模型
| 当前实体 | 已有关键字段/约束 | 在治理方案中的含义 |
|---|---|---|
TenantModel / tenants |
id、workspace_type、owner_user_id、status |
持久化工作空间和数据隔离边界;不改名、不迁移业务数据归属 |
TenantUserModel / tenant_users |
tenant_id、email、role、status、platform_permissions |
工作空间内用户投影;platform_admin 是显式平台权限 |
GlobalAccountModel / identity_accounts |
全局唯一邮箱、状态、验证时间 | 跨工作空间的全局登录身份 |
GlobalAccountCredentialModel / identity_account_credentials |
每个全局账号唯一凭据、凭据状态 | 全局登录凭据 |
WorkspaceMembershipModel / workspace_memberships |
(account_id, tenant_id) 唯一,tenant_user_id 唯一 |
全局账号与工作空间内用户投影的连接关系 |
TenantUserCredentialModel / tenant_user_credentials |
工作空间用户旧凭据 | 兼容期遗留登录凭据,不能在合并/拆分中被遗漏 |
代码证据位于:
yuqei-contract-platform/services/contract-api/src/yuqei_contract_api/persistence/models.pyyuqei-contract-platform/services/contract-api/src/yuqei_contract_api/persistence/identity.pyyuqei-contract-platform/services/contract-api/src/yuqei_contract_api/api/identity.pyyuqei-contract-platform/services/contract-api/tests/test_identity_account_conflicts.pyyuqei-contract-platform/services/contract-api/tests/test_proxy_auth.py
2.2 已有能力
GET /api/v1/identity/account-conflicts及详情接口是只读冲突诊断入口。GET /api/v1/identity/platform-administrators是只读平台管理员名册。- 跨空间平台权限以显式
platform_permissions中的platform_admin为判断依据。 - 现有逻辑已保护“最后一名有效平台管理员”不能被停用。
- 全局账号凭据与工作空间旧凭据并存,治理操作必须覆盖两套凭据的归属和状态。
2.3 尚缺能力
- 没有统一的高风险操作请求、审批、执行和回滚状态机。
- 没有可复用的影响预览、预览哈希和目标版本校验。
- 没有跨重试幂等的
operation_id。 - 没有两名不同平台管理员批准的强制机制。
- 没有覆盖所有受影响身份实体的事务锁顺序与并发冲突协议。
- 没有限时支持访问的理由、范围、到期和自动撤权模型。
- 只读诊断 ID 可作为证据,但不能直接作为写操作标识或写入授权。
3. 治理原则
- 先预览,后提交:没有成功预览及
preview_hash,不能提交操作。 - 一次意图,一个 ID:客户端生成或服务端分配全局唯一
operation_id;相同 ID 的重试返回原请求/结果,不得重复执行。 - 请求与批准分离:请求人不能批准自己的操作。
- 双人批准:必须由两名不同、当前有效的平台管理员批准;执行时再次验证两人资格。
- 有因、有界、有期限:理由和工单引用必填;支持访问必须限定工作空间、权限范围和
expires_at。 - 单事务、无部分成功:身份关系、凭据、权限和审计结果在一个事务边界内提交;任一不变量失败则全部回滚。
- 版本匹配才执行:执行时
preview_hash、目标实体版本和关键状态必须与预览一致。 - 可逆优先:优先停用、墓碑和血缘记录,不物理删除身份实体。
- 全程留痕:预览、提交、批准、拒绝、执行、失败、回滚和到期都写追加式事件;保留脱敏的前后快照。
- 最小披露:列表和审批页面只展示决策所需信息;凭据哈希、令牌、完整敏感字段永不返回。
- 服务端是唯一可信边界:前端隐藏按钮不是授权;每个状态转换与执行动作都由后端重新鉴权和校验。
- 禁止旁路:不存在无需该审批流即可完成同类破坏性写入的内部、管理端或批处理接口。
4. 操作目录与业务不变量
4.1 账号合并 account_merge
用途:将确认属于同一自然人的两个全局账号收敛为一个主账号。
请求必须明确:
source_account_id与target_account_id- 规范化邮箱、邮箱验证状态和人工核验依据
- 每个工作空间成员关系的处理计划
- 全局凭据和旧工作空间凭据的保留/停用计划
- 冲突诊断
diagnostic_id(如有,仅作证据引用)
不变量:
- 源账号和目标账号必须不同,且执行前均存在。
- 合并只改变全局身份和成员关系所有权,不改变任何工作空间业务数据的
tenant_id。 - 个人工作空间与组织工作空间的合同、模板、审查、审批等业务数据绝不互相合并或迁移。
- 同一目标账号不能在同一工作空间产生两条有效成员关系;预览必须给出确定的冲突处理方案。
- 主凭据只能保留一套有效登录入口;被停用凭据保留审计元数据,不回传密码哈希。
- 源账号采用停用/墓碑状态,并记录
merged_into_account_id血缘,不物理删除。 - 合并不能隐式获得
platform_admin;涉及平台权限时须拆成独立的管理员授予操作。
4.2 账号拆分 account_split
用途:纠正错误合并、共用账号或错误归属,将成员关系与凭据按明确映射拆分到新/既有全局账号。
请求必须明确:
source_account_iddestination_account(既有账号 ID 或待创建账号的最小资料)membership_partition:每个待迁移workspace_membership_id的目标账号credential_ownership:全局/旧凭据的保留、停用、重置与验证计划- 邮箱所有权重新验证方式
不变量:
- 每条成员关系只能分配给一个全局账号,不允许复制有效关系。
- 拆分后
(account_id, tenant_id)和tenant_user_id唯一约束仍成立。 - 工作空间内用户投影和业务数据
tenant_id不变。 - 新凭据必须经过独立验证或强制重置,不能复制密码哈希到另一账号。
- 平台管理员权限不会随成员关系自动拆分或复制。
- 原账号与目标账号之间必须保留
split_from/split_to血缘和操作引用。
4.3 工作空间成员关系恢复 membership_recovery
用途:修复缺失、错误停用或错误指向的 WorkspaceMembershipModel,使已核验全局账号重新关联正确的工作空间用户投影。
请求必须明确:
account_id、tenant_id、tenant_user_id- 恢复类型:
reactivate | relink | recreate - 账号与工作空间用户属于同一人的核验依据
- 是否需要停用冲突成员关系
不变量:
- 工作空间和用户投影必须存在,且
tenant_user.tenant_id == tenant_id。 - 不得跨个人/组织工作空间迁移用户投影。
- 恢复后唯一约束成立,角色、owner 状态不得因恢复而越权升级。
- 恢复 owner 关系时必须同时核对
tenants.owner_user_id,预览展示差异。 - 诊断 ID 只能证明发现过冲突;执行仍以当前实体 ID 和版本为准。
4.4 平台管理员授予/撤销
操作类型:platform_admin_grant、platform_admin_revoke。
不变量:
- 目标必须是当前有效且可核验的身份,不以固定用户 ID 判断资格。
- 工作空间
admin与平台platform_admin是两种权限,禁止相互推导。 - 授予或撤销必须单独发起,不得夹带在账号合并、拆分或成员恢复中。
- 执行撤销后至少保留一名有效平台管理员;检查必须在锁定管理员集合后、提交前完成。
- 请求人、两名审批人和目标人不能通过同一账号别名绕过职责分离检查。
- 权限变化后撤销或刷新目标账号的高权限会话,确保新权限边界及时生效。
4.5 限时支持访问 support_access_grant
用途:平台支持人员在用户明确请求或合规处置时,对指定工作空间获得最小、限时、可审计的支持权限。
必填范围:
support_account_idtenant_idpermission_scopesreasonticket_referenceexpires_at- 可选
subject_reference(如指定合同,但不得在列表页暴露敏感正文)
不变量:
expires_at必须晚于批准时间,并受平台最大时长限制;建议默认 2 小时、硬上限 24 小时。- 支持访问不能授予
platform_admin、工作空间 owner、成员管理或身份治理写权限。 - 每次请求只能覆盖一个工作空间;跨空间必须分别申请。
- 访问检查按“基础身份权限 ∩ 已批准 scope ∩ 未过期时间”计算。
- 到期自动失效并写审计事件;缓存和会话授权不得超过授权到期时间。
- 延期视为新操作,必须重新预览和双人批准,不能原地修改
expires_at。 - 支持人员的读取、下载、导出和变更行为须附带
support_grant_id写入业务审计。
5. 统一状态机
draft
-> previewed
-> pending_approval
-> rejected (终态)
-> cancelled (终态)
-> expired (终态)
-> approved
-> executing
-> succeeded
-> failed (异常终态,可按策略重新预览后新建操作)
succeeded
-> rollback_pending
-> rolled_back (终态)
-> rollback_failed (异常终态,进入人工事故处置)
状态转换规则:
| 当前状态 | 动作 | 下一状态 | 强制条件 |
|---|---|---|---|
draft |
生成预览 | previewed |
调用人有请求权限;目标存在;影响可计算 |
previewed |
提交审批 | pending_approval |
请求体与预览哈希一致;理由、工单、到期完整 |
pending_approval |
批准 | 保持/approved |
请求人不可批准;两名不同有效平台管理员后进入 approved |
pending_approval |
拒绝 | rejected |
拒绝理由必填 |
previewed / pending_approval |
取消 | cancelled |
仅请求人或有治理权限的平台管理员;写明理由 |
pending_approval / approved |
超时 | expired |
审批有效期或操作有效期已过 |
approved |
执行 | executing |
重新鉴权、双批准仍有效、预览哈希和目标版本匹配 |
executing |
原子提交 | succeeded |
所有不变量通过,前后快照和执行事件同事务持久化 |
executing |
失败 | failed |
数据写入全部回滚;记录脱敏错误和关联追踪 ID |
succeeded |
申请回滚 | rollback_pending |
在可回滚窗口内,先预览回滚影响,重新双人批准 |
rollback_pending |
回滚提交 | rolled_back |
当前状态与回滚预览匹配,单事务恢复 |
rollback_pending |
回滚失败 | rollback_failed |
数据写入全部回滚并触发 P0 告警 |
审批建议 24 小时失效;支持访问不得因审批有效期而突破自身 expires_at。任何已批准操作在目标版本变化后不能直接执行,必须失效并重新预览、重新审批。
6. 标准操作流程
6.1 预览
预览是只读计算,不产生身份写入。输出至少包括:
- 规范化后的操作类型和目标引用
- 受影响账号、工作空间、用户投影、成员关系、凭据与权限的脱敏摘要
- 预计新增、更新、停用的实体清单
- 操作前关键不变量检查
- 冲突、阻断项和人工核验项
expected_revisions(实体 ID、updated_at或数据库版本号)- 服务端基于规范化输入与预览结果计算的
preview_hash - 预览过期时间
预览不得返回密码哈希、令牌、完整合同内容、无关工作空间信息或超出审批所需的个人数据。
6.2 提交与审批
- 提交时带
operation_id、preview_hash、理由、工单引用和操作参数。 - 后端重新计算规范化摘要,拒绝被篡改或过期的预览。
- 两名审批人分别提交独立决定,记录时间、身份、MFA/会话强度、评论和客户端审计上下文。
- 同一全局账号的不同工作空间用户投影不视为不同审批人。
- 审批人失效、被撤销平台权限或审批过期时,该批准立即失效。
- 对目标是某平台管理员本人的操作,该目标不得充当审批人。
6.3 执行
执行可由授权执行者发起,或在第二次批准后由受控 worker 发起。无论方式如何,执行者均不能代替审批人,且执行时必须重新做全部授权和不变量校验。
执行步骤:
- 以
operation_id读取操作并获取操作行锁。 - 若已有
succeeded/rolled_back结果,直接返回原结果。 - 验证状态、有效期、审批人数、审批人当前资格和职责分离。
- 按确定顺序锁定全部受影响实体。
- 比较
expected_revisions,重新计算影响并匹配preview_hash。 - 采集脱敏
before_snapshot。 - 执行身份变更并验证所有业务/数据库不变量。
- 采集脱敏
after_snapshot,写血缘、事件和业务审计。 - 在同一事务中把操作标记为
succeeded并提交。 - 提交后执行会话撤销、缓存失效、通知和安全告警;失败时进入可靠重试队列,不能回滚已成功的身份事务。
7. API 草案
统一前缀:/api/v1/identity/privileged-operations。
7.1 端点
| 方法与路径 | 用途 | 最低权限 |
|---|---|---|
POST /preview |
只读影响预览 | 允许发起对应操作的平台管理员 |
POST / |
以 operation_id 提交操作 |
允许发起对应操作的平台管理员 |
GET /{operation_id} |
查询状态、审批与脱敏结果 | 请求人、审批人或授权审计人员 |
POST /{operation_id}/approvals |
批准或拒绝 | 非请求人的有效平台管理员 |
POST /{operation_id}/execute |
触发受控执行 | 独立执行权限或受控 worker |
POST /{operation_id}/rollback |
创建/推进回滚请求 | 平台管理员;仍需新一轮预览与双人批准 |
禁止提供可旁路统一流程的 /merge、/split、/recover、/grant、/revoke 或直接写支持授权端点。内部服务也只能调用统一执行器,不得直接更新身份表。
7.2 预览请求示例
{
"operation_type": "account_merge",
"target_refs": {
"source_account_id": "acct_source",
"target_account_id": "acct_target"
},
"evidence_refs": [
{"type": "account_conflict_diagnostic", "id": "account-conflict-example"}
]
}
7.3 预览响应示例
{
"preview_id": "preview_01",
"operation_type": "account_merge",
"preview_hash": "sha256:...",
"expires_at": "2026-08-02T12:30:00Z",
"expected_revisions": [
{"entity_type": "identity_account", "entity_id": "acct_source", "revision": "2026-08-02T11:58:00Z"}
],
"impact_summary": {
"accounts_updated": 2,
"memberships_reassigned": 1,
"credentials_disabled": 1,
"workspace_business_records_moved": 0
},
"blocking_issues": [],
"warnings": []
}
7.4 提交请求示例
{
"operation_id": "op_01K...",
"preview_id": "preview_01",
"preview_hash": "sha256:...",
"reason": "修复经用户和运营双向核验的重复账号",
"ticket_reference": "SUP-2026-0802-001",
"requested_expires_at": "2026-08-03T12:00:00Z"
}
7.5 审批请求示例
{
"decision": "approve",
"comment": "已核对邮箱控制权、成员关系和工单证据",
"preview_hash": "sha256:..."
}
7.6 HTTP 与错误语义
200:幂等重试返回既有操作或结果。201:首次创建操作。400:请求结构或操作参数无效。403:无发起/审批/执行权限,或违反职责分离。404:目标不存在,或按最小披露策略对调用人不可见。409:operation_id被不同载荷占用、目标版本变化、唯一约束冲突或状态转换非法。410:预览、审批或操作已过期。422:预览存在未解决阻断项或业务不变量不成立。423:相同目标存在执行中的互斥操作。
错误响应只返回稳定错误码、可行动摘要和 trace_id,不泄露其他账号或工作空间细节。
8. 数据结构草案
以下是后续实现阶段的逻辑结构,不代表本方案已授权创建迁移。
8.1 privileged_identity_operations
| 字段 | 说明 |
|---|---|
operation_id |
全局唯一幂等键,主键 |
operation_type |
五类操作及明确的授予/撤销子类型 |
status |
统一状态机状态 |
requested_by_account_id |
全局请求人身份 |
reason |
必填业务/合规理由 |
ticket_reference |
必填外部工单或事件引用 |
target_refs |
规范化实体引用,不存展示型敏感信息 |
evidence_refs |
只读诊断等证据引用 |
preview_id / preview_hash |
已确认的预览及防篡改摘要 |
preview_payload |
脱敏影响摘要和阻断项 |
expected_revisions |
执行时乐观并发校验依据 |
expires_at |
操作/批准执行期限 |
executed_by_account_id |
实际执行者或受控 worker 身份 |
result_summary |
脱敏结果及受影响数量 |
failure_code / trace_id |
脱敏失败信息和排障关联 |
created_at / updated_at / executed_at |
时间线 |
operation_id 对规范化请求摘要建立唯一绑定。相同 ID、相同摘要返回原记录;相同 ID、不同摘要返回 409 idempotency_conflict。
8.2 privileged_identity_operation_approvals
idoperation_idapprover_account_iddecision = approve | rejectcommentpreview_hashapprover_permission_revisionauth_strength/session_id_hashdecided_at/expires_at
约束:每个全局审批人对同一操作只有一个有效决定;请求人不能成为审批人;只有两个不同的有效批准才满足执行门槛。
8.3 privileged_identity_operation_snapshots
idoperation_idsnapshot_type = before | after | rollback_before | rollback_afterschema_versionencrypted_payload或受控对象存储引用payload_hashcreated_atretention_until
快照只保存恢复和审计所需字段;凭据仅记录 ID、scheme、状态、更新时间和不可逆指纹,不保存或导出密码哈希明文值。
8.4 privileged_identity_operation_events
追加式事件字段:event_id、operation_id、event_type、actor_account_id、from_status、to_status、reason_code、metadata、trace_id、occurred_at。不得更新或删除历史事件;更正通过追加事件表达。
8.5 support_access_grants
grant_idoperation_idsupport_account_idtenant_idpermission_scopesreason/ticket_referencestatus = scheduled | active | expired | revokedstarts_at/expires_at/revoked_atcreated_at/updated_at
访问判定必须直接或通过短 TTL 缓存核对该记录;缓存 TTL 不得晚于 expires_at。提前撤销属于紧急收权,可立即生效,但仍须写明原因并进入事后双人复核队列,不能借此扩大权限。
8.6 身份血缘
建议增加追加式 identity_account_lineage:记录 operation_id、relationship_type、source_account_id、target_account_id、created_at。用于解释合并、拆分和回滚历史,不作为登录授权来源。
9. 事务、并发与幂等
9.1 锁顺序
所有执行器必须按固定顺序和升序 ID 获取数据库锁,避免不同操作之间死锁:
privileged_identity_operationsidentity_accountsidentity_account_credentialstenantstenant_usersworkspace_membershipstenant_user_credentials- 平台管理员集合/权限承载记录
support_access_grants
如果数据库不支持相同粒度的行锁,必须使用等价的事务级 advisory lock 或串行化执行队列;不得退化为无锁读后写。
9.2 并发规则
- 同一账号、工作空间用户、成员关系或管理员权限上只能有一个
executing操作。 - 预览采用乐观版本,执行采用行锁加版本复核。
- 目标版本变化返回
409 stale_preview,不得自动套用旧预览。 - 两个操作影响集合相交时,后获得锁的操作应失败并重新预览,不等待到审批期限之后静默执行。
- 最后一名平台管理员检查必须与撤销写入处于同一事务和同一锁保护下。
9.3 幂等与外部副作用
- 身份数据库提交是幂等结果的事实来源。
- 会话撤销、通知、缓存失效使用事务 outbox,消息键包含
operation_id + effect_type。 - worker 重试先读取操作终态和 outbox 发送状态,不重复身份写入或重复通知。
failed只表示身份事务未提交;提交后外部通知失败不得把操作改回failed。
10. 回滚与事故处置
10.1 回滚规则
- 回滚不是隐藏的管理员按钮,而是一项新的受治理动作。
- 回滚前生成反向影响预览,验证后续业务状态是否仍允许恢复。
- 回滚需要新的
rollback_operation_id、理由、工单和两名不同平台管理员批准。 - 原请求人仍不得批准;原审批人可否参与由安全策略决定,但至少应有一名未参与原执行的审批人。
- 使用执行前快照和血缘恢复关系,仍执行当前唯一约束和管理员保底检查。
- 已被后续合法操作修改的实体不能盲目覆盖;返回
409 rollback_conflict并进入人工修复方案。
10.2 不完全可逆项
- 已发送通知不能撤回,只能发送更正通知。
- 已访问或已导出的数据不能通过权限回滚消除,必须进入安全事件处置。
- 密码凭据不得通过快照恢复哈希到另一主体;必要时强制重置。
- 已过期支持访问通常不“回滚”为重新激活,只能创建新申请。
10.3 告警
以下事件触发 P0 安全/运营告警:最后管理员保护失败尝试、rollback_failed、审批绕过尝试、重复执行产生不同结果、支持访问过期后仍成功授权、快照哈希不一致、身份事务出现部分提交证据。
11. 审计、披露与留存
11.1 必须记录
- 请求人、审批人、执行者的全局账号 ID 和当时权限版本
- 操作类型、理由、工单、目标实体 ID、预览哈希、版本集合
- 每次状态转换、拒绝/取消原因、执行耗时与结果数量
- 前后快照哈希、血缘、回滚关系和
trace_id - 支持访问的 scope、工作空间、开始/到期/撤销,以及所有使用该授权的敏感动作
11.2 最小披露
- 列表默认展示操作 ID、类型、状态、时间、脱敏目标、审批进度和工单号。
- 详情按角色展示,不向审批人披露无关工作空间或合同内容。
- 邮箱在列表中脱敏;需要核验完整值时使用单独授权动作并记录审计。
- 永不展示密码哈希、会话令牌、重置令牌、API 密钥或快照加密材料。
- 导出治理记录需独立导出权限、理由和审计,不得把快照原文作为普通 CSV 下载。
11.3 留存与完整性
操作、批准、事件和血缘记录应按合规策略长期留存;快照采用更严格访问控制和限定留存期。事件和快照哈希应进入现有审计完整性链或等价的防篡改机制。
12. 与现有工作空间模型的兼容方案
- 保留所有现有
tenant_id字段、外键和查询边界,不进行tenant -> workspace数据库重命名。 - API 和界面可使用“工作空间”,持久化映射仍指向
TenantModel。 workspace_type是产品语义依据:personal表示个人工作空间,organization表示组织工作空间,system仅用于平台系统边界。- 合并/拆分全局账号时只重映
WorkspaceMembershipModel.account_id或创建明确的新关系,不改合同、模板、审查、审批等业务表的tenant_id。 TenantUserModel继续作为工作空间内角色与展示投影;除非批准的恢复映射明确要求,否则保留其 ID。- 现有实体 ID 默认全部保留。确需重映时,在预览中逐条列出,并通过血缘记录解释。
- 个人与组织工作空间的业务数据永不互并;同一全局账号拥有两类工作空间成员关系是合法状态。
- 兼容期同时检查全局凭据和旧工作空间凭据,避免合并后残留可登录入口或拆分后错误共享凭据。
- 现有
diagnostic_id可放入evidence_refs,但不是operation_id,也不能授权任何写入。 - 停用/墓碑优先于删除,以兼容既有外键、历史审计和回滚。
13. 验收标准
13.1 通用门槛
- 所有五类操作都必须先获得未过期预览,提交时校验
preview_hash。 - 相同
operation_id、相同请求重试返回完全相同的操作和结果;不同请求返回409。 - 请求人审批自己的请求返回
403,不产生有效批准。 - 同一审批人重复批准只计一次。
- 只有两名不同、执行时仍有效的平台管理员批准后才能执行。
- 批准过期、审批人权限撤销、预览过期或目标版本变化均阻止执行。
- 执行过程中任一写入/不变量失败时,身份实体无部分变化。
- 成功操作具备前后快照、状态事件、操作者、批准者、工单和追踪 ID。
- 回滚经过独立预览、幂等 ID 和双人批准,不能直接覆盖后续合法变更。
- 所有列表/详情/错误响应通过敏感数据披露测试。
- 代码库不存在绕过统一执行器的同类破坏性身份写路由。
13.2 操作专项门槛
- 账号合并不改变任何业务记录
tenant_id,不复制平台管理员权限,并停用多余凭据。 - 账号拆分按明确映射移动成员关系,不复制密码哈希,不复制平台管理员权限。
- 成员恢复验证
tenant_user.tenant_id,且不提升角色或 owner 状态。 - 平台管理员撤销在并发条件下仍保证至少一名有效管理员。
- 支持访问必须有理由、工单、单工作空间、scope 和到期时间。
- 支持访问到期后,在线请求、旧会话和缓存命中都不能继续授权。
- 支持访问延期必须创建新的已批准操作。
14. 回归测试清单
14.1 权限与职责分离
- 普通个人用户、组织成员、工作空间管理员均不能预览或提交特权身份操作。
- 工作空间管理员不能因
role=admin获得平台审批能力。 - 请求人使用另一工作空间投影审批仍被识别为同一全局账号并拒绝。
- 被停用或刚撤权的平台管理员不能批准/执行。
- 前端隐藏绕过、直接 API 调用、篡改会话声明均被服务端拒绝。
14.2 状态机与幂等
- 每个合法状态转换成功,每个非法转换返回稳定错误码。
- 并发提交相同
operation_id只创建一条记录。 - 并发执行相同操作只提交一次,其他请求返回原结果。
- 相同 ID 不同载荷触发幂等冲突。
rejected、cancelled、expired、failed不能继续执行。- 服务重启和 worker 重试不重复写入或通知。
14.3 预览与并发
- 修改任一目标实体后,旧预览执行返回
stale_preview。 - 两个交叉影响账号/成员关系的操作不会死锁或部分提交。
- 最后两名管理员被并发撤销时最多一个操作成功,且仍保留一名有效管理员。
- 唯一约束冲突发生时整个身份事务回滚。
- 锁超时返回可重试错误,不把操作错误标记为成功。
14.4 合并、拆分与恢复
- 合并覆盖无重叠成员、同工作空间重叠成员、停用成员、owner 成员和旧凭据并存场景。
- 个人与组织工作空间同时存在时,合并前后业务数据归属不变。
- 拆分覆盖新账号、既有账号、邮箱占用、未验证邮箱和凭据重置场景。
- 拆分映射遗漏或重复成员关系时预览阻断。
- 恢复覆盖重激活、重新关联、重建以及目标已被占用场景。
- owner 恢复时
owner_user_id不一致会阻断或明确进入审批差异。
14.5 支持访问
- 缺理由、缺工单、空 scope、跨工作空间或超最大期限均拒绝。
- scope 外页面、API、下载和导出返回
403。 - 到期、提前撤销、支持账号停用后立即失效。
- 授权缓存 TTL 不超过
expires_at。 - 每次敏感访问审计均含
grant_id,且不泄露无关内容。 - 原地延期请求被拒绝;新申请需重新双批准。
14.6 回滚、审计与披露
- 合并、拆分、恢复和管理员权限变更在无后续冲突时可按快照回滚。
- 存在后续合法修改时回滚返回冲突,不覆盖新数据。
- 回滚失败不留下部分恢复状态,并触发 P0 告警。
- 前后快照哈希可验证,审计事件只追加不可修改。
- API 响应、日志、事件、通知和导出均不含密码哈希、令牌或无关敏感字段。
- 账号冲突
diagnostic_id只能作为证据引用,不能用来调用执行接口。
15. 分阶段 Backlog
P0:先建立不可绕过的治理底座
- 冻结并扫描直接身份破坏性写入口,定义统一执行器边界。
- 固化五类操作 schema、状态机、状态转换权限和稳定错误码。
- 实现只读预览、规范化载荷、
preview_hash和expected_revisions。 - 实现
operation_id幂等请求、双人批准、审批过期和职责分离。 - 实现数据库事务、固定锁顺序、最后管理员并发保护和 outbox。
- 实现追加式事件、脱敏前后快照、血缘和审计完整性接入。
- 实现支持访问服务端授权检查、自动到期和紧急撤销。
- 建立 P0 自动化测试:审批绕过、重复执行、部分提交、最后管理员和过期后访问。
P0 发布门槛:没有任何旁路写 API;事务/并发故障注入通过;支持访问到期后不可继续访问;安全和运营共同签字。
P1:受控运营台与完整回滚
- 建设操作列表、预览差异、审批、执行状态和审计详情页面。
- 对接只读账号冲突诊断,一键带入证据引用,但不自动提交操作。
- 实现回滚预览、独立审批、冲突检测与人工事故工作台。
- 完善支持访问 scope 模型、业务审计关联和用户通知。
- 接入工单系统引用校验、审批提醒、过期提醒和结果通知。
- 增加跨数据库、worker 重试、断电恢复和高并发回归。
P1 发布门槛:五类操作均可在受控运营台完成端到端演练;回滚演练和审计导出满足合规验收;界面无敏感信息过度披露。
P2:规模化运营与持续监督
- 风险分级、异常模式检测和高风险操作二次安全挑战。
- 审批 SLA、过期率、失败率、回滚率、支持访问使用率仪表盘。
- 定期复核平台管理员名册、孤儿成员关系和未关闭支持授权。
- 为治理策略提供版本化配置、灰度与审计,不允许降低 P0 硬约束。
- 建立季度灾难恢复演练、权限抽样复核和审计完整性证明。
16. 上线与运营门禁
- 设计评审:产品、身份后端、安全、数据库、运营、合规共同确认操作不变量。
- 数据评审:迁移方案必须独立审批;本设计文档不授权创建表或修改现有数据。
- 预生产演练:使用合成账号覆盖五类操作、并发、失败注入和回滚。
- 灰度:先仅允许指定平台治理小组使用,所有执行触发实时告警。
- 生产门禁:P0 测试全绿,旁路扫描无结果,审计完整性校验通过,回滚手册完成演练。
- 日常运营:每周复核失败/过期操作,每月复核管理员与支持授权,每季度演练回滚。
- 停止线:出现部分提交、越权执行、过期访问、最后管理员失守或审计缺失,立即停用执行入口,只保留查询和事故处置。
17. 明确非目标
- 本阶段不修改
TenantModel、tenant_id或现有业务表的数据归属。 - 本阶段仅实现治理记录迁移、只读预览、操作登记、查询、审批页面与审批状态机;不实现身份数据执行、回滚或修复。
- 本阶段不自动根据冲突诊断执行合并或恢复。
- 本阶段不把组织管理员提升为平台管理员。
- 本阶段不允许支持人员无审批代入用户身份,也不共享用户密码或会话。
本文件是后续自动化开发的治理输入。任何实现任务必须先从 P0 底座拆分技术设计与迁移评审,并以本文件的验收标准和回归清单作为发布门槛。
18. 第一阶段实现检查点(2026-08-02)
已实现且可进入持续回归:
- 平台管理员专属的六类只读影响预览;预览返回规范化目标、
preview_hash、expected_revisions、影响摘要、警告和 15 分钟有效期。 - 持久化治理操作与审批记录。首次登记返回
201;相同operation_id与相同载荷重放返回原记录和200;不同载荷复用同一 ID 返回稳定409。 - 请求人与审批人职责分离;请求人不能审批自己的操作;同一全局账号重复审批只计一次;两个不同且当前有效的平台管理员批准后状态进入
approved;任一拒绝进入rejected。 - 同一审批人的并发重放通过唯一约束和冲突回读保持幂等,不产生重复审批或未处理的
500。过期状态当前采用读时投影,列表筛选与详情返回一致。 - 管理页面提供预览、登记、列表、详情与批准/拒绝入口,并持续展示“治理记录不执行身份变更”。没有
/execute,也没有直接合并、拆分、恢复、授予、撤销或支持访问写路由。 - 迁移
20260802_0037只新增privileged_identity_operations和privileged_identity_operation_approvals,不修改账号、工作空间、成员关系、凭据、平台权限或业务数据归属。
本检查点不授权把 approved 解释为已执行。以下能力仍是后续发布门槛:追加式状态事件与 trace_id、脱敏 before/after 快照、真实 PostgreSQL 并发和锁验证、身份执行器、outbox、支持访问在线鉴权与自动到期、回滚预览/审批/演练、外部工单与通知接入。上述门槛完成前,身份数据执行入口必须保持关闭。
19. 第二阶段实现与验收检查点(2026-08-02)
已完成:
- 迁移
20260802_0038新增追加式privileged_identity_operation_events,包含单操作递增序号、事件类型、状态 before/after、actor、trace_id、脱敏快照、前后快照哈希和时间戳。 - 提交、审批、拒绝和过期物化在操作事务内追加事件;幂等重放不重复追加。过期操作由首次相关读取或审批触发物化,后续读取不重复产生
operation_expired。 - 快照只保存操作状态、计数和目标引用指纹;敏感字段名和值均不进入快照。快照哈希可由返回快照重新计算验证。
- 新增平台管理员专属只读事件接口和前端事件时间线;时间线展示状态转换、
trace_id和脱敏摘要,不暴露原始目标值。 - 真实 PostgreSQL 临时 schema 并发测试通过,覆盖并发幂等提交、同审批人去重、不同审批人有序批准、拒绝终态、过期唯一事件、追踪与快照完整性及最后管理员保护;测试 schema 已清理,公共迁移基线已恢复且没有公共事件表残留。
- Contract API 全量
220 passed, 1 skipped,真实 PostgreSQL 专项单独1 passed;Web 定向28/28、TypeScript、Python 编译、101 路由生产构建和git diff --check均通过。
继续关闭:
- 身份执行器、账号合并/拆分/恢复实际写入、平台权限授予/撤销实际写入和支持访问授权实际写入。
- outbox、执行失败补偿、跨实体固定锁顺序、执行前
expected_revisions再校验、回滚状态机与回滚演练。 - 支持访问在线鉴权、自动到期与紧急撤销,真实工单系统校验、审批提醒、结果通知和审计导出。
因此 approved 仍只能解释为“审批完成、等待未来受控执行器”,不能解释为身份数据已经变更。
20. 第二阶段最终交付复核(2026-08-02)
- 已登录平台管理员浏览器打开合成治理记录
op_browser_events_20260802,事件时间线完整展示追加式序号、状态转换、trace_id、脱敏计数和快照哈希前缀。桌面全页视觉复核未发现重叠,页面控制台 error/warn 为0。 - 独立质量复核后新增迁移
20260802_0039,由 SQLite/PostgreSQL 数据库触发器拒绝治理事件UPDATE/DELETE;本地自动建库路径安装等价保护。列表与详情统一映射过期物化等治理异常,避免极端并发冲突退化为500。 - 最终本地回归为治理专项
10/10、Web 全部定向 Node 测试34/34、TypeScript 与 Python 编译通过;Next.js 生产构建生成101个页面/路由,git diff --check无错误。 - 运行时健康复核确认 Web
3021、Contract API8110、AI Platform8111均返回200;运行中 OpenAPI 的特权身份路径仅包括 preview、create/list、detail、events 和 approvals,破坏性身份写路径数量为0。 - Alembic 代码 head 为
20260802_0039。真实 PostgreSQL 专项使用临时 schema 验证不可变触发器,测试结束后清理,不把测试迁移或事件表遗留在公共 schema。
本检查点完成后仍不得把审批状态机扩展为直接身份写入。下一阶段必须以独立设计和验收任务建设统一执行器、固定锁顺序、执行前版本复核、outbox、失败补偿、支持访问在线鉴权/到期/紧急撤销,以及独立回滚预览和双人审批。
21. Transactional outbox 最小切片检查点(2026-08-02)
本阶段新增迁移 20260802_0040 和 privileged_identity_outbox_messages,但不改变身份执行边界。提交、批准、拒绝和过期物化在追加状态事件的同一数据库事务内生成一条 outbox 消息;(event_id, effect_type) 与 idempotency_key 均有唯一约束,因此操作或审批的幂等重放不会重复排队。
- 迁移不为
0038/0039已存在的历史事件回填 outbox,避免把历史状态重新解释为待投递副作用;投递状态只覆盖 0040 上线后新产生的事件。 - outbox payload 只包含事件/操作 ID、序号、类型、状态、计数、目标指纹、安全
trace_id、快照哈希和发生时间,不包含完整快照、操作者、邮箱、姓名、工单引用或正文、凭据、令牌、密码、secret 或原始目标引用;payload_hash可由 payload 重算。 - worker 基座采用 at-least-once 语义:PostgreSQL 使用
FOR UPDATE SKIP LOCKED认领;消息认领后持有有限租约,失败按指数退避重试,达到最大次数进入dead_letter。SQLite 领取使用BEGIN IMMEDIATE防止意外重复认领,但部署仍只支持单进程、单 worker,不作为横向扩容方案。 - 平台管理员仅可调用
GET /api/v1/identity/privileged-operations/{operation_id}/delivery-status查看消息 ID、事件、状态、尝试次数、稳定错误码和时间;响应不返回payload、lease_owner或错误详情。 - 当前 transport 默认为禁用且不领取消息;没有 SMTP、工单或其他外部副作用,没有人工
POST retry,也没有 execute、rollback、merge、split、recover、grant、revoke 或真实 support access 写入口。 - 发布采用先迁移后发代码;代码回滚时保留 outbox 表。执行
0040数据库 downgrade 前必须停止 worker,并确认不存在 queued、processing、retry_scheduled 或 dead-letter 消息,避免丢失投递意图或事故证据。 - SQLite 迁移与治理专项为
18/18,包含提交、批准、拒绝、过期故障注入、delivery-status GET 无写副作用和底层并发认领互斥;页面专项 Node2/2、Web 全量 Node34/34、TypeScript、Python compileall 和 101 路由生产构建已通过。PostgreSQL 专项已在192.168.3.90:5432/yuqei_contract的隔离临时 schema 中1/1通过,覆盖 0040 head、事件/outbox 一一对应、并发领取互斥、租约恢复、稳定幂等键、重试和 dead-letter;测试 schema 已精确清理。
下一阶段仍须单独评审 transport 选择、消息消费者幂等契约、告警与死信处置 SOP;启用真实 transport 前,还必须评审客户端可控 operation ID 与无密钥目标指纹的外发关联风险,必要时改用服务端 opaque ID/HMAC 或移除非必要指纹。上述能力不得被解释为身份执行器已经获准开放。
22. 全局 outbox 只读可观测与死信排查交付检查点(2026-08-02)
22.1 目标与边界
本阶段只补齐平台管理员对 0040 outbox 的全局健康判断和死信排查能力,复用现有 privileged_identity_outbox_messages,不新增 0041 迁移。所有入口均为只读 GET,不得认领、续租、发送、重试、重排、确认或删除消息,也不得物化治理操作过期状态。
- 活跃消息统一指
queued、processing、retry_scheduled。 - 默认超龄阈值为 900 秒;只读查询可通过受限的
stale_after_seconds调整阈值,但不得改变数据库配置或消息状态。 - 临近最大尝试指活跃消息的
attempt_count >= max_attempts - 1;过期租约指processing消息存在租约且lease_expires_at早于查询时刻。 - 健康分级按最高风险优先:存在任意
dead_letter或过期 processing 租约为critical;否则存在超龄活跃消息或临近最大尝试消息为degraded;其余为healthy。空队列必须为healthy。 - 最老活跃消息年龄在没有活跃消息时返回
null,不能伪造为0;所有阈值计算复用同一个generated_at查询时刻。真实 PostgreSQL 专项已在隔离临时 schema 中验证并发读一致性,以及 overview/dead-letter 两个只读查询前后 operation、approval、event、outbox 全字段快照完全一致。
22.2 API 与脱敏契约
使用与单操作详情分离的命名空间,避免和 {operation_id} 路由冲突:
GET /api/v1/identity/privileged-outbox/overviewGET /api/v1/identity/privileged-outbox/dead-letters
两个接口仅允许当前仍有效的平台管理员访问;个人用户、企业普通成员、企业管理员和已撤销平台权限的旧会话均返回 403,未登录返回 401。接口实现不得调用 _materialize_expired_operations 或任何 worker claim/transition 方法。
overview 至少返回查询时刻、健康级别、各状态计数、活跃总数、最老活跃消息年龄、超龄活跃数、过期 processing 租约数、临近最大尝试数,以及按稳定错误码汇总的 dead-letter 数量。空白或缺失错误码统一归入固定的 unknown,不把原始错误详情转换为展示文本。
dead-letters 提供稳定分页、稳定错误码过滤和确定性排序。默认按 updated_at DESC, message_id DESC 排序,分页参数有明确上限;operation type 和 effect type 作为只读排查上下文返回,本切片不提供对应组合筛选。单项只允许返回:
- message、event、operation ID;
- event type、operation type、effect type;
- 安全
trace_id; - attempt/max attempts;
- 稳定错误码;
- created、updated、last attempt 时间。
两个响应均不得返回 payload、payload hash、idempotency key、lease owner、lease token、原始错误详情、目标指纹、快照或快照哈希、actor、邮箱、姓名、账号/成员身份信息、工单正文、凭据或 secret。列表和聚合也不得通过筛选项、错误文案或导出功能间接泄露这些字段。
22.3 只读一致性与验收标准
- 平台管理员访问 overview 和 dead-letters 返回
200;普通用户和企业管理员返回403,未登录返回401。 - 空队列返回
healthy,所有状态计数为 0,最老活跃消息年龄为null,dead-letter 列表为空页。 - queued、processing、retry_scheduled、过期租约、超龄、临近最大尝试和 dead-letter 的计数与健康优先级符合 22.1,阈值等于边界值时有固定且已测试的包含/排除语义。
- dead-letter 按稳定错误码聚合准确;空错误码只进入
unknown,响应不包含原始错误详情。 - dead-letter 分页无重复、无遗漏,稳定错误码过滤准确,时间相同记录仍按 message ID 确定性排序,非法页码、页大小和阈值返回稳定
422。 - 对同一数据库连续调用两个 GET 前后,operation、approval、event 和 outbox 的行数、状态、attempt、lease、时间戳、payload 与 hash 完全不变;不得因查询物化过期操作或追加事件/outbox。
- 响应字段与序列化后的 JSON 均通过敏感字段泄露扫描,不出现 payload、幂等键、租约所有者、原始错误、目标指纹或身份信息。
- transport disabled 时全局只读接口仍可用,worker 发送/认领数量保持 0,调用接口不会触发 claim。
- OpenAPI 只新增上述两个 GET,不出现
POST retry/requeue/ack,也不出现 execute、rollback、merge、split、recover、grant、revoke 或 support access 身份写路径。 - SQLite 定向测试、Web 定向测试、TypeScript、Python 编译、生产构建和
git diff --check通过。 - PostgreSQL 0040 并发认领、租约恢复和本阶段只读一致性查询已在
192.168.3.90:5432/yuqei_contract的隔离临时 schema 中1/1通过;测试完成后 schema 已精确清理,未以 SQLite 结果替代。
本地交付证据:治理与迁移专项 20/20、反滥用专项 9/9、Web 全量 Node 36/36、页面专项 4/4、真实 PostgreSQL 0040/并发/租约/死信/只读一致性专项 1/1、TypeScript、Python compileall、单一 Alembic head 20260802_0040、101 路由生产构建和 git diff --check 均通过。运行中 OpenAPI 对两个新路径各注册 1 个 GET,outbox 写方法数量为 0;已登录平台管理员桌面/390px 移动视图加载成功且控制台 error 为 0。Contract API 全量测试未在本检查点复跑,不能用上述定向结果替代。
22.4 死信排查 SOP
- 值班人员只读打开 overview,记录查询时刻、健康级别、dead-letter/过期租约/超龄/临近上限计数和使用的阈值;
critical立即建立 P0/P1 事故记录并通知身份治理与平台值班负责人,degraded按 P1 运营异常跟踪。 - 在 dead-letters 中按稳定错误码、operation type 和 effect type 缩小范围,记录 message/event/operation ID、安全
trace_id、attempt/max attempts 与时间,不复制 payload、目标引用、actor 或原始错误到工单。 - 使用安全
trace_id和三个不透明 ID 在受控日志/审计系统交叉核对部署版本、数据库连接、消费者版本和外部依赖状态;需要查看受限原始日志时必须走既有最小权限与审计流程,不能扩展本页面响应。 - 判断是否为代码缺陷、配置/依赖故障、租约异常或消费者幂等冲突。禁止直接
UPDATE/DELETEoutbox、修改 attempt/lease、手工重排、人工调用 transport 或绕过审批执行身份写入。 - 修复方案通过独立变更评审和回归后发布。当前没有人工 retry/ack 能力,历史 dead-letter 保留为事故证据;若未来需要恢复投递,必须另立设计,定义授权、幂等、审计和回滚并重新通过安全评审。
- 发布后用 overview 观察新消息是否继续进入异常状态,并在事故记录中附只读查询证据。关闭事故不删除或改写 dead-letter;涉及 PostgreSQL 多 worker 的结论必须引用隔离 schema 并发验收记录。
22.5 本阶段明确非目标与发布门禁
- 不新增数据库迁移,不改变 0040 状态机、约束、payload 或历史不回填策略。
- 不增加
POST retry、requeue、ack、cancel、delete、lease takeover 或任何人工消息状态写接口。 - 不启用 worker transport,不连接 SMTP、工单、Webhook 或其他外部消费者,不把可观测页面作为 transport 上线批准。
- 不实现消费者业务副作用、身份执行器、支持访问授权、失败补偿或回滚;审批完成仍不代表身份数据已变更。
- 不开放 execute、rollback、merge、split、recover、grant、revoke,不直接写账号、成员、凭据、平台权限或工作空间归属。
- 真实隔离 PostgreSQL 0040 并发验证现已通过,但该结果不等于外部副作用获准上线。完成 transport 选择、消费者幂等契约和外发关联风险评审前,多 worker 与真实 transport 继续阻断。
23. Outbox 数据库完整性加固检查点(2026-08-03)
本检查点是 22 节只读可观测交付之后的独立数据库加固阶段。22.5 中“不新增 0041”仅描述当时不改变 0040 模型的阶段边界;本阶段根据独立质量复核发现的跨操作事件归属和非法租约组合风险,新增迁移 20260803_0041,不回改已经验收的 0040 历史迁移。
- 升级前先只读统计 outbox 与 event 的 operation 归属错配,以及 status/lease 非法组合。任一计数非零时拒绝迁移,不自动修复、删除或重排数据;跨操作错配必须作为审计完整性事件调查。
- 事件表增加唯一索引
(operation_id, event_id);outbox 增加(operation_id, event_id)复合外键并保持ON DELETE NO ACTION。PostgreSQL 由外键直接拒绝把操作 A 的消息关联到操作 B 的事件;SQLite 默认可能关闭外键执行,因此迁移与自动建库路径同时安装 outbox INSERT/UPDATE 拒绝触发器,不能只依赖外键声明。 - 新增租约 CHECK:
processing必须同时具有lease_owner和lease_expires_at;任何非processing状态必须同时清空两项。该约束避免 processing 永久滞留且无法被重领/告警,也避免 queued、retry、delivered 或 dead-letter 残留租约。 - SQLite batch-recreate outbox 表并安装两条关联拒绝触发器,事件表只增加唯一索引;0039 的事件 UPDATE/DELETE 拒绝触发器保留。PostgreSQL 增加索引和约束时可能取得表锁,发布时必须短暂停止 Contract API 治理写流量;当前 transport 已关闭,不存在 worker 写流量。
- downgrade 只移除复合外键、租约 CHECK 和事件唯一索引,不删除或改写 operation、approval、event、outbox 数据。若需要回滚,仍应先停止治理写流量并保留审计证据。
验收证据:SQLite 0041 迁移专项 2/2 通过,覆盖两类升级前拒绝、复合外键、六种非法租约组合、四种合法租约状态及 downgrade/upgrade;治理与 worker 专项 19/19 通过。真实 PostgreSQL 隔离 schema 专项 1/1 通过,覆盖 0041 head、约束元数据、跨操作关联拒绝、非法租约拒绝、并发 SKIP LOCKED、租约恢复、重试/dead-letter、只读可观测无副作用和 schema 精确清理;组合专项合计 22/22。
Contract API 单进程全量回归在 10 分钟命令上限处被终止且未返回失败堆栈,因此不把该次运行写成通过。随后按四个隔离进程覆盖全部 24 个测试模块,收集并执行 234/234 项全部通过;各组分别为合同工作流 91/91、身份与授权 83/83、0041/特权身份/PostgreSQL 22/22、基础设施与迁移 38/38。Python compileall、单一 Alembic head 20260803_0041 和 git diff --check 通过。
本地运行 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 隔离 schema1/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 排序,跨类型顺序固定为:
privileged_operationprivileged_approval_setidentity_accountworkspacetenant_userworkspace_membershipidentity_account_credentialtenant_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 单一 head20260803_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 单一 head20260803_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隔离 schema1/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隔离 schema1/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、共享会话和人工绕过入口全部关闭。
34. support access 不可变持久化与数据库可信时间检查点(已验收,2026-08-09)
本检查点处理第 33 节 No-Go 评审的第一组 P0 基础设施,但不改变发布结论。新增的是 validation-only policy/revocation 证据账本和内部检查 repository,不是运行时授权、会话或 impersonation 能力。
34.1 数据与并发契约
- 加法迁移
20260809_0044新增privileged_support_access_policies和privileged_support_access_revocations。policy 按 operation 唯一,保存固定 policy version/hash、最小 scope、subject 指纹、审批时账号/工作空间 revision、起止时间和登记主体;revocation 每个 grant 最多一条,保存固定原因码、撤销 hash、数据库时间和撤销主体。 - 两张表在 SQLite/PostgreSQL 都由触发器禁止 UPDATE/DELETE;policy 表额外以 CHECK 强制
access_enabled=false、policy_version=1和expires_at>issued_at。所有外键使用NO ACTION,不允许级联删除审计证据。 - 内部
PrivilegedSupportAccessPolicyRepository仅允许当前有效平台管理员从 ready 的support_access_grantoperation 登记 validation policy。同一 operation 并发或重放只保留一条 policy;同一撤销重放幂等,不同原因码改写返回稳定冲突。 - 登记、在线检查和紧急撤销均读取数据库
CURRENT_TIMESTAMP,不接受调用方时间。在线检查每次读取当前 operation/审批人状态、policy 材料、账号/工作空间状态与 revision,并继续调用 validation-only 策略;所有结果固定access_granted=false。 - 运行时没有新增 FastAPI、Web、token/session、transport 或 impersonation 入口;repository 未被 API 路由引用。
34.2 验收证据与发布边界
- SQLite support access 纯策略与持久化专项
19/19,计划、投递、反滥用组合44/44,迁移2/2通过;覆盖数据库时间、幂等登记、审批人失权、revision 漂移、撤销幂等/冲突、数据库不可变和 disabled CHECK。 - 真实 PostgreSQL
192.168.3.90:5432/yuqei_contract隔离 schema1/1通过,覆盖并发登记只产生一条 policy、并发撤销只产生一条 revocation、UPDATE/DELETE 拒绝和随机 schema 清理;凭据未落盘。 - 本地运行库升级到
20260809_0044前已备份为.runtime/db-backups/contract-api-before-0044-20260809-2100.sqlite3。升级后 Contract API8110重启健康,schema-check 显示 actual/head 均为20260809_0044。
本阶段只关闭了 durable state 和可信时间的部分缺口,仍未关闭请求检查与资源读取的 TOCTOU、强认证/会话绑定、撤销跨节点传播 SLA、逐请求不可变审计、统一 deny-by-default enforcement point 和隐私/法律门禁。因此第 33 节 No-Go 结论不变,真实 support access 与身份执行器继续关闭。
35. deny-only enforcement point 与锁内资源锚点检查点(已验收,2026-08-09)
本阶段把第 33 节的“统一 deny-by-default enforcement point”收敛为内部验证契约,仍不提供任何 API 或真实授权能力。请求只能使用固定的 contract.read、contract.comment、review.read、audit.read、template.read 动作,由服务端动作表推导 scope 和资源类型;决策模型固定为 enforcement_status=denied、access_granted=false、executor_enabled=false。
- support access repository 新增单事务
evaluate_validation_under_lock。固定顺序为 policy、operation、approval set、账号/工作空间/成员/归属、撤销记录和资源锚点;锁全部取得后刷新 ORM 状态,并用 PostgreSQLclock_timestamp()(SQLite 使用锁后重新查询的CURRENT_TIMESTAMP)重新检查审批、过期、撤销、主体、scope、revision 和资源租户归属。 - 当前可锁定资源只有合同和审查任务;审计动作锁定合同锚点;模板尚无稳定的租户/版本资源行,因此固定返回
support_access_resource_not_lockable。任何缺失、漂移、锁超时、策略损坏或依赖错误均 fail closed,且不返回资源内容、token、session 或 grant。 - 事务要求无
new/dirty/deleted状态变化,policy/revocation/operation/approval/identity/event/outbox 快照保持不变。真实 support access、身份执行器、共享会话、impersonation 与破坏性身份写接口继续关闭,No-Go 不变。 - 验收证据:enforcement 与 support access 持久化专项
5/5,计划/特权身份/投递/反滥用组合59/59,真实 PostgreSQL 隔离专项3/3,Python compileall、git diff --check和危险路由扫描通过;随机 schema 残留为0。
闭环范围仅限 deny-only validation 和可锁资源锚点;真实资源读取、强认证与会话/设备绑定、跨节点撤销传播、逐请求不可变审计/告警、隐私与法律门禁仍是后续独立阶段。任何具名法务签批必须由实际法务责任人针对正式版本留下可核验证据,AI 或项目人员不得代签、推定完成或伪造姓名、日期、签名、批准记录及生效版本。
36. support access 安全控制账本与逐请求审计复核(代码验收,发布 No-Go,2026-08-10)
迁移 20260809_0045 新增 fail-closed singleton control、追加式 control events、脱敏 request audits 和 audit outbox。默认 kill switch、外部 IdP 和隐私法律门禁全部阻断,executor_enabled=false 同时由 DTO、ORM 和数据库 CHECK 约束。控制账本和请求账本在 SQLite/PostgreSQL 均禁止 UPDATE/DELETE。
- 每次内部验证先锁 control,再按固定顺序锁 policy、operation、approval/identity 和可锁资源;资源只在 tenant/deleted 条件及行锁内生成 anchor hash,不返回资源内容。
- policy 撤销在同一事务递增 revocation epoch 并追加事件;请求携带的 observed epoch 必须与数据库一致。配置的 5 秒 SLA 仅是 fail-closed 策略上限,尚未在多服务节点和真实缓存/消息基础设施上测量。
- IdP assertion、MFA 类型、step-up 时间、session/device hash 和 audience 是受信内部认证边界输入,不是面向客户端的凭据,也不证明外部 IdP、WebAuthn、硬件 OTP 或会话生命周期已经接通。
- 每个 deny-only 决策在同一事务先 flush 不可变 audit,再追加 outbox;PostgreSQL 即时外键确保不存在孤立 outbox,失败时整体回滚。审计只保存哈希、门禁状态、错误码、epoch 和 trace,不保存资源内容、token、session 或原始身份字段。
- SQLite 专项
24/24、真实 PostgreSQL3/3、全量 Contract API281 passed, 3 skipped,其中 3 个 PostgreSQL opt-in skip 已由真实专项补齐;运行态 OpenAPI 中 support access API0、危险身份写路由0。
发布仍为 No-Go。需要外部完成的门禁是:真实 IdP/MFA 与会话绑定集成、跨至少两个节点的撤销传播 SLA 测量、kill switch 控制面/权限/告警演练、真实资源授权后读取,以及具名隐私和法务批准。不得根据内部 assertion 字段、控制表或自动化测试推定这些门禁已经通过。