Files
mnote/design/07-ai/process/7-76-ai-principal-web-pat-and-auth-hardening-v1.md
T

632 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 7-76 [process] AI 主体账号 · Web PAT · 生产级鉴权硬化 · 用户级密码箱 v1
> 创建时间:2026-07-26
> 状态:`PROCESS`**P0P1b 已实现**;§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/<id>/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 **不等于** 登录 vaultvault token **不等于** 登录 WebAI 主体密码 **不等于** 分发给所有用户的万能 key。
---
## 5. 生产级鉴权:撤快速登录
### 5.1 产品行为
| 环境 | 登录页 | 标准邮箱/用户名密码 | `/api/dev/seed` | AI 主体密码登录 |
|------|--------|---------------------|-----------------|----------------|
| 生产 | 无快速登录 | 开 | 关 | 仅知密者(admin |
| 开发(对标生产) | **同样无快速登录** | 开 | 仅当 `ALLOW_DEV_FIXTURES=1` 的机机 seed | 同生产;本地 admin 自持密码 |
开发调试方式:
- 使用真实注册/登录流程,或本地已知 admin/测试人账号密码(**存在密码箱或本地 secret,不进 HTML**)。
- 自动化:优先 PAT;次选 control-plane seed APIfixtures 开时);**禁止**依赖「快速登录」按钮选择器作为唯一 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.<payload>.<sig>
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.<base64url(payload)>.<base64url(sig)>
# 或 mnpat1_ + 高熵 random(服务端只存 hash + sealed
```
Claims(逻辑字段,无论 JWT 形还是 opaque):
```json
{
"v": 1,
"iss": "mnote-web",
"aud": "mnote-api",
"sub": "user:<user_id>",
"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://<host>/api/… # 具体 path 以 tools/tree/page 现网为准
# 外部 AI:以 AI 主体 PAT 写 AI 自己的知识库笔记
curl -H "Authorization: Bearer mnpat1.…" \
https://<host>/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 分享密码给 AIshare-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 只能 resolveAI 自有条目 有效 share-to-AI 条目。
3. 用户 A 的 Web PAT 默认 **不能** resolve 密码;除非错误地签发了 vault scope(产品向导应拆分两步,默认不勾)。
4. 撤销 share 或 revoke token 后立即失效。
### 7.3 与 Page AIPi)的边界
| 角色 | 职责 |
|------|------|
| **Pi Lab(内部)** | 浏览器内 Page AIcookie + directory_grants;不依赖外部 PAT |
| **外部 AIPaseo/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/<user_id>/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: <user_id>
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:<user_id>",
"actor": "user:<user_id>",
"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 / Skillseffective
3. 目录权限(directory_grants,唯一授权真源)
4. **API 访问令牌**Web PAT,可复显)
5. **密码箱 AI 访问**vault token + 总闸 + share 摘要)
6. Sessions / Usage(人用 Pi;外部 AI 不依赖此写路径)
### `/admin/ai` 侧栏
1. 全局 provider / model / policy7-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 UIsealed 复显) | 外部 AI 用 PAT 完成 notes/tree 读写 | **done**(代码+单测;端到端 smoke 建议补) |
| **P1** | 用户级 vault 路径 + share-to-AI + vault 区 | A 不能读 BAI 能读 share | **done(主路径)**:复用 `users/<id>/…` + 12-x share-to-AIWeb 签发 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 APIPAT scope 白名单不含 Pi;无 `pi.run`)。
- [x] reveal 产生审计(`api_token.reveal`)+ 30s 限流生效(代码路径)。
### 12.3 Vault 多用户
- [x] 每用户一箱路径解析:`users/<id>/workspaces/my-space` + AI vault 路径(12-x / vault-core)。
- [ ] owner mismatch → denyshare 模型与路径隔离已有;**全量 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 跨用户拒绝 e2eA 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 ownerWeb 代签与多用户 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-AIvault 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 | 执行:P0P1b 落地(见 `7-76-execution-goal-v1.md`);P2 延后。 |
| 2026-07-26 | 核验并勾选 §10/§12 已完成项;修正 `test-handle-qa.js` 残留快速登录;§12.5 记录残留。 |