Prune stale docs and update .hermes content

Delete a large set of outdated documentation (many files under docs/ and .hermes/plans/, including audits, design, prd, technical, planning, assets, and todos). Update and consolidate .hermes content: refresh shared-memory pages (decision-log, development-workflow, document-map, pitfalls, project-overview, team-conventions) and several skills/references under .hermes/skills. Also modify AGENTS.md, README.md, UI_CODING_STANDARD.md, docs/README.md and .encoding-check-ignore. Purpose: clean up stale planning/audit material and keep current hermes documentation and related top-level docs in sync.
This commit is contained in:
2026-05-15 06:24:07 +08:00
parent 2eded08bc7
commit 3cb3efb4d0
708 changed files with 4033 additions and 142328 deletions

View File

@@ -1,309 +0,0 @@
# 多端登录会话身份模型设计
日期:`2026-04-21`
## 1. 文档目的
这份文档用于补齐当前鉴权体系中“多端登录识别”能力的落地设计,目标是让后端能够稳定标识:
1. 不同设备上的浏览器登录
2. 同一设备上的不同浏览器登录
3. 微信小程序、支付宝小程序、抖音小程序等小程序来源
4. 微信内 H5 与普通浏览器 H5 的差异
同时冻结:
1. `refresh_session` 需要新增的客户端身份字段
2. 前端与网关需要补传的上下文
3. Axum 派生展示名与“当前设备”标识的规则
4. 与后续 `/api/auth/sessions``/api/auth/sessions/:sessionId/revoke` 的契约关系
## 2. 当前问题
当前项目里“设备类型”只有最小占位语义:
1. Node `buildAuthRequestContext(...)` 直接写死 `clientType = "browser"`
2. `clientLabel` 只是按 `User-Agent` 粗略显示“移动端浏览器 / 网页端浏览器”
3. `refresh_session` / `user_sessions` 里没有区分运行时、小程序来源、浏览器实例与设备实例的结构化字段
这会导致:
1. 无法区分“同一台电脑上的 Chrome 和 Edge”
2. 无法稳定标记“微信小程序登录”和“微信内 H5 登录”
3. 无法给会话列表生成稳定、可读的端侧标签
## 3. 设计目标
新的会话客户端身份模型必须满足:
1. 会话列表能区分“设备”与“客户端运行时”
2. 同设备不同浏览器被视为不同登录端
3. 小程序来源可稳定区分到平台级别
4. 前端不需要拿到真实设备名称,也能展示稳定友好标签
5. 浏览器拿不到真实设备名时,系统仍能自动生成展示名
## 4. 基本原则
### 4.1 不追求真实设备名
浏览器端通常无法拿到:
1. 系统设置里的设备名称
2. 手机自定义名称
3. 电脑主机名
因此本设计不依赖真实设备名称,而是生成“会话展示名”。
### 4.2 会话识别拆三层
当前阶段统一拆为:
1. `client_type`
大类,例如浏览器、小程序、桌面客户端
2. `client_runtime`
具体运行时,例如 Chrome、Safari、微信小程序
3. `client_instance_id`
客户端实例 ID用于区分“同设备不同浏览器”以及“同浏览器不同安装/清缓存”
### 4.3 展示名由后端派生
`device_display_name` 不直接信任前端自由文本,而由后端基于结构化字段派生,必要时允许前端提供候选值。
## 5. 字段模型
`refresh_session` 建议新增以下字段:
| 字段名 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `client_type` | `String` | 是 | 大类,见 6.1 |
| `client_runtime` | `String` | 是 | 运行时,见 6.2 |
| `client_platform` | `String` | 否 | 平台,如 `windows``macos``ios``android` |
| `client_instance_id` | `String` | 否 | 客户端实例 ID由前端持久化生成 |
| `device_fingerprint` | `String` | 否 | 后端派生指纹,用于聚合同设备近似会话 |
| `device_display_name` | `String` | 是 | 给用户展示的设备/端侧名称 |
| `mini_program_app_id` | `Option<String>` | 否 | 小程序 appid |
| `mini_program_env` | `Option<String>` | 否 | 小程序环境,如 `develop``trial``release` |
| `user_agent` | `Option<String>` | 否 | 原始 UA |
| `ip` | `Option<String>` | 否 | 登录 IP |
说明:
1. `client_type``client_runtime` 是强约束字段,必须稳定可枚举。
2. `client_instance_id` 用于区分“同一台设备上的不同浏览器/不同安装实例”。
3. `device_fingerprint` 不是鉴权凭据,只用于会话展示聚类。
4. `device_display_name` 是最终展示字段。
## 6. 枚举定义
### 6.1 `client_type`
固定枚举:
1. `web_browser`
2. `wechat_h5`
3. `mini_program`
4. `native_app`
5. `desktop_app`
6. `unknown`
### 6.2 `client_runtime`
当前阶段固定允许:
1. `chrome`
2. `edge`
3. `safari`
4. `firefox`
5. `wechat_embedded_browser`
6. `wechat_mini_program`
7. `alipay_mini_program`
8. `douyin_mini_program`
9. `unknown`
后续如扩展,不改已有语义,只追加枚举。
### 6.3 `client_platform`
当前阶段固定允许:
1. `windows`
2. `macos`
3. `linux`
4. `ios`
5. `android`
6. `unknown`
## 7. 前端采集输入
### 7.1 Web 浏览器
浏览器侧建议补传以下请求头:
1. `x-client-type`
2. `x-client-runtime`
3. `x-client-platform`
4. `x-client-instance-id`
其中:
1. `x-client-instance-id`
由前端首次启动时生成并持久化到本地存储
2. `x-client-runtime`
可由前端粗判,也允许后端根据 UA 覆盖修正
### 7.2 小程序
小程序侧额外补传:
1. `x-client-type=mini_program`
2. `x-client-runtime`
`wechat_mini_program`
3. `x-client-platform`
`ios` / `android`
4. `x-client-instance-id`
5. `x-mini-program-app-id`
6. `x-mini-program-env`
## 8. 后端判定规则
### 8.1 输入优先级
Axum 侧按以下顺序解析:
1. 显式请求头
2. `User-Agent` 自动识别
3. 回退默认值
### 8.2 默认回退
如果没有任何显式端侧头:
1. `client_type = web_browser`
2. `client_runtime` 由 UA 粗判,判不出则 `unknown`
3. `client_platform` 由 UA 粗判,判不出则 `unknown`
### 8.3 微信内 H5
如果 UA 命中 `MicroMessenger` 且不是小程序显式来源:
1. `client_type = wechat_h5`
2. `client_runtime = wechat_embedded_browser`
### 8.4 小程序来源优先于 UA
如果 `x-client-type=mini_program`
1. 不再按普通 UA 逻辑覆盖成浏览器
2. `client_runtime` 必须优先保留小程序来源值
## 9. `client_instance_id` 规则
### 9.1 作用
它用于区分:
1. 同设备不同浏览器
2. 同浏览器不同安装实例
3. 小程序容器与普通 H5 容器
### 9.2 生成策略
前端首次启动时生成一个随机字符串并持久化:
1. Web`localStorage` / `indexedDB`
2. 小程序:平台侧本地存储
说明:
1. 清浏览器数据后会变化,这是可接受的。
2. 它不是登录凭据,只是会话识别辅助字段。
## 10. `device_fingerprint` 规则
后端可基于以下字段做稳定哈希:
1. `client_type`
2. `client_runtime`
3. `client_platform`
4. `client_instance_id`
如果 `client_instance_id` 缺失,则回退:
1. `client_type`
2. `client_runtime`
3. `client_platform`
4. `normalized user_agent`
说明:
1. 这个指纹只用于显示和聚类,不作为鉴权依据。
2. 同一设备上的不同浏览器通常应产生不同指纹。
## 11. `device_display_name` 派生规则
后端固定按以下优先级派生:
1. 小程序:
- `微信小程序 / iPhone`
- `微信小程序 / Android`
- `支付宝小程序 / Android`
2. 微信内 H5
- `微信内网页 / iPhone`
- `微信内网页 / Android`
3. 普通浏览器:
- `Windows / Chrome`
- `macOS / Safari`
- `iPhone / Safari`
- `Android / Chrome`
4. 无法识别时:
- `未知设备`
## 12. 与会话列表 DTO 的关系
当前 `AuthSessionSummary` 至少建议新增:
1. `clientRuntime`
2. `clientPlatform`
3. `deviceDisplayName`
4. `miniProgramAppId`
当前已有字段中的:
1. `clientType`
保留,但升级为新枚举
2. `clientLabel`
后续可由 `deviceDisplayName` 替代,或先兼容保留
## 13. 首版 Rust 落地范围
当前首版建议先完成:
1. Axum 从请求头 + UA 解析 `SessionClientContext`
2. `module-auth``refresh_session` 增加结构化客户端字段
3. 登录创建 session 时写入这些字段
4. 单元测试覆盖浏览器、小程序、微信 H5 的识别规则
本轮不强制一起完成:
1. `/api/auth/sessions`
2. 前端全面补传 `x-client-*`
3. 小程序端 SDK 对接
## 14. 不允许的设计漂移
后续实现时禁止:
1. 继续把所有终端都写成 `browser`
2.`User-Agent` 文本直接充当最终设备名称
3. 用 IP 作为设备唯一标识
4.`client_instance_id` 当成安全凭据
5. 前端任意上传自由文本设备名并直接持久化
## 15. 完成定义
满足以下条件时,这条能力设计视为完成:
1. 会话身份字段已能区分来源、运行时、平台、实例
2. 同设备不同浏览器与小程序来源都有明确建模
3. `device_display_name` 已有统一派生规则
4. 后续 `/api/auth/sessions` 已有稳定可用的数据基础