# 7-76 [process] AI 主体账号 · Web PAT · 生产级鉴权硬化 · 用户级密码箱 v1 > 创建时间:2026-07-26 > 状态:`PROCESS`(**P0–P1b 已实现**;§12 验收清单已勾选;P2 / 部分跨用户 e2e 残留见 §12.5) > 执行看板:`design/07-ai/process/7-76-execution-goal-v1.md` > Owner:`07-ai` / `mnote-web` control-plane auth / `12-vault`(密码箱多用户与代签面) > 建议 repo 落点:`design/07-ai/process/7-76-ai-principal-web-pat-and-auth-hardening-v1.md` > > 上位依据: > - `/mnt/Data1T/mnote/ARCHITECTURE.md` > - `/mnt/Data1T/mnote/AGENTS.md`(云首发 control-plane、`libsql-local`、Page AI = Pi Lab) > - `design/07-ai/process/7-71-unified-ai-management-control-plane-and-pi-lab-integration-v1.md` > - `design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md` > - `design/12-vault/process/12-1-password-vault-dedicated-crud-workbench-v1.md` > - `design/12-vault/process/12-2-vaultd-local-token-agent-read-path-v1.md` > - `/home/lix/.agent-infra/vault-policy.md` + `$mnote-vault` > > 触发会话: > - Paseo `28e19bd9-f091-4fa8-807b-e80b88d3aba7` > 主题:生产/开发分流、屏蔽测试快速登录、让外部 AI 以正式身份访问自身记录与用户笔记;密码箱按用户隔离并支持分享给 AI。 > > 用户已拍板(相对初稿的硬约束,**不得回退**): > 1. **开发态对标生产**:撤下「测试账号快速登录」;开发只是在生产形态上调试,不靠 fixture 快捷入口。 > 2. **Web PAT 可复显**:不做「创建后永远不可再看明文」;列表默认遮罩,需要时可 reveal;**不是**放弃安全,而是可审计的受控复显。 > 3. **`mnote.e2e` 定位改为系统 AI 主体账号**(不是普通冒充测试人):拥有 AI 自己的知识库、笔记与记录;可查看**别人分享给 AI** 的密码;**仅持有该账号密码的人(一般是 admin)**可登录检查;普通用户不能访问该主体工作区。 > 4. **外部 AI 主能力** = 用户笔记的增删读写(及必要元数据),**不**作为再介入 Pi 内部对话的主路径。 > 5. **密码箱跟随用户**:一个用户只有一个密码箱(用户真源,而非「共享 workspace 一库混装」)。 > 6. **Admin 可代用户签发 vault token**(须审计)。 --- ## 1. 第一结论 本设计同时收口四件事,且它们共用同一套主体模型: | 主题 | 结论 | |------|------| | 鉴权 UI | 移除 `/auth` 测试快速登录与 HTML 内嵌测试密码;`MNOTE_WEB_ALLOW_DEV_FIXTURES` 只服务 seed/smoke 机机接口,**不**再驱动登录页后门。 | | AI 主体 | 保留并正式化系统账号 **`mnote-e2e` / `mnote.e2e@example.com`** 为 **AI Principal(`principal_kind=ai_service`)**,不是普通 e2e 人设;其工作区 / 知识库 / vault 为 AI 私有面。 | | 机机访问 | 引入 control-plane **Web PAT(`mnpat1.*`)**:外部 AI / 脚本用 Bearer 调用 JSON API;浏览器人用继续 cookie session。 | | 密码箱 | 从「workspace 级单库」演进为 **每用户一库**;支持 **share-to-AI**;vault capability token(`mnv1.*`)与 Web PAT **职责分离**;admin 可代签 vault token。 | **一句话:** 人用 cookie 登录自己的工作区;外部 AI 持 **用户或 AI 主体的 PAT** 做笔记 CRUD;读密另持 **vault token**;AI 自己的家在 **AI 主体账号** 下,普通用户进不去。 --- ## 2. 背景与现状 > §2 保留**设计前**痛点快照;**当前实现状态以 §10 / §12 勾选为准**(2026-07-26 核验)。 ### 2.1 测试快速登录(设计前) | 项 | 设计前 | 当前(已核验) | |----|--------|----------------| | 开关 | `ALLOW_DEV_FIXTURES` 默认 false | 不变;seed 仍 fail-closed | | `/auth` | 无开关渲染快速登录 + `data-test-password` | **已移除**按钮与测试密码 DOM | | 测试 | 断言 HTML **含**快速登录 | 断言 **不含** 快速登录 / `data-test-password` / 明文密码 | ### 2.2 AI 管理面(设计前 → 当前) - `7-71` `/admin/ai`、`/user/ai` 主线仍在。 - **已补**:Web PAT API + Bearer 中间件 + 管理 UI「API 访问令牌 / 密码箱 AI 访问」。 - PAT 存储当前为部署侧 sealed JSON(`~/.config/mnote/api-access-tokens.json`),非 control-plane 表;API 合同满足 7-76。 - Page AI = Pi Lab;外部 AI 默认走 PAT + notes/tree/tools,**不含** Pi run scope。 ### 2.3 密码箱(设计前 → 当前) - 真源仍为 workspace 下 `.mnote/vault/**`;用户默认落在 `users//workspaces/my-space`(一人一默认箱路径)。 - share-to-AI / AI vault 路径:12-x 既有能力 + `POST /api/vault/ai/token` Web 签发(admin 可代签)。 - 12-2 vaultd / `mnv1` 读密路径保留。 --- ## 3. Goals / Non-Goals ### 3.1 Goals 1. **开发 = 生产基线**:登录页无快速登录、无测试密码 DOM;需要 seed 时用受控 API / CLI,不走人机后门。 2. **正式 AI 主体**:`mnote-e2e` 升级为系统 AI 账号(可配置显示名);自有笔记/知识库/vault;可接收他人 vault 分享;人登录该账号需密码(admin 持有),普通用户无入口。 3. **Web PAT**:用户与 admin 可签发/列表/撤销/复显(受控);scope 白名单;默认服务「外部 AI 笔记 CRUD」而非 Pi 会话劫持。 4. **密码箱用户真源**:每用户一个 vault 命名空间;share-to-AI 显式授权;admin 可代签 vault token 并全量审计。 5. **凭证分职**:cookie / Web PAT / vault token / 扩展 token / 上游 provider key **互不冒充**。 ### 3.2 Non-Goals - 不把外部 AI 做成第二套 Page AI 宿主;不替换 Pi Lab。 - 不在本阶段做 OAuth2 完整 AS / 第三方 App 商店。 - 不取消 12-2 本机 vaultd 路径;云端多用户在其之上叠加 owner 与 Web 签发。 - 不做「admin 默认可明文浏览任意用户密码」;break-glass 若需要另开 P2 且强审计。 - 不把 vault 挂进 graph/`tree.*` 为普通页面节点。 --- ## 4. 主体与凭证模型 ### 4.1 主体(Principal) | `principal_kind` | 示例 | 谁用 | 工作区 / 知识库 / vault | |------------------|------|------|-------------------------| | `human` | 普通注册用户 | 浏览器人 | 自己的用户空间 | | `ai_service` | `mnote-e2e`(系统 AI 主体) | 外部 AI 以 PAT 操作;人仅 admin 用密码检查 | **AI 私有** 空间 | | `service`(可选 P1) | `service:ops` | 运维脚本 | 极窄 health/overview,默认无用户笔记 | **AI 主体账号规则:** - 账号标识稳定:`username=mnote-e2e`(或 `system/ai`,迁移期保留 e2e 兼容 id)。 - control-plane 标记:`principal_kind=ai_service`、`is_system=true`、`login_policy=password_holders_only`。 - **禁止** 对普通用户暴露「切换到 AI 账号」;禁止公开注册同名。 - **禁止** 把 AI 主体密码写进前端 HTML / 文档 / smoke 默认文案(smoke 用 env 或 vault,不进 SSR)。 - 普通用户访问 AI 主体私有资源 → `403 principal_forbidden`。 - Admin 检查:用**密码登录**该账号(或未来 break-glass 代登,P2);UI 上可在 `/admin/ai` 显示「AI 主体状态」,**不**自动注入密码。 - **禁止** 将 `mnote-e2e` 列入 `MNOTE_ADMIN_USER_IDS` 或 `access-policy.json` 的 `admins`(历史 e2e 方便已废止)。 ### 4.1.1 账户分离(方案 A · 2026-07-26 已落地) | 账号 | principal / role | admin 能力 | 用途 | |------|------------------|------------|------| | **`mnote-admin`** | human / `admin` | **是** | ops:`/admin/*`、access-policy、代签 PAT/vault、全局 AI policy | | **`liaibo` 等真人** | human / `user` | **否** | 个人笔记 / vault / 分享给 AI | | **`mnote-e2e`** | `ai_service` | **否** | AI 私有工作区与知识库;外部 AI 以 PAT 操作 | Admin 判定真源(保持兼容):`users.role=admin` **或** `access-policy.admins` **或** `MNOTE_ADMIN_USER_IDS`。 云首发基线:`admins=["mnote-admin"]` 且 `MNOTE_ADMIN_USER_IDS=mnote-admin`。 本地 insecure 默认口令仅用于 dev(见 `TESTING_REFERENCE`);生产 init 必须显式 `--password`。 ### 4.2 五种凭证(禁止混用) | 凭证 | 前缀 | 作用 | 持有方 | |------|------|------|--------| | Session cookie | (httpOnly cookie) | 人用浏览器全站 | 浏览器 | | **Web PAT** | `mnpat1.` | 机机调 mnote-web JSON API | 外部 AI / 脚本 env 或 0600 文件 | | **Vault token** | `mnv1.` | 仅 vault list/get/resolve/login | agent 本地 token 文件 / skill | | Extension token | `mnext1.` | Chrome 扩展写 vault | 扩展;**禁止**给 agent | | Provider API Key | 厂商格式 | 调 OpenAI 等 | admin provider 配置;**不是**登录 MNote | **原则:一种凭证一种职责。** Web PAT **不等于** 登录 vault;vault token **不等于** 登录 Web;AI 主体密码 **不等于** 分发给所有用户的万能 key。 --- ## 5. 生产级鉴权:撤快速登录 ### 5.1 产品行为 | 环境 | 登录页 | 标准邮箱/用户名密码 | `/api/dev/seed` | AI 主体密码登录 | |------|--------|---------------------|-----------------|----------------| | 生产 | 无快速登录 | 开 | 关 | 仅知密者(admin) | | 开发(对标生产) | **同样无快速登录** | 开 | 仅当 `ALLOW_DEV_FIXTURES=1` 的机机 seed | 同生产;本地 admin 自持密码 | 开发调试方式: - 使用真实注册/登录流程,或本地已知 admin/测试人账号密码(**存在密码箱或本地 secret,不进 HTML**)。 - 自动化:优先 PAT;次选 control-plane seed API(fixtures 开时);**禁止**依赖「快速登录」按钮选择器作为唯一 smoke。 ### 5.2 实现落点(P0) 1. **删除** `AuthPage` 中测试快速登录按钮、`TEST_ACCOUNT_*` 常量注入 DOM、`data-test-password` 等。 2. 清理 `AUTH_SCRIPT` 中 quick-login 分支;表单脚本不得依赖 `quickLogin` 节点(避免 `if (!quickLogin) return` 整页失效)。 3. 更新 `gateway.rs` / 相关 SSR 测试:断言 **不包含**「测试账号快速登录」与测试密码属性。 4. `scripts/TESTING_REFERENCE.md`、AGENTS smoke 文案:改为「开发用标准登录或 PAT;fixtures 仅 seed」。 5. **可选 harden**:对 `principal_kind=ai_service` 的密码登录增加审计日志 `auth.ai_principal.login`;rate limit 加强。 ### 5.3 明确不做 - 不把快速登录藏到「连点 logo 十次」之类隐藏手势。 - 不在生产 HTML 注释里保留 e2e 密码。 --- ## 6. Web PAT(外部 AI 访问合同) ### 6.1 产品 IA #### `/user/ai` → 导航 **「API 访问令牌」**(`#ai-api-tokens`) 用户只管理 **自己的** PAT: - 创建:名称、过期、scope 勾选(白名单)。 - 列表:前缀 `mnpat_****abcd`、创建/最后使用/过期、状态。 - **Reveal(可复显)**:默认遮罩;用户点击「显示」→ 二次确认(或短时 re-auth)→ 展示明文;操作写审计。 - 撤销 / 改名 / 旋转(rotate = 废旧发新,旧 jti 立即失效)。 - 文案:用于外部 AI、脚本访问 **你的** 笔记与授权 API;**不是** vault 读密 token;**不是** Pi 内部会话钥匙。 #### `/admin/ai` → **「API 访问令牌」** 1. 平台级 `service` PAT(可选,窄 scope)。 2. **代签**:选择主体(普通用户 **或 AI 主体**)→ 生成该 `sub` 的 PAT + 审计。 3. 强制撤销任意 PAT、查看元数据与审计。 4. 为 **AI 主体** 预置/轮转「给外部 AI 用的主 PAT」的运营入口(仍走同一表结构)。 ### 6.2 可复显的安全模型(相对「只显示一次」) 用户明确要求 **可再显示**,因此采用: ```text 创建时: raw = 高熵随机 或 mnpat1.. token_hash = SHA-256(raw) // 鉴权比对 token_sealed = Seal(server_key, raw) // 可恢复密封(AEAD) token_prefix = raw 前后缀展示位 明文不进日志 Reveal: 校验 session 为 owner 或 admin 代操作权限 解封 token_sealed → 返回明文一次响应 写 audit: api_token.reveal 鉴权请求: 只比对 token_hash,不走 sealed 热路径 ``` 约束: - `server_key` 来自部署密钥(env / 文件 0600),**不**进库、不进前端。 - Reveal **限流**(按 user / token_id);列表默认永不自动展开。 - 若部署选择更高安全档(`PAT_REVEAL_MODE=create_only`),可编译/配置为创建后清空 sealed —— 默认产品档为 **reveal_allowed**(符合本拍板)。 - 泄露响应:revoke + 轮转;审计含 `jti` 不含 raw。 ### 6.3 Token 形态与存储 ```text mnpat1.. # 或 mnpat1_ + 高熵 random(服务端只存 hash + sealed) ``` Claims(逻辑字段,无论 JWT 形还是 opaque): ```json { "v": 1, "iss": "mnote-web", "aud": "mnote-api", "sub": "user:", "principal_kind": "human | ai_service | service", "scope": ["notes.read", "notes.write", "tree.read", "tree.write"], "workspace_ids": ["*"], "jti": "...", "iat": 0, "exp": 0 } ``` **control-plane 表 `api_access_tokens`:** ```text id, jti, subject_user_id, -- 权限主体 created_by_user_id, -- 签发者(admin 代签可不同于 subject) name, token_prefix, token_hash, -- 鉴权 token_sealed, -- 可复显密封;create_only 模式可空 scopes_json, workspace_ids_json, expires_at, revoked_at, last_used_at, last_reveal_at, created_at, updated_at ``` ### 6.4 Scope 白名单(对齐「外部 AI = 笔记 CRUD」) **P0 默认推荐(创建向导默认勾选):** | Scope | 含义 | 外部 AI 默认 | |-------|------|----------------| | `notes.read` | 读页面/文件正文与元数据(授权工作区内) | ✓ | | `notes.write` | 创建/更新/删除笔记正文(授权范围内) | ✓ | | `tree.read` | 读树/目录投影 | ✓ | | `tree.write` | 新建/重命名/移动/归档(`tree.*`) | ✓(可默认关,产品可选) | | `ai.settings.read` | 读自己的 effective AI 设置 | 可选 | | `ai.usage.read` | 读用量 | 可选 | **默认不开放(Non-Goals / 需显式高危勾选):** | Scope | 说明 | |-------|------| | `ai.sessions.write` / `pi.run` | **不**作为外部 AI 主路径;避免外部 agent 驱动内部 Pi | | `ai.admin.*` | 仅 admin session 或 admin 级 service PAT | | `vault.resolve` | **不**塞进 Web PAT 默认;读密用 `mnv1` | | `provider.keys.read` | 禁止;上游 key 永不经 PAT 下发明文 | 映射实现: - 优先复用现有 `/api/mnote/tools/*` 与 local-folder / page / tree 路由的 **统一鉴权中间件**。 - 每个 handler 声明 required scopes;缺 scope → **403**,不静默放大。 - `directory_grants` / membership **仍然生效**:PAT 不能绕过文件夹授权。 - 写入走既有版本/冲突/watcher 纪律(对齐 7-18);外部 AI 写文件 = 授权 roots 内 patch/write,不是第二套真相。 ### 6.5 鉴权中间件规则 1. 请求带 `Authorization: Bearer mnpat1…` → 走 PAT;**忽略** cookie 叠加(避免权限并集歧义)。 2. 仅 cookie → 人用 session(现有行为)。 3. 绑定 `RequestContext.user_id = subject_user_id`,`auth_method = pat`,附带 scopes。 4. AI 主体 PAT:`subject` 为 AI 用户 id,只能访问 **AI 私有空间 + 显式 share 给 AI 的资源**。 5. 人用户 PAT:只能访问该用户被授权的 workspace 资源。 6. HTML SSR 管理页:**不以长期 PAT 当 cookie** 渲染全站;机机走 JSON。P2 才考虑 exchange code → 短会话。 ### 6.6 调用示例 ```bash # 外部 AI:以某用户 PAT 列树 / 读写笔记 curl -H "Authorization: Bearer mnpat1.…" \ https:///api/… # 具体 path 以 tools/tree/page 现网为准 # 外部 AI:以 AI 主体 PAT 写 AI 自己的知识库笔记 curl -H "Authorization: Bearer mnpat1.…" \ https:///api/… ``` Agent 持有方式:环境变量或 `0600` 文件(对齐 vault token 习惯);**禁止**默认写入前端 localStorage。 --- ## 7. AI 主体:自己的知识库、记录与「被分享的密码」 ### 7.1 AI 私有面 AI 主体拥有与普通用户同构的能力边界,但 **默认不对普通用户可见**: | 能力 | 说明 | |------|------| | 笔记 / 树 | AI 自己的 workspace(或 system workspace 绑定 AI 主体) | | 知识库 | LightRAG 命名空间绑定 AI 主体;索引 AI 私有笔记与授权语料 | | 设置 | `/user/ai` 在 **AI 主体 session** 下的 effective 配置;admin 可在 `/admin/ai` 看全局 | | Vault | **AI 自己的一箱** + **他人 share-to-AI 的只读投影** | 普通用户 **不能**: - 打开 AI 主体的 FileTree / 笔记(无 membership)。 - 列出 AI 主体 vault 私有条目。 - 使用 AI 主体密码(除非 admin 运营持有)。 Admin **可以**: - 密码登录 AI 主体做检查(审计)。 - 代签 / 撤销 AI 主体的 Web PAT 与 vault token。 - 在管理面查看 AI 主体健康、用量、token 元数据(默认无 vault 明文)。 ### 7.2 分享密码给 AI(share-to-AI) ```text 用户 U 的 vault 条目 C → 用户标记 share_to_ai = true 或 创建 share grant: (item_id, grantee=ai_principal, perms=resolve|list) → AI 主体的 vault token / AI 读密路径可见 C → 用户 U 的其他条目默认不可见 → 其他用户 V 不可见 C(除非另有 grant) ``` 隔离验收: 1. 用户 A token 不能 resolve 用户 B 私有条目。 2. AI token 只能 resolve:AI 自有条目 ∪ 有效 share-to-AI 条目。 3. 用户 A 的 Web PAT 默认 **不能** resolve 密码;除非错误地签发了 vault scope(产品向导应拆分两步,默认不勾)。 4. 撤销 share 或 revoke token 后立即失效。 ### 7.3 与 Page AI(Pi)的边界 | 角色 | 职责 | |------|------| | **Pi Lab(内部)** | 浏览器内 Page AI;cookie + directory_grants;不依赖外部 PAT | | **外部 AI(Paseo/Codex/…)** | 持用户或 AI 主体 PAT,做 **笔记/树 CRUD**;可选另持 vault token 读密 | | **禁止默认** | 外部 AI 用 PAT 驱动 `/api/page-ai/pi/*` 代聊、劫持内部 session | 若未来需要「外部系统触发一次 Pi run」,单独立项高危 scope,不进 7-76 P0 默认。 --- ## 8. 密码箱:一人一箱 + 代签 ### 8.1 真源模型(升级 12-1) **产品口径(冻结):密码箱跟随用户,一个用户只有一个密码箱。** 推荐存储解析: ```text # 逻辑 VaultRoot(user_id) → 用户私有系统目录 # 物理(云 / 多用户服务器,推荐) {control_or_data_root}/users//vault/** # 物理(单机 local-first 兼容) 若部署仅有单用户绑定默认 workspace: 可仍落在该用户默认 workspace 的 .mnote/vault/** 但元数据必须带 owner_user_id,且 API 按 user_id 解析,禁止「同事共享 workspace 就共享密码箱」 ``` **禁止:** - 多个 human 共用一个无 owner 的 workspace vault 当默认。 - Agent 用通用 file 工具扫任意 vault 路径。 - 默认 actor 写死所有人共用 `mnote-e2e` 而不校验 owner(开发本机单用户过渡期除外,须文档标明)。 条目 frontmatter / index 强制: ```text ownerUserId: shareToAi: bool | grant refs ``` 旧数据迁移: - 缺 `ownerUserId` → 归属「当前 workspace 的 owner 用户」或部署配置的 `MNOTE_VAULT_LEGACY_OWNER`;迁移完成前,非 owner deny。 ### 8.2 Vault token claims 升级 在 12-2 `VaultTokenClaims` 上强制: ```json { "v": 1, "iss": "mnote-vault", "aud": "mnote-vault", "sub": "user:", "actor": "user:", "principal_kind": "human | ai_service", "scope": ["vault.list", "vault.get", "vault.resolve"], "item_ids": [], "include_shared_to_ai": true, "workspace": null, "iat": 0, "exp": 0, "jti": "..." } ``` - `item_ids` 非空 = 白名单;空 = 该主体命名空间内策略允许的全部(仍受 share 与总闸)。 - AI 主体 token:`include_shared_to_ai=true` 时合并分享投影。 - **Admin 代签**:`created_by` 记 admin;`sub` 仍是目标用户或 AI 主体;**必须审计** `vault_token.issue.delegated`。 ### 8.3 签发入口(同一后端) | 入口 | 谁 | 能力 | |------|----|------| | `/vault` →「AI 访问」 | 用户 | 管自己的条目可见性、签发/撤销自己的 vault token | | `/user/ai` →「密码箱 AI 访问」 | 用户 | 总闸、复制 token、测 doctor;深链避免双真源 | | `/admin/ai` →「密码箱治理」 | Admin | 代签、强制撤销、元数据与审计;**默认不 reveal 用户密码明文** | **总闸:** ```text user.settings.vault_ai_access = off | resolve_only | resolve_and_login ``` 总闸 off → 既有 token verify 失败或签发拒绝。 ### 8.4 与 Web PAT 的关系(再强调) | 需求 | 钥匙 | |------|------| | 外部 AI 增删读写用户笔记 | **Web PAT**(用户 sub) | | 外部 AI 写 AI 自己的笔记/知识库 | **Web PAT**(AI 主体 sub) | | 外部 AI / skill 读密 | **Vault token mnv1**(对应用户或 AI 主体) | | 人打开 `/vault` 编辑 | **Session cookie** | | 扩展保存密码 | **mnext1** | 创建向导默认 **两步拆分**:「笔记 API 令牌」与「密码箱令牌」,禁止一键「全能钥匙」无 scope 确认。 --- ## 9. 信息架构汇总 ### `/user/ai` 侧栏 1. Overview(已有) 2. Models / Tools / Skills(effective) 3. 目录权限(directory_grants,唯一授权真源) 4. **API 访问令牌**(Web PAT,可复显) 5. **密码箱 AI 访问**(vault token + 总闸 + share 摘要) 6. Sessions / Usage(人用 Pi;外部 AI 不依赖此写路径) ### `/admin/ai` 侧栏 1. 全局 provider / model / policy(7-71) 2. **API 访问令牌(平台 + 代签 + AI 主体 PAT)** 3. **密码箱治理(代签 vault token / 强制撤销 / 审计)** 4. **AI 主体**(状态、是否可登录、最近登录审计、知识库/工作区绑定) 5. **环境健康只读**:快速登录已移除、fixtures 开关状态、PAT reveal 模式 ### `/auth` - 仅标准登录/注册。 - 无测试快速登录、无测试密码 DOM。 ### `/vault` - 按 **当前登录用户** 的唯一密码箱工作。 - 条目:**允许 AI 使用 / 分享给 AI**。 - AI 访问令牌入口深链到 `/user/ai#vault-ai-access`。 --- ## 10. 分阶段落地 | 阶段 | 内容 | 验收要点 | 状态(2026-07-26 核验) | |------|------|----------|-------------------------| | **P0** | 删除快速登录 + 修脚本/测试/文档;AI 主体字段标记最小落库 | 任意环境 HTML 无测试密码;标准登录可用 | **done** | | **P0.5** | PAT API + Bearer 中间件 + `/user/ai`·`/admin/ai` PAT UI(sealed 复显) | 外部 AI 用 PAT 完成 notes/tree 读写 | **done**(代码+单测;端到端 smoke 建议补) | | **P1** | 用户级 vault 路径 + share-to-AI + vault 区 | A 不能读 B;AI 能读 share | **done(主路径)**:复用 `users//…` + 12-x share-to-AI;Web 签发 vault token | | **P1b** | Admin 代签 PAT/vault token、撤销、审计;AI 主体运营 | 代签有 `created_by` 审计 | **done** | | **P2** | PAT exchange 短会话、break-glass、service 细粒度、`PAT_REVEAL_MODE=create_only` | 高级场景 | **deferred** | **实现顺序回顾:** P0 → P0.5 → P1/P1b 已合入主工作区;P2 不阻塞产品主路径。 --- ## 11. 安全边界清单 1. 凭证分职,禁止用 vault token 调笔记 API 或用 PAT 当 master key。 2. PAT 与 cookie 不同时并权。 3. Scope 白名单 + handler 强制;默认不含 Pi 写入。 4. directory_grants / membership 不可绕过。 5. PAT reveal 可审计、限流;日志无 raw token。 6. AI 主体私有资源对普通用户 403。 7. share-to-AI 显式;撤销即时。 8. Admin 代签全审计;默认不 reveal 他人 vault 明文。 9. 扩展 token 禁止进 agent。 10. 开发对标生产:无登录页后门。 --- ## 12. 测试与验收 > 勾选口径:**[x] = 代码已实现且本轮有单测/静态核验证据**;未勾选 = 未做独立 e2e 或仅结构依赖、需后续补测。 > 核验日:2026-07-26。证据:`auth.rs` / `api_access_token.rs` / `middleware/request_context.rs` / `ai_admin` UI / `cargo test -p mnote-web --lib` 定向套件。 ### 12.1 P0 鉴权 - [x] `/auth` HTML 不含「测试账号快速登录」、`data-test-password`、硬编码 e2e 密码属性。 - [x] 标准注册/登录仍可用(`auth_api_*` 单测通过)。 - [x] `ALLOW_DEV_FIXTURES=0` 时 `/api/dev/seed` 403(既有 fail-closed 保留)。 - [x] 相关单元/SSR 测试已改断言(`auth_entry_uses_mnote_web_login_ui_when_compat_enabled`)。 ### 12.2 Web PAT - [x] 用户可创建/列表/撤销/复显自己的 PAT(`/api/ai-tokens*` + UI)。 - [x] Bearer 可 `notes.read/write`(及 tree scope):middleware + documents/tree/tools `ensure_scope`。 - [x] 缺 scope 403(`ensure_scope_session_passes_pat_requires` 单测)。 - [ ] 用户 A 的 PAT 不能写用户 B 的私有笔记(**结构上**仍受 directory_grants/membership;**无独立 PAT 跨用户 e2e**)。 - [ ] AI 主体 PAT 不能读普通用户未授权笔记(同上,依赖 grants,**无独立 e2e**)。 - [x] 默认不能调用 Pi run API(PAT scope 白名单不含 Pi;无 `pi.run`)。 - [x] reveal 产生审计(`api_token.reveal`)+ 30s 限流生效(代码路径)。 ### 12.3 Vault 多用户 - [x] 每用户一箱路径解析:`users//workspaces/my-space` + AI vault 路径(12-x / vault-core)。 - [ ] owner mismatch → deny(share 模型与路径隔离已有;**全量 owner 字段强制校验未单独立项闭环**)。 - [x] share-to-AI 后 AI resolve / 取消分享:12-x vault-core share/unshare 能力与单测保留。 - [x] Admin 代签 vault token 有审计(`POST /api/vault/ai/token` + `vault_token.issue(.delegated)`)。 - [x] 通用 file/RAG 仍排除 vault(12-1 既有门闩,本轮未回退)。 ### 12.4 AI 主体 - [x] `mnote-e2e` 注册标记 `role=ai_service`;`/api/ai-admin/users` 返回 `principalKind`。 - [ ] 普通用户 session 无法打开 AI 主体工作区(依赖 membership/默认私有 workspace;**无专用 403 e2e**)。 - [ ] Admin 密码登录 AI 主体成功且有**专用**审计事件(通用登录可用;**未单独埋 `auth.ai_principal.login` e2e**)。 - [ ] AI 知识库命名空间隔离冒烟(**未做本轮专项 smoke**)。 ### 12.5 残留 / 下一步(不回退已勾选) 1. 补 PAT 跨用户拒绝 e2e(A token → B 笔记 403)。 2. 补 AI 主体工作区拒绝 / 知识库隔离浏览器 smoke。 3. 可选:PAT 迁 control-plane 表;AI 主体专用登录审计。 4. P2 整包延后。 5. 零散脚本:`scripts/test-handle-qa.js` 已改为标准登录(本轮核验时修正)。 --- ## 13. 与现有设计的衔接 / 覆盖关系 | 文档 | 关系 | |------|------| | **7-71** | 在 effective AI 配置之上增加 Token / AI 主体运营区段;不另起第二套 provider 真相。 | | **7-18** | 外部 AI 写笔记仍受 AiAccessScope / allowed roots / 冲突模型约束;本设计补 **谁持何种凭证进入**。 | | **12-1** | 工作台 UI 保留;**存储归属**从 workspace 单库升级为 **用户一箱**(本文 §8 为增量合同)。 | | **12-2** | vaultd + `mnv1` 路径保留;claims 强制 user/AI owner;Web 代签与多用户 verify 为增量。 | | **12-3** | `mnext1` 仍仅扩展;不与 PAT/mnv1 合并。 | | **AGENTS / TESTING_REFERENCE** | 删除「优先测试账号快速登录」作为生产或默认真路径的表述。 | 后续若 vault 用户根路径实现细节膨胀,可再拆 `12-4-user-scoped-vault-and-share-to-ai-v1.md`,但 **产品口径以本文为 SSOT**,避免双源。 --- ## 14. 开放实现细节(不阻塞 P0) 1. AI 主体 username 是否长期保留 `mnote-e2e` 字符串,或迁移为 `system-ai`(兼容 id 映射)。 2. `VaultRoot` 物理根:control-plane data dir vs 用户私有默认 workspace —— 实现选一种并写迁移脚本。 3. Web PAT 用 opaque+hash 还是可验证 signed payload —— 两者均可,表结构已支持。 4. 外部笔记 CRUD 优先挂现有 `mnote_agent_tools` 还是一组更窄的 REST 资源路由 —— 建议 **先中间件 + 现有 tools/tree/page**,少造表面。 --- ## 15. 建议 PR 切片 | PR | 内容 | 风险 | |----|------|------| | PR-A | 删除快速登录 + 测试/文档 | 低;立刻降公网面 | | PR-B | `api_access_tokens` schema + middleware + 单测 | 中 | | PR-C | `/user/ai` PAT UI(创建/列表/撤销/reveal) | 中 | | PR-D | notes/tree 路由 scope 接线 + 外部 AI smoke | 中 | | PR-E | 用户级 vault root + owner + share-to-AI | 高(数据迁移) | | PR-F | admin 代签 + AI 主体运营 + 审计 | 中 | --- ## 16. 完成定义(Definition of Done) 当下列全部成立时,本稿可迁 `done/`: | # | 条件 | 状态 | |---|------|------| | 1 | 登录页无快速登录后门,开发/生产行为一致 | **满足**(P0) | | 2 | 外部 AI 可仅凭 Web PAT 在授权范围内笔记 CRUD,无需 cookie/Pi | **代码满足**;建议补浏览器/API smoke | | 3 | AI 主体隔离笔记/知识库,仅密码持有者可人登检查 | **部分满足**(角色标记+默认 workspace;缺专用 e2e) | | 4 | 密码箱用户隔离 + share-to-AI;vault token 与 PAT 分职 | **主路径满足** | | 5 | Admin 代签与 reveal/revoke 有审计;文档与 smoke 一致 | **满足**(残留见 §12.5) | **结论:** 不迁 `done/` 直至 §12 未勾选项(跨用户 e2e / AI 主体工作区 / KB 隔离 smoke)补齐或明确降级为 follow-up。P0–P1b 产品主路径可按 `PROCESS` 继续使用。 --- ## 17. 变更记录 | 日期 | 说明 | |------|------| | 2026-07-26 | 初版:吸收 Paseo `28e19bd9-…` 讨论与用户六条拍板;落盘 `7-76`。 | | 2026-07-26 | 执行:P0–P1b 落地(见 `7-76-execution-goal-v1.md`);P2 延后。 | | 2026-07-26 | 核验并勾选 §10/§12 已完成项;修正 `test-handle-qa.js` 残留快速登录;§12.5 记录残留。 |