632 lines
30 KiB
Markdown
632 lines
30 KiB
Markdown
# 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/<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 **不等于** 登录 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.<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 分享密码给 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/<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 / 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/<id>/…` + 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/<id>/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 记录残留。 |
|