Why: old r2 and r3 draft-stage paragraphs contradicted the retained approval records and current user takeover authorization. Evidence: independent Documents review identified the ambiguity; targeted diff and link checks are captured outside the candidate. Impact: label historical planning limits without changing genuine NOT_RUN results, deployment boundaries, or the newer login recovery implementation record. Refs: Documents PR #89; Dev PR #489; independent review /tmp/mq-trial489-docs-independent-review-20261014.json
30 KiB
title, type, date, artifact_contract, execution, product_contract_source
| title | type | date | artifact_contract | execution | product_contract_source |
|---|---|---|---|---|---|
| svc 统一部署与一次性登录 Key 方案 | feature | 2026-10-12 | ce-unified-plan/v1 | code | ce-plan-bootstrap |
svc 统一部署与一次性登录 Key 方案 r3
确认状态:r1 原批准保留;r2 新增(R13–R15、KTD11–KTD14、U7)的 2026-10-12 批准记录保留;r3 登录恢复实施记录待人工评审。r2 的批准来自原状态任务 Documents #89(来源提交 b8df6f3c2a3180b06c61d2303bbfea9a3914f186),不由 #87 的合入推定。2026-10-14 用户已明确授权智能体接管 Dev #489/#488/#486,继续验证、试行、合入及清理;本轮全部合入由智能体执行,不扩展到部署、共享数据库或其他未纳入任务。
U7 进展(历史记录,2026-10-12,未合入):Backend #185 与 Dev #486 的旧候选曾记录 CI success,来源为旧任务记录,不能放行本轮候选。原 U7/U8 候选都占用迁移 28;本轮须同步后按当前主干已用版本顺延,保留主干已有迁移与账户保护。账户 U5 的登录恢复接线见 r3 与 §11;账户 U8 消费审计按其独立范围推进,不由 U7 批准代签。
修订记录:r1=2026-10-12 草案经源码复核后定稿(四项取舍采纳;补齐 §6 合同;对齐恢复方案 KTD 与已合入的 recovery U1 实例身份);同日获批并进入推送与 U1/U4 实施。
r2=同日复核 dev 登录故障、当前 U1/U4 源码和帮助缺口;保留 R1–R12、KTD1–KTD10、U1–U6 的含义与编号,补充 R13–R15、KTD11–KTD14、U7/U8,并细化 U5。保留原 r2 批准记录,不以该批准代签其他新增范围。
r3=2026-10-13 用户提交《登录流程方案》并指示「按此方案实施」——该指示即登录恢复范围的实施批准;当日实施合入(Backend #187、Frontend #76、Dev #504),合同定稿于 Dev docs/svc-deploy/2026-10-13-login-recovery-contract.md,实施记录见 §11。r3 为实施记录与三裁定回写,不改动 R/KTD/U 编号语义。
需求来源:2026-10-12 用户提出将 svc.sh 增强为统一部署入口(二进制与源码、环境准备、无初始密码开户与账户恢复),并要求一次性 API Key 只能兑换「设置密码」受限会话。负责人:weichen;编制:Cursor Agent。
主记录:本文;任务卡:Dev .task/feature-svc-unified-deploy-login-key.md。源码路径以 Dev 根目录为基准。
1. 目标与成功边界
目标:在干净或既有机器上,用同一套 svc 入口完成二进制或源码部署、按档准备依赖,并通过后端账户能力完成无初始密码开户与账户恢复;一次性登录 Key 只能兑换设密受限会话,完成设密前不能访问业务数据。
必须区分三类能力:
| 能力 | 结果 | 非目标 |
|---|---|---|
| 统一安装 / 升级 | 指定版本或提交物化为可切换候选;切换后实例身份、DB、数据根与账户不变 | 不接管共享 PostgreSQL;不因发现端口而认领外部服务 |
| 环境检测与准备 | doctor 只报告;env prepare 按模式执行可幂等计划 |
不把任意发行版 Go/Node 标为可构建;工具链版本读自所选提交或发布清单 |
| 账户激活 / 恢复 | 无预设密码开户或签发恢复 Key;原子兑换 → 受限会话 → 设密 → 正常登录 | 不删除重建用户;不迁移业务归属;普通找回默认不撤销长期集成 Key |
与 服务重启与全量数据恢复方案 的关系:
- 复用 KTD1–5、KTD12:持久 manifest、唯一监管者、team=PG 且失败不回落 SQLite、readiness 与身份。
- 复用 已合入的 recovery U1(Dev #415):
runtime/<name>/manifest.json、启动身份校验、svc init、原子写路径(见 Devdocs/recovery/u1-identity-manifest.md)。 - 本方案新增:部署来源统一(binary/source)、doctor/env prepare、账户生命周期与一次性登录 Key、安装入口与双轨收敛。
- 命名区分:账户恢复 Key ≠ 服务数据恢复。
2. 当前事实及重点差距
本轮 source 基线(SELF):权威 Dev 2723e452440fc11022c0f6d916cd34cc4cd17335;文档任务同步后 HEAD 26f2045e33516207959b5983acd32e1d3ea66bca(增加任务元数据);Backend 5e8ada727c358438f8ff9f94eabb697e99fe28dc;Frontend cc233fec908d0f8c12193042942b94ad68508091;Dev 固定 Documents 8d0810bd35f4934e0ad37fb96d7bab6d7a732473。本轮 Documents 草案从其独立权威 main 1048e7c6afc2bb4efc541e68575c3fb2669ee332 起步;与 Dev 固定版相比,两份目标方案正文没有上游差异。Dev gitlink 未推进。原 r1 基线只作历史,不用于当前能力判断。
2026-10-12 17:37:03(Asia/Shanghai)的只读运行观察和启动/同步命令,原始脱敏输出由 Dev make evidence 写入 .task/feature-svc-account-durability-plan.md。这是 SELF 观察,不是登录、故障或恢复验收,后者均 NOT_RUN。
最终同步基线为 Dev d6d46fde6d903d0d8e37440d830fb5183d6ef5e9:主干另已推进固定 Documents 至 1048e7c6afc2bb4efc541e68575c3fb2669ee332,并修复 svc 批量操作的 PIDCHECK/EXEC 等内存态串用;本轮未重复该修复,也未推进本轮 Documents 草案 gitlink。Backend/Frontend 固定 SHA 未变;上述初始基线和17:37观察保留历史时点,不冒充最终 HEAD。
| 对象 | 已存在的基础 | 尚需补齐 |
|---|---|---|
| 部署来源 | deploy --from local|dev|release|rdm;dev/rdm 独立缓存物化;release 读本地 Releases 仓并校验 sha256 |
远程下载/离线导入;统一 install;release@ver 钉扎(现 sort -V|tail -1 取最大版本,见 scripts/svc.sh resolve_release) |
| 部署生命周期 | 预检失败不停机;预检通过后 先停再构建;无程序备份/自动回滚 | 候选获取与校验全程不停旧服务;缺摘要硬失败;迁移后回退独立判断 |
| 实例身份 | recovery U1:manifest + marker + init;svc.env 派生 |
账户 CLI 必须绑定实例最终配置;缺绑定拒绝,禁止回落 ./data |
| 环境 | 端口/flock/pg 工具链按需检查;文档列前提 |
doctor / env prepare;无 flock 时不得放行冲突操作(现状直通) |
| 账户 CLI | Backend 已有 create/reset-password/list/issue-login-key/revoke-login-key/bootstrap-admin;svc 已接 user 且附加 OpenExisting |
顶层/专题帮助未列账户入口;OpenExisting 仍 migrate;账户路径未完整验证 DB identity;参数回显及 argv 凭据需整改 |
| 登录凭据 | 长期 api_keys 与独立 login_keys 已分离;一次性 Key 原子兑换为短期受限会话 |
原子开户+签发、统一密码变更及审计仍有缺口;不能把基础实现等同于整体验收 |
| 强制改密 | Backend 已有受限会话和 must-change 中间件,强制设密成功销毁会话并要求重新登录 | Frontend 仍只有密码/长期 Key 两个入口,未接兑换及 sessionScope/reloginRequired;设密后直接 onDone 与后端不兼容 |
| 安装双轨 | Dev svc deploy 多实例;Releases install.sh 系统 systemd |
统一部署计划;install.sh 薄封装或明确遗留 |
命题裁定(源码复核):
- 长期 API Key 是 show-once plaintext,不是 single-use;一次性登录 Key 是独立类型,当前代码已区分。
- r1 所记「无统一改密门」已被 U4 源码覆盖,不能继续作为待建能力;普通改密/管理员重置仍缺统一事务及旧恢复 Key 作废,见
modules/backend/internal/server/accounts.go:208、modules/backend/internal/store/auth.go:244。 scripts/svc.sh:1503已实现 user 转发,但帮助未列;modules/backend/internal/app/app.go:743的账户插入和 Key 签发分别提交,不能采信注释中的「同事务」。
2.1 dev 8788 事件与归因边界
用户明确故障对象为 dev(127.0.0.1:8788)。17:37 的实际进程打开 runtime/dev/data/agent-system.db,数据库 marker 为 dev 的 SQLite 身份;该现场没有使用 PostgreSQL,不能沿用旧报告的 pg1/agent 描述。
| 只读对象(北京时间) | 账户身份 | 最后设密时间 | 与 live 摘要比较 |
|---|---|---|---|
| 12:35:03 快照 | 与 live 用户 ID 相同 | 09:52:11 | 不同 |
| 14:50:37 快照 | 与 live 用户 ID 相同 | 09:52:11 | 不同 |
| 17:37 的 live | 同一账户,must-change=0 | 16:03:20 | 当前值 |
比较仅输出时间、状态及相等性,不输出摘要或凭据。它证明字段重写,不证明明文改变;随机盐使同一明文的重新散列也可能不同。未发现正常启动/更新/迁移 up/设置播种自动覆盖既有密码的路径;现有 audit_events 只有 Surface 事件,无法归因此次写入。另有已消费恢复 Key,但其消费时间不能证明之后哪次设密由它触发。
前一轮观察到 dev 与候选 rdmcand 指向同一数据根;17:37 候选处于停止,因此历史共享不写成该时点双进程事实。18:14:09 续查时,外部流程已将候选重新启动于独立的 runtime/rdmcand/data/agent-system.db,实际文件 inode 与 dev 不同,登记和制品版本相符;dev 最后设密时间仍为16:03:20。这缓解了当前共享现场,不能代替 Backend 防误用锁或完整验收。历史共享说明跨实例影响范围,不证明某次更新主动改密;本轮未执行这些现场变更。
本轮不进行真实账户登录尝试或再次重置来验证旧密码;故障重放仅在脱敏、独立数据副本上执行。首批完成还需另行授权的 dev 原账户正常登录验收,见 U5/U8 合同;该验收不在 live 进行 kill、并发或故障注入。历史操作者归因仍未证实,新增审计不能反向补造旧事件。
3. 需求合同
| ID | 范围 | 要求与可判定验收 |
|---|---|---|
| R1 | 安装入口 | doctor / install / 兼容 --from 生成同一部署计划;二进制无需 Go/Node;源码固定提交且子模块组合可核验 |
| R2 | 实例身份 | 启停、升级、开户、签发 Key 命中同一 store 与 data 根;缺失/不符拒绝;与恢复案 R2/manifest 一致 |
| R3 | 切换安全 | 构建/下载/摘要失败时旧服务继续;程序+插件+前端作为版本组合切换;固定版本不得被「最大版本」替代 |
| R4 | 存储档 | team 默认 PG+auth;existing / managed-local / local(SQLite) 显式选择;PG 失败不回落 SQLite |
| R5 | 开户 | 无初始密码;角色明确;创建与激活 Key 同事务;同名冲突不覆盖 |
| R6 | 账户恢复 | 精确命中既有账户;不改密码/不吊销会话/不改业务数据于签发时;保持 owner/角色/成员关系 |
| R7 | 一次性 Key | ≥256bit、只存摘要、activate|recover、默认 TTL 30min、状态机、原子兑换、专用 POST body |
| R8 | 受限会话 | 仅身份查询、设密、退出;业务/下载/SSE/admin/插件拒绝;普通 must_change 同样服务端限制 |
| R9 | 设密事务 | 写 hash、清待激活/待改密、作废未用恢复凭据、失效相关会话同事务;成功后销毁受限会话并要求密码重新登录 |
| R10 | 集成 Key | 普通找回默认保留长期 API Key;失陷恢复显式 --revoke-all-credentials |
| R11 | 首次管理员 | 仅核验为新实例(无用户且无历史业务标记);事务防并发;不得因「未发现管理员」接管有数据环境 |
| R12 | 证据 | 验收绑定固定源码/制品摘要/环境;SELF/REPLAY/GATE 分源 |
| R13 | 可发现与兼容 | 完整/短帮助、help user、实例 user --help 和文档列出实际账户能力;不支持的制品在调用前拒绝;帮助/探测不建库、不迁移、不启动服务 |
| R14 | 账户稳定与审计 | 普通启停/更新不隐式重置;密码变更、恢复凭据/会话处理及成功审计原子提交;记录 actor、入口、实例、operation 与时间,不记录密码、摘要或 Key 明文 |
| R15 | 运维既有库 | 账户操作核验预期库和实例;读操作不建库或 migrate;schema 不兼容明确拒绝,迁移走独立入口;CLI 参数和日志不暴露凭据 |
4. 技术决策(KTD)
KTD1. 部署方式只决定程序来源,不决定数据位置。 binary ↔ source 可切换;切换保持 DB、数据根、账户与 instance_id。
KTD2. 账户规则只在 Go 后端。 脚本调用实例对应版本的 CLI;不直接改用户表。
KTD3. 一次性登录凭据独立于长期 api_keys。 专用表与兑换 API;普通 Bearer、SSE ?key=、长期 Key 路径均拒绝。
KTD4. 受限会话为独立 scope。 默认 10 分钟;allowlist 仅 me / 设密 / logout。
KTD5. 团队安装默认 PostgreSQL;存储选择显式。 与恢复 KTD5、config.Tier=team 一致。
KTD6. 一次性 Key 默认 30 分钟(可配置建议范围 5–120 分钟);审计记实际 TTL。
KTD7. 激活/恢复设密成功后必须重新密码登录(销毁受限会话、清 cookie)。普通主动改密可保留「要求原密码」路径;激活路径不要求原密码。
KTD8. 普通账户恢复默认保留长期集成 Key;失陷显式撤销全部凭据。 两种情况均保留业务数据。
KTD9. 候选校验完成前不停旧服务;切换短窗;schema 迁移后能否自动回退旧程序另行判断,失败不自动用备份覆盖业务库。
KTD10. 双轨收敛到同一部署计划。 新旧 CLI 同后端;Releases install.sh 变为薄封装或文档降级为遗留,禁止第三套生命周期。
KTD11. 能力声明与帮助同源,但后端仍是执行权威。 新制品提供无副作用的结构化能力声明;旧制品仅按已验证版本兼容,未知形态拒绝。禁止尝试未知子命令或靠 strings 包含某词推断能力。保留 reset-password 的直接重置语义,不偷偷改成签发恢复 Key。
KTD12. 统一密码与开户事务。 严格唯一 INSERT;散列计算在事务外,事务内重新核验权限/账户版本。主动改密、CLI/管理员重置、恢复设密共同执行密码+标志+旧登录 Key 作废+规定会话处理+审计。普通路径保留集成 Key,失陷模式按 KTD8 明确撤销;失败全部回滚。 首次管理员按实例串行,不能只依赖 owner UNIQUE:PG 锁定既有实例身份行,SQLite 使用立即事务,取得串行权后重新核验无用户、无历史业务;引导与能改变此前置条件的开户共用串行点。管理员、激活 Key 和审计共同提交;marker 缺失时由显式 init 处理,账户命令不得补建身份。
KTD13. 审计可归因,运维不隐式迁移。 成功审计与变化同事务;失败记录注明未提交;HTTP actor 来自已认证会话,CLI 记录受信 OS 运维 UID/身份来源,不接受调用方填写的 actor 作为证明。实例身份和操作 ID 实填。既有库读命令只校验兼容性;迁移另行批准和执行。
KTD14. 凭据只经安全输入和单次交付。 优先无回显输入/stdin/受保护文件,DSN 不传 argv。serve 已消费 AGENT_SYSTEM_DSN,账户 CLI 的等价安全输入需在 U7 显式接入并以能力声明核验,不能假定当前 user flags 已读取环境。非交互输出独占创建 0600 文件,日志/evidence 只记凭据 ID。提交后交付失败按 operation 查询,再撤销/重发;库存摘要不能重新打印旧明文,不把重试当首次创建。
5. 职责划分与接口示例
| 部分 | 负责 |
|---|---|
svc.sh |
doctor、env prepare、获取制品、构建、实例配置、生命周期、调用账户 CLI |
| 后端 CLI | 创建账户、签发/撤销一次性 Key、核验目标库、OpenExisting、操作审计 |
| 后端认证 | 原子消费 Key、受限会话、设密事务、中间件限制 |
| 登录页 | 输入一次性 Key、设密、过期/重发提示;遵循 DESIGN.md |
| 发布制品 | 版本、平台、摘要、组件版本、能力声明 |
拟新增 CLI(当前脚本尚不支持;兼容 --from 保留):
./svc.sh doctor --mode binary --tier team
./svc.sh install prod --mode binary --version <固定版本>
./svc.sh install prod --mode source --repo <仓库> --ref <固定提交>
./svc.sh prod deploy --mode binary --version <固定版本>
./svc.sh prod user create --owner alice --role user
./svc.sh prod user issue-login-key --owner alice --purpose recover
./svc.sh prod user list
./svc.sh prod user revoke-login-key --id <凭据编号>
Linux 服务账户与应用账户分开管理。
6. 规格合同(实施前必须写进 U1/U4,否则返工)
- 独立凭据类型 +
POSTbody 兑换 API。 - 会话 scope / 中间件 allowlist;覆盖
must_change普通首登。 - 设密接口分型:受限会话不校验原密码;普通改密继续要求原密码;同事务完成写 hash / 清标志 / 作废 Key / 吊销会话。
- 密码未设置状态:
password_hash可空或 sentinel;VerifyLogin拒绝未设密;创建 = 用户行 + 激活 Key 同事务。 - CLI
OpenExisting:目录/DB 不存在则失败;账户运维默认只校验兼容 schema,不 migrate;迁移独立执行。配套 U1 v0.2 候选同此合同,原 requireCompatible 自动 migrate 是历史实现。 release@<ver>必须解析到该版本目录;缺摘要硬失败。- 部署:获取/构建/校验不停旧服务;组合切换;迁移后回退独立判断。
install与--from同一计划;双轨收敛(KTD10)。- 首次管理员仅新实例;事务防并发。
- 凭据交付:交互 stdout 一次 / 非交互
chmod 600文件;日志与 evidence 只记凭据 ID;随机密码路径首期可并存但默认无密码 + Key。 - 无
flock时拒绝冲突生命周期与账户操作(收紧现状「直通」)。
详细设计(正文在 Dev 仓,随本任务分支提交):
- U1:
docs/svc-deploy/u1-instance-account-binding.md - U4:
docs/svc-deploy/u4-one-time-login-key.md
7. 增量与完成门槛
| 增量 | 主要范围 | 完成条件 |
|---|---|---|
| U1 实例—账户绑定补齐 | 账户 CLI 读实例 manifest;OpenExisting;锁缺失拒绝;install 骨架对接既有 identity | 开户/签发命中同一 store;错误 data/DSN 拒绝;与 recovery U1 字段对齐 |
| U2 二进制与源码来源 | 下载/离线导入、仓库物化、版本清单、候选先校验 | 无开发仓可部署;固定版本与摘要可核验;修复 release 钉扎 |
| U3 环境检测与准备 | 系统适配器、依赖计划、专用工具链、监管配置 | Ubuntu/Debian 首验;幂等;不接管共享服务;binary 不装 Go/Node |
| U4 一次性凭据与认证限制 | Backend CLI/Store/Server/迁移 | 原子开户/兑换/设密;受限会话无法调业务 API;并发兑换单胜 |
| U5 脚本与登录页接线 | 账户帮助、无副作用能力探测、凭据安全交付、Login/设密页 | 新建与恢复走通;设密后清受限会话并返回密码登录;不支持的产品明确拒绝;DESIGN.md |
| U6 组合与故障验收 | Dev 组合、发布包、真机、数据保护 | 两种部署 × 存储档;故障注入;浏览器 + 真机;治理回归 |
| U7 账户事务与稳定性补齐 | 原子开户/引导、统一改密与撤销、审计、只读兼容打开 | 并发建号不覆盖;中断无半状态;旧恢复 Key 失效;身份缺失不改库;升级保持登录能力 |
| U8 实例运维核验与凭据诊断 | 账户状态/事件查询、绑定和制品诊断;补齐 U1 身份消费 | 可定位合法变更来源、识别错库/失效会话/错误 Key 类型;只展示非秘密元信息 |
当前 U1/U4 基础已在源码中,近期优先 U7 和 U5,并与恢复方案 U8 隔离保护协调。 U7 先冻结后端账户事务/审计及能力声明合同;U5 随后完成浏览器与脚本;U8 诊断复用其记录。U2/U3 保留后续,不让通用安装器阻塞 dev 账户稳定。各仓从共同批准基线起独立任务;Backend → Frontend → Dev 组合,正式组合验证后才推进 gitlink。
U7 近期实施与验证合同
覆盖 R5/R6/R9/R10/R11/R14/R15;依赖既有 U1/U4,实施负责人待指定。
范围(相对于 Backend):internal/store/auth.go、internal/store/login_keys.go、internal/store/platform.go、internal/app/app.go、internal/server/accounts.go、cmd/agent-system/main.go;迁移目录仅在 schema 需要时改动。
先刻画现有显式改密、重置及建号行为,再修复并发与中断窗口。账户校验、禁止隐式 migrate、严格 INSERT 和统一密码事务是单一后端实现,脚本不直接写表。
现有测试参考 internal/store/login_keys_test.go、internal/server/login_key_test.go、auth_e2e_test.go;新增 internal/store/account_lifecycle_test.go 与真实 PG 跨进程用例(路径相对于 Backend)。
T7-1:两个独立进程同名开户,只有一个账户/激活 Key 成功,败者不改变胜者角色、密码和成员。
T7-2:开户、Key 签发、改密、会话处理、审计提交各故障点,最终只见完整旧态/新态;输出失败但提交未知时按 operation 查询。
T7-3:普通改密、临时重置、恢复设密后旧未用 Key 都不可兑换;长期集成 Key 按 R10 保留/撤销。
T7-4:读命令命中旧 schema、缺库、错 marker 均拒绝且不 DDL/建库;两个独立进程以不同 owner 并发首次引导单胜,另覆盖引导与普通开户竞争;有历史业务而无用户时拒绝。
T7-5:普通重启、两次更新、SIGTERM/SIGKILL 后测试账户原密码仍验证成功,账户权限及成员不变;不以摘要是否变化替代密码可用性。
执行环境:独立 SQLite 副本、真实 PostgreSQL 独立库、独立端口;不在当前 weichen 账户上故障注入。所有 T7 用例当前 NOT_RUN。
U5/U8 近期接线与验证合同
U5 覆盖 R7/R8/R9/R13/R15,依赖 U7 对外字段合同;范围 scripts/svc.sh、docs/svc.md、Frontend src/api.ts、src/auth.ts、src/components/LoginPage.tsx、src/App.tsx。复用现有登录/设密页,类型明确消费 sessionScope、passwordSet、reloginRequired;恢复 Key 不写持久浏览器存储或 URL。
同一登录页保留默认账号密码入口,增加「激活/恢复 Key」入口并说明只用于设密;长期 API 密钥入口标清集成用途。错类型提示指向正确入口,切换清输入,更新初始密码旧说明,不新增独立恢复页面。
刷新/兑换响应不确定时先查询会话,有效受限会话恢复设密;会话不存在且 Key 已消费/过期时提示联系账户管理员重发,返回「激活/恢复 Key」入口。无密码账户不得被引导用旧密码登录。
测试参考 tests/test_svc_user_binding.py、Frontend test/auth.test.ts、e2e/changepw-form.playwright.ts;新增 Dev tests/test_svc_user_help.py、Frontend e2e/login-key.playwright.ts(计划文件)。
T5-1:完整/短/专题/实例帮助覆盖六个账户命令,并均无 DB/目录/服务副作用。
T5-2:旧产品和 RDM 不支持账户 CLI 时在执行前拒绝;stdout 单次交付,stderr/进程参数/history 无秘密;两类 Key 双向填错有正确引导且切换清输入。
T5-3:激活/恢复完成兑换→设密→重新密码登录;刷新/响应丢失先核会话;Key 已消费而会话失效时经管理员重发的新 Key 可以续办;过期、重复兑换、CSRF 错误有准确提示。
T5-4:受限会话无法访问业务/下载/SSE/admin/插件;设密成功后不得因直接 onDone 误放行。
U8 覆盖 R2/R14/R15,依赖 U7 审计;在 user info/账户事件查询(拟新增)展示实例、设密时间、凭据类型及状态。错误配置、注销、Key 失效与密码错误分别诊断,普通登录错误不泄漏他人账户信息。CLI/HTTP 两入口产生可追溯同一 operation 记录;历史无审计事件保持未知。
首批 dev 原账户闭环(NOT_RUN):实现及隔离验证通过后,另行确认运行验收窗口/实例/版本,由 owner 在浏览器输入已知密码,检查原 user ID、角色、成员及原数据访问。若确实不能登录,明确恢复授权后才签发恢复 Key,保持账户和业务归属;不能为验收先重置能用的密码。随后在授权窗口验证普通重启及两次受控更新后原账户仍可登录。密码不交给 Agent、不进入证据;本轮不操作 live。
必须覆盖的验收场景(摘要)
干净机器二进制安装;源码固定提交与子模块;两种部署互切后数据不变;构建/摘要/配置失败旧服务可用;PG 身份不符拒绝且不回落 SQLite;无密码开户且不覆盖既有;同 Key 并发兑换仅一次成功;受限会话拒业务/SSE/admin;消费后建会话失败与设密中途失败无半完成态;恢复前后业务与权限对账;SQLite 与真实 PG;治理回归;源码测试不代替浏览器与真机。
额外钉住:release@v 多版本不得装成更高版本;账户 CLI 不得回落默认 ./data。
8. 一次性 Key 流程
flowchart LR
A["创建账户或签发恢复 Key"] --> B["输入一次性 Key"]
B --> C["后端原子消费"]
C --> D["仅允许设置密码的短期会话"]
D --> E["设置并确认新密码"]
E --> F["作废恢复凭据及相关旧会话"]
F --> G["使用新密码正常登录"]
凭据规则:独立前缀;只存摘要;用途 activate|recover;状态未使用/已消费/已撤销/已过期;密码变更后旧恢复凭据不可再用。符合 OWASP Forgot Password 与 Session Management;保留现有 CSRF 校验。
9. r2 编制期历史记录:风险与当时未授权范围
本节与第 10 节保留 r2 编制时点的原始边界,不表述本轮当前状态。当前批准、接管与交付范围见页首及 Dev #486;登录恢复实施记录见第 11 节。历史 NOT_RUN 保持原对象范围,部署及共享数据库操作仍须另行授权。
- 编制时点的实施、故障注入、真机部署、浏览器验收:未授权 / NOT_RUN。
- 本轮交付终点:r2 本地草案、恢复配套修订、任务卡与索引、文档核查和审阅记录;以本地文档提交固定审阅对象并满足同步门禁。新增修订仍待批准,不推进 Dev gitlink、不推送/建 PR;启动/同步工具的任务元数据提交单独记录。
- 与 recovery U1 重叠处只做增量,不重做已合入的 manifest 层。
10. r2 编制期历史记录:规划进展与当时下一步
- r1 已批准;U1/U4 基础实现存在于当前固定源码,完整浏览器/故障/运行验收不可据此推断通过。r2 的新增需求和设计仍待人工评审。
- 首选下一任务(待授权):恢复方案 U8 的 SQLite 服务隔离+本案 U7 账户一致性;先消除共享测试数据和不可归因密码变化,再落实 U5。U2/U3 保持后续。
- 前置:审核新增 R13–R15、U7/U8 及 U5 更新,明确实现和隔离测试范围/owner;实现、测试、部署分别授权。Documents 与 Dev 文档草案位于各自独立工作区;正式组合、gitlink 推进与运行验收未执行。
11. r3 实施记录:登录恢复流程(2026-10-13)
授权:用户 2026-10-13「按此方案实施」(会话指示,覆盖登录恢复范围的实施与合入;
代码 Review 与 PR 合入按 Dev policy/delivery.md 由智能体执行)。
三项裁定(对 r2/U5 合同的分歧,用户按新方案裁定)
| 分歧 | r2 表述 | r3 裁定 |
|---|---|---|
| 登录页长期 API 密钥入口 | 保留并标注集成用途 | 移除日常登录入口;程序化客户端直接带 Authorization 头 |
| 管理员网页恢复方式 | 直接设置新密码 | 改为签发一次性登录 Key(旧 HTTP 端点兼容保留,仅前端 UI 下线) |
| must-change 首登会话 | full 会话 + 动态拦截 | 登录即建 set_password_only 受限会话,me.sessionScope 统一驱动设密门 |
已合入(2026-10-13)
- Backend #187(
f103540d):迁移 v28(users/sessions.auth_version+account_events审计); 管理员POST/GET /api/admin/users/{owner}/login-keys、DELETE .../login-keys/{id}; 改密事务内复验会话与认证版本;登录会话创建绑定认证版本(竞态窗口关闭); 主动改密/管理员与 CLI 重置统一单事务;兑换入口按 IP 退避;TLS 下 Secure cookie。 - Frontend #76(
0655bc3):登录页双入口(密码默认 + 一次性 Key,移除ask_); 设密门免当前密码、成功后回登录页(消费reloginRequired);刷新按sessionScope恢复门; 用户管理改签发/撤销 Key(明文仅一次)。 - Dev #504(
268ef29):合同文档 + 组件 pin。
覆盖判定:R7/R8/R9 及 U5 的浏览器接线主体已实现;U7 的登录恢复事务部分已实现, 原子开户/引导、CLI 凭据安全交付与 PG 锁串行仍未实施,U7 不得据此记完成。
验收证据
- GATE:Dev #504 HEAD CI 三作业(test / test-pg / svc-realcombo)success;Backend #187 CI success。 test-pg 即真实 PostgreSQL 运行本任务 store 测试(含 100 并发兑换单胜、登录竞态)。
- SELF(收据,绑定 Dev HEAD
268ef29):真实浏览器全流程验收 PASS——login-key.playwright.ts+changepw-form.playwright.ts于隔离 auth-on 实例 (serve --data /tmp/lr-e2e-data --auth on,二进制 backend=467718a+frontend=9fc7da2): 管理员签发 → 只输 Key 兑换 → 免旧密码设密 → 新密码登录进工作台 → 同 Key 重复兑换被拒。 收据 sha256:be7ae9a2…(验收)、b3a9e691…(终态)。用例文件待 Dev 工作区解封后入库。
遗留(另行任务与授权)
SSE 存量长连接断流通知;e2e/login-key.playwright.ts 入库与 Dev pin;U7 余量;
部署与 live 账户验收。详见合同文档「遗留项」。