From c236d3099dd3e20f7bdd35caf162e0be9a7d52eb Mon Sep 17 00:00:00 2001 From: weichen Date: Wed, 14 Oct 2026 01:48:47 +0000 Subject: [PATCH 1/3] Define the U3 account directory and owner recovery contract Why: refine the approved IAM scope into enforceable account, directory and recovery boundaries. Evidence: SELF document checks and independent document reviews; business tests and CI are NOT_RUN. Impact: proposed contract and implementation plan only; preserve the approved global plan and all runtime data. Refs: Dev feature/iam-u3-account-directory-recovery; IAM r1 U3 and IAM-D/IAM-C --- ...-10-14-iam-u3-account-contract-and-plan.md | 211 ++++++++++++++++++ docs/proposals/INDEX.md | 4 + 2 files changed, 215 insertions(+) create mode 100644 docs/proposals/2026-10-14-iam-u3-account-contract-and-plan.md diff --git a/docs/proposals/2026-10-14-iam-u3-account-contract-and-plan.md b/docs/proposals/2026-10-14-iam-u3-account-contract-and-plan.md new file mode 100644 index 0000000..1310209 --- /dev/null +++ b/docs/proposals/2026-10-14-iam-u3-account-contract-and-plan.md @@ -0,0 +1,211 @@ +# IAM U3:账号资料、有效态、受控目录与本人恢复 + +版本:r1 草案;日期:2026-10-14;owner:weichen。状态:待阶段 4–5 人工评审,未实施、未验收、未合入、未部署。 + +主任务:Dev `feature/iam-u3-account-directory-recovery`。承接用户对下一步 U3 的“授权”、[统一方案 r1](2026-10-13-identity-role-permission-plan.md) U3 及 [IAM-D/IAM-C 合同](2026-10-13-iam-i1-permission-contract.md) §5–6。统一方案原文 SHA256 为 `39b8e9a6e078dc509bd80118c1807ce6ed23e4466e5902b51f4bfa255d55c0a1`,本补充不修改其正文,不扩展到 U4–U9。 + +本文落实已经批准的人员资料、管理员业务隔离和本人恢复原则,补齐实际公共接口与切换方法。网页建号不再接受初始密码/管理员位、旧凭据不能自动成为本人证明,是本次需要明确审阅的兼容取舍。用户的启动授权不代替新对象的阶段评审。Review、CI 与合入按 Dev `policy/delivery.md`;生产迁移、部署、服务/真实账户操作、异常人工恢复和清理另行授权。 + +## 1. 核查对象与现状 + +以下仅为固定源码观察(SELF),不是运行结果。业务测试、真实 PostgreSQL 重放、浏览器、部署及迁移均 `NOT_RUN`。 + +| 对象 | 固定版本/位置 | 观察及影响 | +|---|---|---| +| Dev 启动基线 | `add097d06137c4912a4ed9279e98ca353e4e4cf1` | 已取得启动收据并建立独立任务工作区;原 I1 已交付,不重做 | +| Backend | `5d5fc9891c0eb8994545ccb8b740535d9b99fb0e`,`internal/store/auth.go` | `users.id/owner` 稳定,已有姓名、原子建号及 auth_version;缺资料版本与停用状态 | +| 当前扩展面 | 同 Backend,`pkg/extension/extension.go`、`internal/app/hostcaps.go` | 实际为 `2.12.0-experimental`,EXT-19 已有 Name 和姓名搜索;目录直接取所有账号,Valid 恒 true,无提供者字段授权 | +| 认证与恢复 | 同 Backend,`internal/server/accounts.go`、`internal/store/login_keys.go` | 管理员仍直接取目标明文 Key/设置密码;SessionInfo 不核对当前账号版本;部分最终写缺同账号锁/CAS | +| RDM 消费源码 | `6fcb256205c2384abed35d3b6ab96ca7216e9297` | 成员目录有 member.manage 检查;缺能力时有效性检查放行、调用者丢弃错误;三权只校验项目成员关系 | +| Frontend | Dev 基线所固定版本,`src/components/adminSections.tsx`、`src/api.ts` | 管理页提供签发/复制目标恢复 Key;必须随新合同收口,不能只改 Backend | +| 本文 Documents 起点 | 权威 `origin/main`:`7bd4e17827d4c7c88647bf3c9199c495b60cb107` | 独立 `feature/iam-u3-account-contract`;不在 Dev 的固定子模块里改源码 | + +Backend `account_lifecycle_test.go`、`login_keys_test.go` 和现有 HTTP auth helper 大量硬编码 OpenSQLite。仅设置 PG DSN 不会让这些场景在 PostgreSQL 执行;历史报告中的 PG 覆盖不能作为本轮证据。 + +在途 `feature/svc-account-durability-plan` 当前授权集中于文档/svc 帮助及其测试,不替代 U3 业务实施。实施前再核验 Backend/RDM 权威 HEAD、在途迁移及写者范围;迁移编号和扩展面次版本由当时基线分配,不预占 v31 或 2.13。 + +## 2. 需求、范围与验收对象 + +| 需求 | 本轮具体结果 | 可判定验收条件 | +|---|---|---| +| R3/R5 | 可选资料及独立资料写面 | 姓名、电话、邮箱、工号、部门、备注全部可空;填写/清空保持原 userId、owner、角色、凭据和业务数据;本人不能借资料 patch 改权限 | +| R4/R5 | 精确/模糊、字段与状态过滤、分页及最小候选投影 | 同名不误选;未授权字段不返回、不匹配、不影响数量/分页;稳定 ID 决定选择对象;职位过滤明确留 U4 | +| R11/R12 账号部分 | 有效态、认证与执行边界检查 | 停用拒绝新请求/登录/兑换,旧会话与 API key 不再认证,候选人不能提交,持续流停止新输出;启用不恢复已撤会话和 Key | +| R7/R12 恢复部分 | 管理员提案、独立本人确认、受限兑换 | 管理员拿不到目标设密能力;初始密码/旧 Key 派生链不成为证明;无独立证明线上拒绝;完成后本人重新密码登录 | + +U3 不建设通用职位权威、多职绑定、角色 cap 配置、完整管理矩阵、项目职位重选或统一跨库 authorityEpoch。现有 RDM 单角色、项目成员及文档职责保持各自权威。本人资料和恢复只在必要的既有界面中闭合;完整系统用户/职位配置体验仍是 U7。 + +## 3. 单一账号权威与数据 + +Backend 复用 `users.id` 与唯一 `owner`;owner 仍为登录名/兼容定位,不允许资料修改改变身份。RDM 不复制账号表,不把显示名或工号当身份主键。 + +| 数据 | 规则 | +|---|---| +| Profile | `name/phone/email/staffNo/department/notes` 默认空;`profileVersion` 独立递增,显式空值表示清空;未知字段整体拒绝 | +| AccountState | `enabled`、`stateVersion`;valid 表示账号存在且 enabled。未设密码/强制改密是独立认证限制,不混作停用 | +| Credential lineage | 持久来源及是否具备本人证明资格;`legacy_unknown` 默认无资格,普通管理员路径永远不能提升资格 | +| Sessions | 保存受信认证方式、凭据来源、auth_version;受限会话关联确切 activation/recovery grant;未知来源不作证明 | +| Directory revision | 独立、持久、事务内更新;候选目录版本只受其可见辨识资料/有效态变化影响,私密联系方式编辑不能成为候选查询的侧信道 | +| Recovery | 独立 proposal/proof-session 引用/grant 记录;仅存随机凭据摘要,保留目标、用途、到期、状态、版本、操作者与因果关联 | + +建议长度上限:姓名 128、电话 64、邮箱 254、工号 64、部门 128、备注 2000 个 Unicode 字符;邮箱空值合法,非空做基础格式校验。电话只 trim,不猜国家码/号码归一化;文本不强制唯一。拒绝控制字符和超限输入,不静默截断。搜索限制 128 字符,参数化查询,并转义 LIKE 通配符。 + +资料 patch 只允许六个字段,要求 expectedProfileVersion;读取/写入按稳定 userId。请求中的 owner 若用于兼容,必须与服务端 ID 映射一致。`isAdmin/enabled/roles/password/proof` 等字段不能混入 profile 或普通建号请求。 + +网页账号管理只创建无凭据的普通账号,可选资料全空成功;初始密码/管理员位参数明确拒绝,不能忽略后继续建号。新建记录持久标识 pending initialization,迁移中的既有未知账号不能推断为该状态。首管及新账号首凭据建立由可信本地 owner/bootstrap 边界完成,其来源持久为可信 owner 初始化;网页管理员不能通过旧 activate 接口补领凭据。 + +补充窄 CLI `user initialize-account `:由受信机器/DB owner 为尚无密码、无历史完成初始化、无其他既有凭据的新待初始化账号建立首 activation grant,要求 expectedStateVersion/初始化 generation,保持原 userId、owner、管理员位及业务关系。网页创建后的待初始化账号和本地新账号都走同一服务。Key 过期、交付失败或已兑换但尚未完成首次设密时,可信 owner 可以显式续办:在同账号事务中撤旧 activation grant/派生受限会话,推进 generation 后重新交付;不得重复建号,不改变已完成初始化账号的密码。完成首次设密的事务把状态改为 initialized,后续所有 initialize/reissue 都拒绝。 + +首管 bootstrap 的建号/首 activation grant 保持同事务;其过期续办也仅限上述已明确来源的 pending 状态,不借“用户库非空”重建首管。generic activate、reset 与该 initializer 明确分开,普通系统管理员、HTTP/Host/角色 grant 均不能调用 initializer。CLI 来源记录实际本地操作者及目标,不接受 --actor 自报成为授权;秘密仅交受信 owner 的保护交付通道。代码与隔离测试纳入 U3,真实机器/DB owner 身份核对、首次交付及续办运行仍须另行批准的操作计划,不用可选资料验证收件人。 + +## 4. 受保护系统动作与资料权限 + +U4 前保留一份 Backend 受保护账号管理适配器,仅识别 `system.users.view/create/edit/disable` 与 `credential.recover`,以当前有效的既有系统管理员资格为过渡来源。该适配器不是通用 Role/SystemGrant 新权威,不给项目 grant 赋予系统资料权限,也不把插件 `Host.IsAdmin()` 布尔当新目录授权回执。 + +本人可读/改六个非授权资料字段,须正常、有效完整密码会话;受限设密会话只走设密门。系统资料读取和搜索需 system.users.view,编辑需 edit,停用需 disable,恢复提案需 credential.recover。调用方、对象、资格及 expectedVersion 在写事务中重验;资格读取失败拒绝。 + +启用/停用与资料分别提交、分别版本化。停用事务锁定受保护管理者集合,保证至少一个有效且具备可用正常认证能力的系统管理者;PG 使用共同的锁顺序/管理者保护锁,SQLite 使用写事务,禁止“先 count 后独立 update”。并发停用最后两名管理者最多成功一个。 + +停用不物理删除账号或密码摘要,不改历史业务归属。事务内推进认证版本、撤销 API key、会话、激活/恢复 grant 与提案,并审计;启用后必须重新认证,已撤对象永不自动恢复。 + +## 5. IAM-D 提供者、授权与候选范围 + +新增可选 `AccountDirectoryProvider`,不向既有 Host 增加必实现方法;提供查询与精确候选查验两个入口。下列是冻结的语义形态,Go 的具体命名及签名可在同语义下按仓内规范落实: + +| 合同 | 输入/输出 | +|---|---| +| Query | schemaVersion、purpose、领域及 project/document locator、exact/fuzzy 模式、query、searchFields、状态过滤、cursor、limit;不接受 Actor、isAdmin 或 allowedFields 输入 | +| Page | userId、owner、valid、获准 name/staffNo;opaque directoryVersion、nextCursor。候选目录不提供全系统总数或 bulk export | +| Exact lookup | 在同一 purpose/locator 下指定稳定 userId(兼容 owner 必须显式标识并解析);只返回确定身份、valid 与当前版本,不扫描整个目录 | +| 专用注册口 | 可选目录授权 Registrar,由受信进程内 RDM 注册;重复/冲突注册拒绝。Backend 构造 actor/plugin/purpose/locator,回调解析真实项目/文档并返回当前资格、可见字段、可匹配字段及候选范围 | + +Backend 首先验证受信 dispatch actor 的有效态,再咨询专用领域回调。purpose 与 locator 只是待验证请求,不能授予资格。项目选人查当前 member.manage;职责候选查目标文档/项目当前 duty.assign,并从服务端得到当前项目成员集合。候选范围在匹配、排序、分页前施加,不能取全系统页再由 RDM 删掉非成员。 + +系统管理查询由 Backend 适配器决定。领域回调不能返回联系方式权限;系统资料授权不能绕过 RDM 项目资格。未知 purpose、字段、模式、状态、职位过滤,文档/项目不一致,actor 为空,提供者/回调缺失或报错,一律拒绝;不用 ResourceAuthorizer V1、旧宽松 EXT-19 或手填 owner 兜底。 + +| 使用面 | 默认可见/可匹配字段 | 范围 | +|---|---|---| +| 系统资料管理 | userId、owner、valid、六个获准资料字段 | system.users.view;资料查询独立于候选 DTO | +| 本人资料 | 本人六个字段 | 身份从认证上下文取得,不接受目标 owner 参数 | +| 项目成员候选 | userId、owner、name、valid | 当前 member.manage;enabled 账号;无私密字段 | +| 文档职责候选 | userId、owner、name、valid | 当前 duty.assign;当前项目成员且账号 enabled;现有职责规则再检查 | + +staffNo 仅在回调有明确受信字段授权时披露/匹配;本期项目默认不授予,不能因用户填了工号便自动开放。电话、邮箱、部门、备注仅在独立资料面出现。searchFields 必须是当前允许匹配/返回字段的子集;被禁止字段不能通过默认全文搜索暗中参加匹配。 + +精确 ID/owner 命中也要先授权。模糊候选必须让用户明确选一个稳定 ID,名称与 ID 不混用。候选查询无词时不枚举全系统,返回提示输入账号/姓名;模糊词至少两个字符,最大页 50,系统管理最大页 100。keyset 按稳定 owner、userId 排序;游标签名绑定 actor、purpose、真实领域范围、字段、模式、过滤、查询及授权/可见目录版本。串用或过期拒绝,要求重新查询,不悄悄换页。 + +EXT-19 保留现有 Owner/Name/Valid 隐私范围,Valid 改为真实状态。旧签名缺领域 locator,只允许明确系统目录管理上下文,或已有可信 dispatch 领域上下文经同一授权服务判定;无此上下文拒绝。U3 的 RDM 消费者要求新能力,不回落旧宽松行为。此为明确的授权收紧,登记兼容变化,禁止描述成旧行为完全不变。 + +RDM 成员/三权写入共享服务先检当前领域管理资格,再做精确账号查验;有效态错误必须传播。检查覆盖成员添加/任命、章节、整文、批量三权,选人查询不是提交授权。三权仍要求项目成员、相异人员、轮次/冻结条件;不因提供者确认账号有效而省略领域规则。 + +## 6. IAM-C:本人证明与恢复 + +### 6.1 最小可交付证明 + +首期只实现“提案之前已经存在的、具备可信凭据来源的完整密码会话”这一分支;不新增短信、邮件、SSO、恢复码或 WebAuthn。姓名/电话/邮箱/工号不参与证明建立、搜索或匹配。 + +具备资格须同时满足:目标本人、auth-on、确切 cookie/password 会话、full、非强制改密、未过期/撤销、当前 auth_version、enabled、可信 owner 初始化或可信本人恢复的凭据来源。API key、auth-off、restricted、混合/冲突 Bearer+cookie、客户端自报 actor/scope/proof、管理员密码/Key 派生链和 legacy_unknown 均拒绝。 + +提案事务在同账号锁内记录当时已存在的合格会话摘要引用,确认只能使用记录中的仍有效会话;不靠客户端时间,也不因提案后新登录得到资格。新登录/凭据变更遵守同一锁顺序,避免时间相等或并发插入绕过。 + +管理员给初始密码 → 自行强制改密 → 再密码登录的 full 会话仍继承不可信来源。主动改密不能提升证明资格,资料修改不能提升,管理员不能改变来源。历史密码保留正常登录,但来源默认 legacy_unknown;不得把“用户再次登录”解释为证明历史管理员从未掌握密码。若未来要信任某些历史账号,需另有用户批准的可信 owner 证明建立方案。 + +无合格旧会话时线上明确拒绝,不能签 Key 给管理员“代交”。丢失全部会话/凭据的异常本人恢复仍由另行批准的物理 owner 操作处理,U3 不提供新的旁路。 + +### 6.2 提案、确认、兑换与完成 + +| 路径/动作 | 合同 | +|---|---| +| POST `/api/admin/users/{userId}/recovery-proposals` | credential.recover、版本及幂等校验;仅提案 ID/目标/状态/期限/版本。不改目标密码、不删正常会话、不改变 must_change,不返回证明或 Key | +| GET/DELETE 同目标提案 | 管理者仅状态/撤销;路径目标与记录绑定;没有恢复秘密或会话摘要输出 | +| GET `/api/me/recovery-proposals`(含按 id 查状态) | 目标只能是当前本人;返回 pending/confirmed/completed/revoked/expired、版本/generation 及非秘密来源说明,不接受 owner 注入,不返回 Key/证明摘要 | +| POST `/api/auth/recovery-proposals/{id}/confirm` | 本人 cookie 证明、pending、expectedVersion/auth_version 在最终事务重验;一次 confirmed 并发 grant;仅该本人 HTTP 响应获得明文 | +| POST `/api/auth/recovery-proposals/{id}/rotate-grant` | 仅原合格本人证明,confirmed、expectedVersion/generation 与当前账号版本;原能力及派生会话同事务撤销后仅本人获得新明文;不重新确认、不给管理员补领 | +| POST `/api/auth/recovery-grants/redeem` | body 中凭据;摘要定位目标,无 URL/Bearer 形式;一次消费与建立 set_password_only 会话同事务 | +| POST `/api/auth/password`(grant 设密) | 复用既有入口;此分支须有确切可信 activation/proof-linked grant;最终事务再核状态/来源/版本/期限/撤销;成功更新密码、撤相关能力与全部旧会话、审计,返回 reloginRequired,清 cookie | +| 同入口(有密码的强制改密) | 仅服务端标记为 password-authenticated 的确切受限 cookie 会话;必须校验 current 密码、enabled、auth_version、must_change。在最终事务更新、撤旧会话并重新登录;继承原来源,不获得本人恢复证明资格;legacy Key 派生会话不得进入此分支 | +| 普通主动改密 | 保留 current 校验;继承凭据来源;若保留当前会话,事务内同步其 auth_version,其余会话撤销 | + +提案默认 24 小时;grant 5 分钟;受限会话 10 分钟。grant 256-bit CSPRNG、摘要存储,绑定 target/purpose/proposal generation/issued auth_version。响应 no-store,不写日志/审计/localStorage/剪贴板自动复制。确认或兑换不生成业务会话;设密后必须正式密码登录。 + +确认成功但响应丢失,不重放明文。页面进入“结果待核验”,读取本人提案当前非秘密状态;pending 才可再次显式确认,confirmed 显示“原能力无法读回”,仅仍具备原合格证明的本人可点“重新生成设密能力”,走 rotate-grant 后继续兑换。过期、撤销、完成或证明失效则说明当前状态并退出;不能自动循环签发或让管理员补领。幂等重复返回非秘密状态。若兑换响应丢失而 cookie 已成为合法受限会话,先经 `/api/me` 确认当前模式,再继续设密;若结果仍未知,查状态后由本人显式续办。 + +登录/`/api/me` 输出服务端确定的非秘密 `passwordChangeMode`:grant_setup 或 current_required;UI 据此在可信 activation/recovery 设密时省略 current、强制/普通改密时要求 current。客户端自报模式无效,未知模式拒绝。明确失败整体回滚;结果未知先查状态,不能盲目自动重试秘密签发。 + +PG 同账号凭据事务统一锁顺序 user → proposal → grant → session;最终 UPDATE 使用版本谓词,不能仅先 SELECT。SQLite 复用 immediate 写事务。提案/确认/撤销/补发/兑换/设密/主动改密/停用/正常会话建立共享版本栅栏;审计与写入同事务,故障无部分成功。 + +### 6.3 公共扩展与旧入口 + +可选 `RecoveryProposalProvider` 只公开提案、状态和撤销的 metadata DTO;actor 从 Host 绑定。插件不确认本人、不接收恢复 Key,不按 actor 字符串推导 cookie 证明。本人恢复 grant 的秘密交付只在 Backend 本人 HTTP 会话入口;若未来工具需要本人确认,须另有不可伪造证明 handle 合同。本期无必要消费者则不增加无人消费的 Host 写方法,只冻结 DTO 与 HTTP 合同。 + +旧 POST `/api/admin/users/{owner}/password`、POST `.../login-keys` 明确拒绝并给出新提案路径,不能返回成功兼容。建议稳定 410/`RECOVERY_CONTRACT_REPLACED`;目标不存在也不进行凭据写。旧 Key 的读取仅限无秘密 metadata;撤销必须校验 path owner 与 key 真属主。 + +generic CLI `user reset-password`、`issue-login-key --purpose recover/activate` 对既有账号拒绝;在打开/迁移 Store、撤 apiKey/session、生成密码之前校验,避免先产生副作用再拒绝。新账号可信 owner 初始化和无用户首管 bootstrap 按§3保留窄路径;本地创建同事务建号/首 activation grant,已登记新 pending 账号由 initializer 续办,不把它们扩展成已有凭据账号恢复。svc 包装器/帮助同步,不新增 --force 旁路;真实运行仍另行授权。 + +## 7. 账号停用、Host 与后台边界 + +SessionInfo 与 AuthOwner 必须读取当前 enabled;会话同时比较当前 auth_version。密码验证与最终创建会话在同账号栅栏内复验。权威状态读取错误拒绝,修正当前 password gate 读错误放行。 + +Host 各账号能力及保留的 dispatch Host 在调用时读当前状态,不能把构造时 actor 当永久 allow。run start/resume、工具执行、HITL 确认后、Backend 有后果写入和新输出前核验账号状态;停用通过既有取消机制停止本实例活动任务,其他实例在持久状态重验时停止。SSE 每个新帧/重放前复验,安静流继续周期复验,不能只等待 15 秒定时检查后继续输出旧数据。 + +U3 只承诺账号有效态和 Backend 自有凭据/写事务边界。RDM 在实际写入前再次做当前账号查验;Backend 与 RDM 独立数据库间,最后一次查验与 RDM commit 仍非同一事务,不能据此宣称零窗口的跨库提交 fencing。完整角色/成员/三权撤销、跨实例运行 fencing 和旧模型上下文整体重建是 U8。若验收要求 RDM 并发停用后绝不提交任何在途写入,则需将该 fencing 作为新增契约重审,不能扩大 U3 的通过声明。 + +## 8. 迁移、切换与回退 + +增量迁移保留账号 ID、owner、密码摘要、业务/角色/成员/历史职责。name 原值保留;新资料空、不从聊天或联系方式推断。enabled 对既有账号按已批准的既有有效态初始化,profile/state 版本设固定初值。旧来源均标 legacy_unknown;不给历史记录补造本人证明。 + +恢复切换必须同时处理未兑换 legacy Key、已兑换 legacy restricted session,以及既有 full session 的证明资格。无 proof-linked 来源的旧恢复/激活能力不能在新线上免 current 设密;不可仅禁止新签发。有既有密码但 must_change 的账号仍可通过密码认证及 current-required 分支完成强制改密,来源继续 legacy_unknown/原不可信来源。旧 full 会话业务策略与证明资格分开,正常业务认证继续遵守当前有效态与版本,恢复资格默认拒绝;保留历史密码不意味着能证明历史管理员从未掌握它。 + +新 schema 可读不代表旧认证协议可安全写。切换要求所有 HTTP、CLI、后台写者固定新版本;旧 writer 不得与新合同并行。迁移兼容登记/启动守卫须阻止旧程序静默重开恢复及忽略 disabled;若当前版本机制不足,列为实施停止条件并提出具体栅栏修订。回退只到保留新安全协议的修复版本,不能恢复旧越权接口/有效凭据。 + +本轮仅在新建隔离 SQLite/PG 副本演练迁移、重启和失败回滚;现有服务、真实账号及生产数据库均不触碰。生产切换另交备份/身份/writer 清单与恢复演练方案,且必须先具备获批的两份 owner 操作安排:新 pending 账号首次初始化/过期续办的身份及秘密交付手册,以及丢失全部凭据的历史账号异常恢复/证明建立手册。由 weichen 指定实际可信机器/DB owner 操作者、固定目标和接口并批准运行;两者缺一不得切换,不用临时数据库手术代替。本文不批准对 legacy 账号提升证明来源,也不提供异常重置旁路。 + +## 9. 实施顺序、负责人及文件范围 + +协调 owner 为根智能体;组件在各自权威 origin/main 建登记工作区,固定来源及组合,不编辑 pins。各项由新上下文实施者承担,根智能体验收实际 diff、记录证据及提交;共享 schema/公开合同先串行冻结,文件和语义不独立的工作不并行。 + +| 工作项 | 依赖与范围 | 完成条件 | +|---|---|---| +| U3-1 资料/状态/凭据来源模型 | Backend store、migrate、compat、account events;核对最新迁移编号 | 双档空值/CAS/重启;固定数据模型和安全兼容门槛,原身份不变 | +| U3-2 当前认证及停用 | U3-1;auth、accounts、sessions、Host、runmgr、SSE | 事务锁/CAS及最终认证检查;最后管理者;停用/启用不复活已撤能力;关键拒绝路径先写红测试 | +| U3-3 受控目录提供者 | U3-1/2;accounts service、hostcaps、extension、registry、surface/version | 授权/字段/范围先于查询,exact lookup、签名分页、EXT-19收口;真实 provider 测试 | +| U3-4 恢复与旧入口收口 | U3-1/2;store recovery、server HTTP、CLI/svc 帮助;新 pending initializer/续办与首管窄路径 | 管理员绕行全拒;可信本人正例完整链;待初始化稳定ID续办;历史强制改密current分支;旧能力拒绝和失败原子性;双连接竞争 | +| U3-5 RDM 最小消费 | U3-3;RDM public pins、注册回调、accountdir、members、chapter/doc/batch duty shared service | 缺能力拒;候选范围在提供者内;提交重验;保留 I1项目/三权规则;新提供者真实组合测试 | +| U3-6 既有界面及契约记录 | U3-3/4/5;Frontend api、UsersPanel、本人既有设置入口/恢复确认、RDM selector;Documents/Dev svc 合同 | 删除密钥复制/旧重置承诺,建号无密码/管理员位;本人资料与确认闭合;提示待 owner 初始化/无证明;未知结果查状态及本人显式更换;按服务端模式要求current;错误与过期响应不覆盖新状态 | +| U3-7 固定组合验证和阶段出口 | 全部;Backend/RDM/Frontend/Documents PR 与 Dev pins、任务卡 | SQLite/PG、组合/build、浏览器及独立 Review;用户阶段6/7评审,当前HEAD CI 后智能体合入;运行/部署状态另列 | + +U3-1 是共享前置;U3-2 冻结账号栅栏后目录/恢复才有稳定基础。目录与恢复如仍碰 auth/schema/extension 由根智能体串行整合;仅独立测试或文件在已提交合同后并行。Frontend UI 修改前重新按 DESIGN.md 核对,消费既有令牌、motion、Collapsible,不新增完整管理中心。 + +阶段 1–3 承接已批准统一方案的 U3 需求和本轮启动授权,不重走未受影响的用户意图。本文涉及公开契约、数据/证明及兼容切换细化,需阶段 4–5 审阅本版本后进入实施;阶段 6/7 保留人工出口,阶段 8/9 按仓规由智能体完成。不得用测试通过代签人工评审。 + +## 10. 验证计划与证据 + +所有下列测试当前 `NOT_RUN`。优先更新已有错误合同测试,观察期望红失败,再实现;不复制仅镜像实现的测试。测试对象为隔离临时数据,真实模型/线上账号不在本轮验证范围。 + +| 验证组 | 关键正常/拒绝/竞争场景 | 对象与方法 | +|---|---|---| +| 资料与身份 | 全空建号、填写清空、同名不同ID、格式/长度、未知/权限字段注入;同 expectedVersion 两写只有一胜者 | 真实 Store+HTTP,SQLite/PG 共用合同用例 | +| 目录与隐私 | self/system/member/duty权限矩阵;仅禁用电话命中的哨兵不改变结果/数量/游标;空词、限额、过期/跨范围cursor、未知过滤拒绝 | 真实 Backend provider;字段授权回调失败;序列化输出和错误无秘密 | +| 当前账号 | 密码/full/restricted/APIkey/旧Host/HITL/run/SSE;停用与建会话/兑换/最终写交错;启用不复活已撤对象;最后两管理者并发 | 两个独立 Store/连接;可控 barrier;双档,不靠 SQLite 单连接证明 PG | +| 恢复正例 | 网页建号→可信owner按原ID初始化,activation过期/交付失败/已兑换未设密可续办,首次完成后initializer拒绝;可信 owner 初始化→正式密码会话→管理员 metadata 提案→本人确认→一次受限兑换→设密→正式重新登录,身份和业务数据不变 | 真实HTTP、同源cookie;两HTTP实例共享隔离PG;无 mock 身份认证 | +| 恢复负例 | 初始密码/旧Key/reset 派生full、unknown lineage、APIkey/auth-off/提案后会话、错误owner、Bearer混合、管理员代确认均拒;资料不改proof | 实际旧/新HTTP链+CLI拒绝,确认前后数据库无不应发生的变更 | +| 原子与恢复 | confirm/revoke、redeem/reissue、completion/disable/activechange、登录insert/改密强制交错;100并发至多一有效能力;audit/commit失败回滚;响应丢失无管理员补领 | SQLite同文件双Store/PG同schema双连接;显式测试日志证实 PG 场景确实执行 | +| RDM 消费 | 项目管理先授权后lookup;不存在/disabled/provider缺失报错;章节/整文/批量三权拒失效候选;职责仍要求成员;正例I1隔离/cap保持 | 固定新Backend/RDM的实际注册提供者+命令/HTTP,不只 mock Host | +| UI 与兼容 | 管理员不见明文;全空资料建号;本人确认/无证明拒绝;确认/兑换响应丢失查状态、显式更换、失效退出;grant_setup/current_required正确显示;同名明确选择;旧接口迁移提示;桌面/移动无错位,design guard | 真实浏览器;surface snapshot/version/导出边界;不开放额外用户流程 | +| 迁移/组合 | 原ID保留;legacy Key restricted不能继续设密;既有密码强制态要求current且继承无proof来源;旧full无proof;disabled重启不复活;旧writer/不安全回退拒绝;组件固定pins+build | 隔离双档;Dev make test/build、test-pg及相关治理检查;当前HEADCI | + +本地与作者工具结果为 SELF;独立审阅者重放须记录身份、命令、精确对象才是 REPLAY;当前 HEAD CI 有运行链接/受信执行身份才是 GATE。命令、对象SHA、完整脱敏输出由 `make evidence` 写入,任务卡/PR引用,不手抄测试通过。设计源码观察不冒充运行 FAIL 或 PASS。 + +## 11. 备选、代价及停止条件 + +主要备选是直接补资料、沿用旧目录和一次性 Key 交付。成本更低,但字段搜索仍可探测、管理员仍可接管目标账号,不满足已批准 IAM-D/IAM-C。另一个备选是本期同时建设外部身份/邮件/恢复码及 U4 授权权威,能改善无会话恢复,但扩大基础设施及迁移边界,当前不采用。 + +推荐本方案:一个账号权威、受控提供者、来源可追溯的本人会话、默认拒绝旧恢复旁路。代价是新账号需可信 owner 初始化,历史未知来源/丢失全部会话者不能线上恢复;最小搜索兼容也收紧,完整管理体验仍需 U7。方案批准不等于批准任何真实账号初始化或迁移。 + +遇到以下情况暂停受影响实施并修订:无法可靠判断 credential lineage 或默认拒绝旧能力;旧 writer/回退守卫不能落实;提供者必须依赖请求自报资格;新契约要求通用角色迁移、不可伪造插件本人proof、跨库 fencing 或外部身份证明;双档结果不一致;新的公共合同/权限设计需要改变本文取舍。已授权且不受影响工作可继续,不静默扩大范围或降低验收。 + +## 12. 交付出口 + +评审出口是本 r1 的明确兼容取舍及 U3 实施/测试范围。实施后分别给出需求到证据映射、剩余 U4–U9 差距、组件PR/固定组合、阶段6/7候选和独立Review记录。合入按有效 Review+当前HEADCI执行;生产上线、历史owner证明建立与工作区回收保持待授权;上线前须批准并演练§8的首次初始化/续办及异常恢复两份owner操作安排。 + +原 r1 仍是总方向,本补充是 U3 的具体设计对象。完成后主记录只更新真实推进状态,下一步优先核对 U4 的职位权威与现有在途工作,再提出具体任务建议;不得将本轮启动授权扩展到 U4。 diff --git a/docs/proposals/INDEX.md b/docs/proposals/INDEX.md index 4043a58..02967d4 100644 --- a/docs/proposals/INDEX.md +++ b/docs/proposals/INDEX.md @@ -6,6 +6,10 @@ ## 先看哪一份 +- [IAM U3 账号资料、有效态、受控目录与本人恢复 r1](2026-10-14-iam-u3-account-contract-and-plan.md): + 2026-10-14 用户授权启动 U3;承接统一 IAM 方案及 IAM-D/IAM-C,具体提供者、凭据来源、旧入口切换及双档验证计划待阶段 4–5 人工评审。 + 本轮不改原批准正文;业务实施、验收、合入与部署均未完成。新账号可信 owner 初始化和历史未知来源的线上恢复限制须审阅本版本。 + - [IAM I1 权限边界与公开扩展合同 r1](2026-10-13-iam-i1-permission-contract.md): 依据已批准的[用户、职位角色与分层权限统一方案 r1](2026-10-13-identity-role-permission-plan.md) (Documents `a088809`)冻结系统/项目/资源边界、管理员无业务 bypass、创建者 cap、独立 `audit.read` 与公开扩展四要素。 From fb3a8214df963697ebd745ad11eb852ed9444fe3 Mon Sep 17 00:00:00 2001 From: weichen Date: Wed, 14 Oct 2026 05:51:37 +0000 Subject: [PATCH 2/3] Specify U3 stage-six review repairs Why: independent review found in-flight write, API-key admission, lock-order, and cross-instance cursor gaps in the U3 candidate. Evidence: SELF source review of Backend 4053c4d against approved U3 r1; proposal records triggers, scope, and replay plan. Impact: request stage-four and stage-five re-review before further affected implementation; no production action. Refs: IAM U3 r1; Dev policy/delivery.md --- ...26-10-14-iam-u3-stage6-review-amendment.md | 58 +++++++++++++++++++ docs/proposals/INDEX.md | 4 +- 2 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 docs/proposals/2026-10-14-iam-u3-stage6-review-amendment.md diff --git a/docs/proposals/2026-10-14-iam-u3-stage6-review-amendment.md b/docs/proposals/2026-10-14-iam-u3-stage6-review-amendment.md new file mode 100644 index 0000000..1ecaf06 --- /dev/null +++ b/docs/proposals/2026-10-14-iam-u3-stage6-review-amendment.md @@ -0,0 +1,58 @@ +# IAM U3 阶段 6 审阅后契约修订与补测计划 r1 + +状态:待阶段 4–5 人工重审;日期:2026-10-14。本文是已批准 [U3 r1](2026-10-14-iam-u3-account-contract-and-plan.md) 的增量修订,不替换其已批准正文。对象是 U3 实施候选在合并 Backend U7 账号事务后暴露的四项缺口;U4–U9、生产迁移、部署和真实账户操作不在本次授权申请内。 + +## 1. 当前证据与为什么要重审 + +U3 r1 的实施候选为 Backend `4053c4d038b1d13a356e0196db56ab11c0e4f8e1`、RDM 源码 `ad815febce522d029a74cd90217409efc9a81c35`、Frontend `a961b19ebb793d6c02a72002050d269700b1a5bd`。Backend 在此后有**未提交的审阅修复候选**;它们不是已批准或已验收版本。Dev 任务卡 `feature/iam-u3-account-directory-recovery` 保存上述固定版本的 SELF 收据,RDM 任务卡保存源码外 Go overlay 的 SELF 收据。正式 pin、独立重放、GATE、生产与部署均 `NOT_RUN`。 + +独立只读代码审阅发现以下可复现缺口;现有绿色单测不推翻这些代码路径事实: + +| ID | 触发与证据位置 | 对 U3 r1 的影响 | +|---|---|---| +| A1 在途写入 | `internal/server/server.go` 的 `createProject` 在读取请求体后调用 `Store.CreateProject`;`internal/store/projects.go` 插入时不读取当前账号。请求通过认证后暂停,另一连接停用账号,再继续请求,项目仍可在停用提交之后写入。其它非 GET 业务入口须同样盘点,不能只修这一条。 | 违反 r1 §7 Backend 有后果写入前检查有效态;同库可闭合的窗口尚未闭合。 | +| A2 API key 隐式建号 | `internal/app/app.go` 的 `CreateAPIKey` 调用 `Store.CreateAPIKey`;后者 `EnsureUser` 可创建启用、来源为 legacy 的账号,再签发完整 API key。 | 绕过 r1 §3 待初始化账号及首凭据边界;纯集成账号的历史读取兼容与新增建号须分开。 | +| A3 锁顺序 | 合并 U7 后普通建号走 account 锁再取 bootstrap 锁;首管及本地建号曾反向取得两锁,同 owner 并发有 PG 死锁风险。 | 影响首管与并发建号的活性及 U7 的准入保证。 | +| A4 游标与可见版本 | `internal/app/account_directory.go` 和 `internal/server/account_search.go` 以进程内随机密钥签游标,负载均衡切到另一实例时下一页 409。受限候选的版本又组合了全局 public revision,域外账号改名也会使该域游标过期。 | 违反 r1 §5 多实例分页及“目录版本只受可见辨识资料/有效态变化影响”的范围;后一问题还可成为域外活动侧信道。 | + +本次提出的处理改变持久数据设计(共享游标签名密钥)和账号入口兼容行为(阻断 API key 隐式建号),并需要把原计划的“写前复验”细化为同库提交围栏。按 Dev `policy/delivery.md` 的“技术路线、模块职责、公开契约、数据或权限设计……修订方案或计划,经人工重审后继续”,阶段 6 评审暂缓,先审本文阶段 4–5 修订。 + +## 2. 修订后的契约 + +### A1:同库账号写入围栏 + +Backend 自有数据库内,每个以账号身份执行的有后果业务写入,必须在**最终写事务**内按统一顺序取得该 actor 的账号锁,读取当前 `enabled` 并在同事务提交业务变更。若目标资源另有 owner,仍由领域授权决定可写范围;账号锁不能授予项目或文档权限。停用事务取得相同账号锁并推进有效态,因此并发结果只能是业务写入先提交、停用后提交,或停用先提交、业务写入拒绝;不得出现停用提交后旧请求再写入成功。 + +实现采用显式 Store 事务入口和逐路由终点映射,不在请求全程持有额外数据库连接,也不以“handler 刚检查过”代替事务内复验。先盘点 Backend 当前 `66` 条非 GET API 路由,将凭据/资料写入(已有或补充最终事务复验)、Backend 自有业务写入、外部副作用/运行控制、只读或无业务后果动作分别记录。项目、会话消息、工件、设置、任务、工具/命令等适用的 Backend 自有写入改由同一账号锁和事务检查;文件或外部工具在真正执行前重验并使用既有取消机制,不能宣称跨外部系统原子性。`auth-off` 的本地默认 owner 继续按 r1 的嵌入式隐式身份规则处理;若存在明确停用记录则拒绝。独立 RDM 数据库的最终提交窗口沿用 r1 §7 已声明限制,不在 U3 声称零窗口跨库 fencing。 + +变更须给出“路由 → 最终 sink → actor/owner 来源 → 围栏方式 → 重放用例”清单;有未覆盖的有后果入口即阻止阶段 6 完成。对同一数据库中的认证版本、会话/API key 在途撤销窗口另作影响分析;若需要扩大本次安全承诺,先再修订本文,不用账号 enabled 检查冒充凭据撤销栅栏。 + +### A2:API key 不再建号 + +本地 `apikey create` 只对**已存在、当前启用、已完成初始化**的账号签发;迁移而来的 `legacy` 账号保留既有无密码集成 Key 兼容,但其 Key 不得作为 U3 本人恢复证明。待初始化账号、停用账号及不存在的 owner 拒绝,不执行 `EnsureUser`、不新增账号或 Key;现有 Key 不因本修订自动撤销。CLI 由当前机器/DB owner 边界执行,不能通过普通管理员 HTTP 或角色 grant 自助补领。未来新增纯集成账号须单列身份/授权方案,不能借 `apikey create` 的历史便利隐式建号。 + +Store 的旧 `CreateAPIKey` 目前被许多内部测试当建号 fixture 使用。生产调用入口先收紧;实施时应把内部 fixture 与正式签发 API 分离,并扫描所有非测试调用者,避免新调用重新打开隐式建号旁路。CLI 对不存在 owner 的返回由“创建并签发”改为明确拒绝,这是有意兼容收紧。 + +### A3:统一锁序 + +PG 账号事务统一按账号 owner 升序取得账号 advisory lock,再取得首次建号/末位管理员保护所需 bootstrap lock,然后锁用户行及后续 proposal、grant、session。SQLite 保持同等业务检查,依靠写事务串行。普通建号与首管竞争仍须保留 U7 的“空库同时入场仅一方成功”语义;不能通过改锁顺序让两方都成功。多 owner 操作按稳定排序,事务内重查 ID、版本和管理员资格。不得用死锁重试掩盖顺序倒置。 + +### A4:共享、分域且可见的游标 + +使用 256-bit CSPRNG 密钥,经共享账号数据库的 compare-and-set 在首次需要时原子持久化;保存密钥版本,绝不输出到 API、日志、任务卡或浏览器。管理搜索与领域目录以不同 HMAC 域签名,游标绑定密钥版本及 r1 原定的 actor、purpose、locator、范围、匹配字段、查询、过滤、可见目录版本。多实例从同一数据库读取同一密钥;密钥读取失败拒绝分页,旧进程本地随机游标切换时返回明确冲突并要求重新查询,不静默换页。 + +`AllCandidates` 的公共辨识变化可以使用全局公开 revision;有限候选范围的版本应由**当前可见候选集合**内的稳定 ID、有效态与获准辨识字段及领域 `ScopeVersion` 形成,不加入域外账号或未授权字段。快照读取与游标版本计算需在一致的数据库视图内完成,或在返回前复核版本并拒绝过期结果,避免版本与页面来自不同时间点。资料私密字段编辑不改变不获准字段的目录版本;当前允许披露的 `staffNo` 独立纳入版本。候选名单变化须失效旧游标。 + +若权威数据库已经实际应用 U3 v32,不能就地修改其迁移正文,必须追加新的迁移并给出旧游标废止步骤;只有证明 v32 尚未进入任何持久实例,才可在未发布候选内修订 v32。U7 `account_audit` 占 v31,U3 账号基础/恢复仍按已合并候选的 v32/v33 排序。 + +## 3. 实施与验证顺序 + +1. 固定当前主干与候选 SHA,盘点 66 条非 GET 路由、Host/runmgr 出口及账号写入者;给每条有后果 sink 指定最终有效态围栏和测试。完成 A2 生产入口及内部 fixture 分离,补“不存在/pending/disabled 均不建号或签 Key”双档测试。 +2. 按 A3 统一 PG 锁序。用两个独立连接/进程和可控屏障覆盖首管对普通建号、同 owner 建号、管理员停用对初始化/改密;验证无死锁、唯一成功与事务审计。 +3. 落 A4 密钥持久化、域分隔和范围内版本;补 SQLite 两 Store、PG 两实例跨进程翻页、字段/范围变动、域外改名、密钥不可见和故障拒绝测试;确认迁移版本前置事实。 +4. 按 A1 路由清单逐类实现最终写围栏。用暂停请求体和暂停最终 SQL 写入的确定性测试,在 SQLite、PG 双连接与 PG 两进程验证“停用先提交则无晚到写”;覆盖项目、消息、工件、设置等代表性入口及实际 RDM 限制说明。外部副作用单独验证最后检查与取消。 +5. 同步最新 main,重跑 Backend 全包、PG Store/Server、RDM 源码外组合与 Frontend 正常测试;对受影响真实浏览器路径重放。三仓正式 pin、GATE、独立 Review、阶段 7 验收仍遵守原计划的后续人工节点和受信发布前置。 + +## 4. 评审决定 + +请只裁定是否批准本修订的阶段 4–5,使 U3 阶段内可继续上述修复和隔离验证,然后**重新提交阶段 6 人工评审**。不请求现阶段批准阶段 6、阶段 7、PR 合入、受信版本 tag、真实机器/数据库账号操作、生产迁移或部署。若 A1 的全路由写围栏成本或 A2 的 CLI 兼容收紧不可接受,应在此处明确缩小验收承诺或另立增量;不得保留原 U3 §7 的完成声明同时留下已知晚到写。 diff --git a/docs/proposals/INDEX.md b/docs/proposals/INDEX.md index 02967d4..3212356 100644 --- a/docs/proposals/INDEX.md +++ b/docs/proposals/INDEX.md @@ -7,8 +7,8 @@ ## 先看哪一份 - [IAM U3 账号资料、有效态、受控目录与本人恢复 r1](2026-10-14-iam-u3-account-contract-and-plan.md): - 2026-10-14 用户授权启动 U3;承接统一 IAM 方案及 IAM-D/IAM-C,具体提供者、凭据来源、旧入口切换及双档验证计划待阶段 4–5 人工评审。 - 本轮不改原批准正文;业务实施、验收、合入与部署均未完成。新账号可信 owner 初始化和历史未知来源的线上恢复限制须审阅本版本。 + 2026-10-14 用户批准 U3 r1 阶段 4–5;承接统一 IAM 方案及 IAM-D/IAM-C。本轮不改已批准正文。 + 实施候选经独立审阅发现写入围栏、API key 隐式建号、锁序和多实例游标缺口;[阶段 6 审阅后修订 r1](2026-10-14-iam-u3-stage6-review-amendment.md) 待受影响的阶段 4–5 人工重审。阶段 6/7 验收、合入与部署均未完成。 - [IAM I1 权限边界与公开扩展合同 r1](2026-10-13-iam-i1-permission-contract.md): 依据已批准的[用户、职位角色与分层权限统一方案 r1](2026-10-13-identity-role-permission-plan.md) From 5783940e4c434102cbca8c22bd282d87b28fa840 Mon Sep 17 00:00:00 2001 From: weichen Date: Wed, 14 Oct 2026 07:33:19 +0000 Subject: [PATCH 3/3] Define U3 delegated projection actor fence Why: Delegated RDM renames currently lose the real operator before the Backend project write, leaving an account-disable race in the Backend database. Evidence: Read-only source review identified both HTTP lifecycle and command rename paths; the proposed contract and validation plan are pending human stage 4-5 review. Impact: Adds a narrow request-bound projection capability proposal and two-account final transaction plan. No product code, production state, or approval status changes. Refs: Approved U3 r1 and A1-A4 amendment; Dev feature/iam-u3-account-directory-recovery task card. --- ...u3-delegated-projection-actor-amendment.md | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 docs/proposals/2026-10-14-iam-u3-delegated-projection-actor-amendment.md diff --git a/docs/proposals/2026-10-14-iam-u3-delegated-projection-actor-amendment.md b/docs/proposals/2026-10-14-iam-u3-delegated-projection-actor-amendment.md new file mode 100644 index 0000000..d994cfb --- /dev/null +++ b/docs/proposals/2026-10-14-iam-u3-delegated-projection-actor-amendment.md @@ -0,0 +1,40 @@ +# IAM U3 委任项目改名真实操作者围栏修订与实施计划 r1 + +状态:待阶段 4–5 人工重审;日期:2026-10-14。本文是已批准 U3 r1 及其 [A1–A4 增量修订](2026-10-14-iam-u3-stage6-review-amendment.md) 的**单点补充**。仅涉及 I1 已交付的委任项目改名投影在 U3 账号停用围栏下的真实操作者传递;不改变 RDM 项目职责规则,不启动 U4–U9,不授权 PR、合入、生产迁移、部署或真实账号操作。 + +## 1. 问题、对象与边界 + +独立只读复核在 Backend `4053c4d038b1d13a356e0196db56ab11c0e4f8e1` 后的 U3 未提交修复候选中发现:RDM `internal/rdmapp/system_projects.go` 已对请求主体 Bob 检查当前 `project.settings`,但委任 Bob 改创建者 Alice 的项目时,`internal/rdmapp/host_projection.go` 调用 `extension.NewScopedHost(Alice, "rdm")` 重建创建者 Host;Backend `internal/app/project_lifecycle.go` 的 `RenameOwnedProject` 将 Host owner Alice 同时作为最终 Store 事务 actor。若 Bob 通过 RDM 的最后授权检查后被停用,Backend 的 `projects` 表仍可在停用提交后由 Alice 身份完成改名。RDM 与 Backend 之间的授权快照窗口依旧不可原子关闭;**此次可关闭的是 Backend 同一账号数据库内 Bob 有效态与项目投影写入的窗口**,不能把后者归入已批准的跨库限制。 + +需求不变:项目创建者决定项目权限;获其委任且当前具有 `project.settings` 的成员可改名;系统管理员本身不因此获得项目内容或改名权。委任操作者停用后,已在途的物理投影不得在 Backend 最终事务中成功;创建者停用亦不应由旧 Host 句柄继续投影。项目 ID、创建者 owner 和委任 actor 是三个不同字段,不能互相代用。I1 的持久失败/恢复记录仍是跨库失败出口,不能假定两库提交原子性。 + +## 2. 推荐契约与备选 + +推荐新增**由当前受信请求 Host 派生**的可选项目投影能力,例如 `extension.DelegatedProjectProjectionHost.ProjectProjectionForCreator(creator, projectID) (ProjectHost, error)`。RDM 的 HTTP 生命周期回调和 `rdm.project.rename` 命令处理器都把当前认证主体绑定到各自传入的 `requestHost`;两条入口都必须从**该次收到的 Host**派生,不能另造操作者身份。派生句柄继承操作者 Bob、仅把资源 owner 收窄为 Alice。方法不接收调用方自填的 `operator`,也不复用按任意 owner 重建的全局 `NewScopedHost` 作为委任写入口。普通 `NewScopedHost` 继续服务既有读取/恢复能力,但不得替代新委任写入口。能力只向已注册的 RDM 项目生命周期/命令提供者开放;缺少能力、绑定不一致、项目不存在或插件来源不符时拒绝,不回退为创建者身份执行。 + +Backend 派生时核对原 Host 的操作者、插件来源、目标项目 ID 与持久创建者;RDM 仍按现有顺序检查 Bob 的当前 `project.settings`、项目 HostBinding 和改名意图。`RenameOwnedProject` 将**真实 actor Bob**、**创建者 owner Alice**、目标 ID 与名称分开送入 Store。最终 Store 事务按账号 owner 稳定排序锁定 Bob 与 Alice 并读取两者有效态,再取项目名称锁、读取持久项目 owner、校验名称并更新同一 `projects` 行;创建者本人改名时只锁一次。停用先提交则拒绝改名,改名先提交则停用等待;账号锁不授予 Bob 项目权限,RDM `project.settings` 检查继续独立存在。`auth-off` 缺失本地默认 owner 的例外只对该 owner 生效,明确停用记录拒绝。 + +未采用的方案:在全局 `NewScopedHost` 增加可自填 actor 参数,无法从受信请求句柄证明该 actor 是本次操作者;只在 RDM 最后授权后再查一次账号,仍有检查到 Backend SQL 之间的同库晚到窗口;把委任投影从 A1 承诺中豁免,则保留已知的 Backend 同库晚到写,与获批的 A1 契约不符。 + +## 3. 数据流、失败恢复与兼容 + +1. HTTP 认证主体 Bob → Backend 生命周期回调或命令分发的 requestHost(Bob)→ RDM 当前 `project.settings` 判定与 HostBinding/意图校验。 +2. RDM 从**同一个** requestHost 派生 Alice 项目的投影能力;能力内部固定 `actor=Bob, owner=Alice, projectID=既定项目`,RDM 不提交自选 actor。 +3. Backend Store 在最终事务中锁定并复核两个账号、项目 owner 与名称后提交物理改名;RDM 随后按 I1 原流程完成自己的数据库投影和意图记录。 +4. 派生能力在改名意图建立前不可用时直接拒绝,RDM 数据保持原状;意图建立后的账号停用、权限撤回、超时或 Backend 写入失败按 I1 规则标记失败并保留恢复信息。重试需新的受信请求 Host 和原意图 actor,重新检查当前权限/账号;不可把旧创建者 ScopedHost 当作跳过 Bob 的恢复捷径。 + +这个修订增加一个可选公开 Go 能力,不改 HTTP 请求/响应、迁移表或现有 `ProjectHost` 方法集合;RDM 的测试替身需要显式实现派生能力。若正式 Backend 尚未发布该能力,RDM 委任改名须返回能力不可用,不能默默回退。Frontend 无需新控件;已有项目改名错误提示可显示拒绝。两个数据库之间以及 RDM 权限撤回后的瞬时窗口仍不能声称零窗口,按 I1 持久意图和人工核对规则处理。 + +## 4. 实施计划与可判定验证 + +1. 固定 Backend、RDM 和 Documents 候选 SHA;在 Backend `pkg/extension` 定义可选受信派生接口,在 `internal/app` 实现请求绑定、来源/项目核对。逐一追踪 HTTP 生命周期与 `rdm.project.rename` 命令两条入口;旧全局 ScopedHost 的委任写用法在两条改名路径全部替换,其它读取调用保持原状。 +2. Backend Store 增加两账号稳定排序的最终事务围栏,仅在项目投影入口使用;检查 Bob 与 Alice 有效态、持久项目 owner、全局名称唯一性和 rows affected。保持单账号项目创建路径与 U7 bootstrap 锁序;不得在 RDM 回调或模型/网络调用期间持有 Backend DB 事务。 +3. RDM 改名与失败重试从当前回调 Host 派生投影句柄,继续执行现有 `project.settings` 和 HostBinding/意图检查。缺少新能力时明确失败;已建立的意图按原规则保留,未建立意图的请求不凭空新增。补创建者本人、委任成员、停用创建者、错误绑定和权限撤回场景。 +4. 用可控屏障在 Backend 最终投影 SQL 前暂停,分别在隔离 SQLite 双 Store、PG 双连接/两进程停用 Bob,再继续,断言 Backend 项目名不变、无创建者身份回退;反向顺序验证先改名后停用。并发 Alice/Bob 锁顺序测试不得死锁。RDM 组合测试同时覆盖无会话 `rdm.project.rename` 命令和 HTTP PATCH:委任成员仅有 `project.settings`、无 `doc.read` 时均可改名;两入口的停用围栏、授权撤回与失败意图恢复均成立,不宣称跨库原子性。 +5. 重跑 Backend 全包/构建、隔离 PG、RDM 源码外 Go overlay、相关 Frontend 构建与浏览器路径;固定 SHA、命令和完整脱敏输出进入 Dev 任务卡 `SELF` 收据。随后重新提交阶段 6 人工评审;正式 pin、阶段 7、独立 Review/GATE、PR 与合入按既有前置分别处理。 + +停止条件:不能从受信请求 Host 得到真实操作者、测试发现 RDM 的回调来源并非可信身份、两账号锁序与 U7 冲突、或必须更改现有 `ProjectHost` 公开方法/持久数据设计时,先修订本文并重审,不以创建者代替 actor 继续交付。 + +## 5. 本次评审请求 + +请仅裁定本文件的阶段 4–5 契约与计划。批准后才实施上述新增接口与委任投影修复,并重跑受影响验证,再提交阶段 6 人工评审。本次不请求阶段 6 结论、阶段 7 验收、PR/合入、受信 tag、生产迁移或部署。