Files
mnote/design/12-vault/process/12-2-vaultd-local-token-agent-read-path-v1.md
T
Agent Board bc6f8488ee feat: vault core/CLI/workbench, vaultd token path, filetree view-state cleanup
Land password-vault dedicated workbench and mnote-vault-core/CLI, agent token
read path design, vault transport split, and retire obsolete filetree smokes.
Ignore local vault reimport scripts that trip secret scanners.
2026-07-24 11:36:06 +08:00

23 KiB
Raw Blame History

12-2 [process] 本地 vaultd + Agent Token 读密路径(不依赖 mnote-webv1

创建时间:2026-07-23
状态:PROCESSP0 L-Access 已落地P1aP1d 已落地web AI list/get/resolve/login/session → coremnote-vault serve UDS + CLI sock→内嵌 fallbacklogin/session 不依赖 mnote-web
Owner12-vault
建议 repo 落点:design/12-vault/process/12-2-vaultd-local-token-agent-read-path-v1.md

上位依据:

  • design/12-vault/process/12-1-password-vault-dedicated-crud-workbench-v1.md(vault 系统空间、AI 密码本策略、文件真源)
  • /home/lix/.agent-infra/vault-policy.md(多 agent 唯一策略 SSOT
  • skills/mnote-vault / $mnote-vault
  • design/07-ai/process/7-18-local-first-agent-file-editing-control-plane-v1.md(禁裸读敏感路径)
  • ARCHITECTURE.md local-first 原则

触发证据:

  • Paseo session 21242fc0-db03-454c-a1e5-723688351ee7:取密依赖 mnote-web 存活 + auth-e2e cookie,步骤膨胀、失败面大。
  • 用户目标:读库本质是本地文件;解密/受控读取只需轻量常驻服务,应绑庞大 mnote-webagent 内部保存 token 做授权取密。

1. Overview

1.1 一句话

文件是库;mnote-vaultd 是受控读密网关(及未来 master key 匣);agent 只持 capability token,不持 master key,也不依赖 mnote-web 进程。

1.2 与 12-1 的关系

维度 12-1(已落地主线) 12-2(本文)
真源 {workspaceRoot}/.mnote/vault/** 结构化文件 不变
人用 UI /vault + mnote-web /api/vault/* 保留;不作为 agent 读密必经
AI 策略 单 AI 本 + 单 resolve 通道 + 禁扫 vault 文件 策略不变transport 换
Agent 通道 HTTP → mnote-web(需 cookie / auth-e2e UDS/loopback → vaultdBearer token
常驻进程 整站 mnote-web 可选 极小 vaultd

12-1 P1「多 agent 唯一策略」中的语义(只读 AI 本、resolve 唯一取密、禁通用 file冻结保留
本文只改 谁持进程、用什么凭证、是否依赖 3000

1.3 现状诚实声明(实现前必读)

当前实现(2026-07):

  • 条目为 workspace 下 结构化 Markdown + frontmattermnote.kind: credential)。
  • cipher-book.json片段占位符展开(如 [A] → 共享串),不是 磁盘 AES 整库加密。
  • 尚无「用户 master password → 内存 master key → 密文 at rest」完整链路。
  • 「解密」在产品语言上 = load 文件 + cipher 展开 + nested secret 解析 + 策略门闩

因此 12-2 分两层能力,禁止混称

名称 P0 是否必须 说明
L-Access 受控读密网关 token 鉴权、只暴露 list/get/resolve、审计、不依赖 mnote-web
L-Crypto 真·at-rest 加密 P2 可选 master key 仅驻 vaultd 内存;token 永不等于 master key

P0 交付 L-Access 即可消除「3000 挂了就不能取密」。
L-Crypto 在文件格式与迁移就绪后再做,不阻塞 agent 读路径瘦身。


2. Goals / Non-Goals

2.1 Goals

  1. Agent 读密(list / get / resolve / login 所需密钥字段)不依赖 mnote-web 运行
  2. 常驻面缩小为 mnote-vaultd(或等价 one-shot CLI 内嵌同一 core,见 §5.4),不跑 SSR/Sidebar/文档编辑。
  3. Agent 内部保存 vault_token;每次调用带 token禁止把 master password / master key 写入 agent 配置。
  4. 保持 12-1 策略:仅 AI 密码本 actor;禁通用工具扫 .mnote/vault/**resolve 审计不落明文到日志。
  5. CLI / Pi 工具 / skill 语义对齐:稳态 12 次调用完成取密(已知 id 时仅 resolve)。
  6. mnote-web UI 与 human CRUD 可继续用现网路径;演进期可与 vaultd 并行,终态共享 mnote-vault-core

2.2 Non-Goals(本设计不承诺)

  • 不重做 /vault 工作台 UI。
  • 不把 vault 变成 graph / tree.* 节点。
  • 不要求远程多机共享 vaultd(本机 local-first;跨机另案)。
  • 不在 P0 强制引入 at-rest 加密迁移(L-Crypto = P2)。
  • 不取消「禁扫 vault 文件」策略(即便未来密文 at rest,仍禁止 agent 把 vault 目录当通用笔记读)。
  • 不把 Cloudflare 过人机验证自动化为无人值守(仍 human → session 回写)。

3. 问题根因(为何要拆)

今天 Agent 取密关键路径:

  skill/policy
    → mnote-vault-cli auth-e2e   # 需要 mnote-web + 用户 auth API
    → list / resolve HTTP        # 需要 3000 存活 + cookie
    → (可选)login / session

失败面:
  - web 未启动 / 崩溃 / 端口占用
  - cookie eval / FORCE_COLOR / 环境变量污染
  - agent 探索路径、读 policy、猜 URL → 工具次数爆炸

根因归类:

误绑定 正确归属
读本地文件 → 需要 Web 服务器 读文件 → 本地 IO + 可选小网关
Agent 身份 → mnote-web session cookie Agent 身份 → vault capability token
解密权 → 与 SSR 同进程 解密/受控 resolve → vaultd(或 CLI 内嵌 core

4. 目标架构

4.1 分层图

┌──────────────────────────────────────────────────────────────┐
│ Agent runtimeGrok / Codex / Hermes / Pi / Paseo           │
│  - 持有 vault_tokenenv / 0600 文件 / 可选 keyring         │
│  - 薄客户端:CLI 或 Pi tool → 只打 vaultd                     │
│  - 禁止:通用 file 扫 .mnote/vault/**                         │
└────────────────────────────┬─────────────────────────────────┘
                             │  Authorization: Bearer <token>
                             │  优先 Unix Domain Socket
                             ▼
┌──────────────────────────────────────────────────────────────┐
│ mnote-vaultd(常驻 · 小 · 本机)                               │
│  - 校验 tokeniss/aud/exp/scope/actor                      │
│  - 解析 vault rootworkspace / AI actor root               │
│  - load index / credential md + cipher expand + nested secret │
│  - L-Crypto 时:内存持 master keylock 清空                  │
│  - 审计 jsonl(无 plaintext value                           │
│  - 不托管:SSR、树、Page AI、control-plane 全站逻辑           │
└────────────────────────────┬─────────────────────────────────┘
                             │ 受控读写 FS
                             ▼
┌──────────────────────────────────────────────────────────────┐
│ 本地文件真源(SSOT,与 12-1 相同)                              │
│  {workspaceRoot}/.mnote/vault/                                │
│    vault-index.json                                           │
│    credentials|*.md(条目)                                   │
│    cipher-book.json                                           │
│    audit.jsonl                                                │
│  AI 本:MNOTE_AI_VAULT_ACTOR 对应 root(实现沿用 12-1)         │
└──────────────────────────────────────────────────────────────┘

旁路(可选,非读密阻塞路径):
  mnote-web  /vault UI、human CRUD、共享到 AI 本
  vault-login-helper  resolve 后外站登录 / session 回写

4.2 两类密钥(禁止混用)

名称 持有者 用途 落盘?
Master keyL-Crypto 仅 vaultd 内存 解密 at-rest 密文包 否(仅 unlock 后内存)
Vault tokencapability Agent 配置 / runtime 向 vaultd 证明「谁、何 scope、何 actor」 可:~/.config/mnote/vault-tokens/*.token0600

硬规则:

  1. Token 不能派生或编码 master key。
  2. 泄露 token ⇒ 在 vaultd 存活且已 unlock 时可 resolve不能离线解密磁盘(L-Crypto 后更强)。
  3. 泄露 master password ⇒ 可 unlock 整库;不得写入任何 agent skill / env 默认模板。

4.3 Token 声明(逻辑 schema

{
  "v": 1,
  "iss": "mnote-vaultd",
  "aud": "local-vaultd",
  "sub": "agent:paseo",
  "actor": "mnote-e2e",
  "scope": ["list", "get", "resolve", "login", "session"],
  "workspace": "optional-stable-id-or-path-hash",
  "iat": 0,
  "exp": 0,
  "jti": "uuid"
}
  • 序列化:紧凑 JSON + HMAC-SHA256(本机 secret)或 ed25519(若未来多签发方)。
  • 传输:Authorization: Bearer <base64url(payload).base64url(sig)>(类 JWT 即可,不必上完整 OIDC)。
  • P0 默认 scopelist + get + resolvelogin/session 可同 token 或分 token。
  • actor 必须等于 AI 密码本 actor(默认 mnote-e2eenv MNOTE_AI_VAULT_ACTOR 可覆盖签发时固化进 token)。

4.4 Bootstraptoken 从哪来)

一次(人或安装脚本):
  1. 启动 vaultd(或 CLI doctor
  2. L-Crypto)用户 unlock → master key 入内存
  3. vaultd issue-token \
       --agent paseo \
       --actor mnote-e2e \
       --scope list,get,resolve,login,session \
       --ttl 90d
  4. 写入 ~/.config/mnote/vault-tokens/default.token0600
  5. 各 agent skill / envMNOTE_VAULT_TOKEN_FILE 或 MNOTE_VAULT_TOKEN

日常 agent
  读 token → 调 resolve → 用完 value(不写聊天、不写无关日志)

轮换:revoke jti + 重新 issuevaultd 维护 deny jti 集合(内存 + 可选小文件)。


5. mnote-vaultd 规格

5.1 进程与监听

P0 决定
二进制名 mnote-vaultd(或 mnote vault-daemon 子命令)
默认传输 Unix Domain Socket$XDG_RUNTIME_DIR/mnote-vaultd.sock(回退 /tmp/mnote-vaultd-$UID.sock
TCP 备选 127.0.0.1:17300默认关;仅显式 --listen-tcp
权限 sock 0600、属主当前用户;拒绝非本 uid(若 OS 支持 peer cred
启动 user systemd / 登录脚本 / 首次 CLI 自动拉起(见 §5.4)
资源 目标:常驻 RSS 远小于 mnote-web;无浏览器、无 leptos SSR

5.2 API 表面(JSON over HTTP/1.1 on UDS,或 length-prefixed JSON

与现 Pi / CLI 语义对齐,路径前缀建议 /v1/

方法 路径 scope 解密/展开 说明
GET /v1/health 无 token 也可 ok, unlocked, version, actorDefault
GET /v1/items list 否* L0 列表;同现 list_ai
GET /v1/items/{id} get 否* 元数据 + 掩码;无 password 明文
POST /v1/items/{id}/resolve resolve body: field, accountId?, secretId?
POST /v1/items/{id}/login login 内部 resolve 可 P1 仍代理或旁路 helper
POST /v1/items/{id}/session session 写 session 人机回写
POST /v1/lock admin 清 master keyL-Crypto
POST /v1/unlock admin 本机交互/钥匙环;给 agent 默认 scope
POST /v1/tokens/issue admin 签发 agent token
POST /v1/tokens/revoke admin 吊销 jti

* list/get:若未来元数据也加密,仅解密标题等非 secret 字段;password 永不出现在 list/get。

错误码(稳定字符串,CLI/skill 可分支):

code HTTP 含义 Agent 动作
vaultd_unavailable sock 连不上 尝试 autostart 或提示用户起服务
vault_token_missing 401 无 Bearer 检查 token 文件 / env
vault_token_invalid 401 签名/格式错 重新 issue
vault_token_expired 401 exp 轮换 token
vault_scope_denied 403 scope 不足 重新签发含所需 scope
vault_actor_mismatch 403 token.actor ≠ AI 本 用正确 actor token
vault_locked 423 L-Crypto 未 unlock 提示用户 unlock不是启动 mnote-web
vault_item_not_found 404 id 不存在 / 非 AI 本 list 再选
vault_resolve_field_invalid 400 字段不合法 对齐 schema
vault_resolve_ai_only 403 试图 resolve 用户本 策略拒绝(与 12-1 一致)

5.3 与文件布局(概念,兼容 12-1)

{workspaceRoot}/.mnote/vault/     # SSOTvaultd 与 mnote-web 共享读
  vault-index.json
  … credential markdown …
  cipher-book.json
  audit.jsonl                     # 可继续追加;vaultd 写 agent 通道审计

~/.config/mnote/
  vaultd.toml                     # 可选:root 覆盖、ttl 默认、tcp 开关
  vault-tokens/
    default.token                 # agent 用,0600
  vaultd-hmac.key                 # 签发 secret0600;仅 vaultd/admin CLI 读

$XDG_RUNTIME_DIR/mnote-vaultd.sock

Workspace root 解析顺序(P0):

  1. 请求头 / CLI 显式 --workspace
  2. env MNOTE_VAULT_WORKSPACE
  3. vaultd.toml 默认
  4. 与现 mnote-web 一致的「当前 dev workspace」约定(实现时对齐 vault.rs AI root 解析,避免双根)

AI actor root:继续 MNOTE_AI_VAULT_ACTORtoken 内 actor 必须匹配。

5.4 无常驻时的退化(仍不依赖 mnote-web)

若用户不愿常驻 daemon

mnote-vault-cli resolve --id X
  → 若 sock 可达:走 vaultd
  → 若不可达:CLI 内嵌 mnote-vault-core(同代码)+ 本机 token 校验
       校验密钥来自 ~/.config/mnote/vaultd-hmac.key(用户可读则等同本机信任)

注意: 内嵌模式仍 禁止 无 token 裸读;仍 禁止 agent 用通用 Read 打开 vault 文件。
AutostartCLI 可 spawn 一次 vaultduser 级),避免每个 agent 拉起全站 3000。

5.5 login / session 边界

能力 建议 owner 依赖
resolve 密码字段 vaultd 必含 仅本地文件
list/get vaultd 必含 仅本地文件
外站 login playbook vaultd 插件 vault-login-helper 网络 + 可选浏览器
Cloudflare session 回写 同 login helper 人 + chrome-bridge

P0 最小闭环:health + list + get + resolve
login/session 可 P0.5 从现 mnote-web 逻辑迁出或短期仍调 web(但 resolve 不得回退依赖 web)。


6. Agent 侧合同

6.1 环境变量

变量 含义
MNOTE_VAULT_TOKEN 直接 token 字符串(会话级优先)
MNOTE_VAULT_TOKEN_FILE 默认 ~/.config/mnote/vault-tokens/default.token
MNOTE_VAULT_SOCK 覆盖 UDS 路径
MNOTE_VAULT_WORKSPACE workspace root
MNOTE_AI_VAULT_ACTOR 与签发 token 一致(文档提示;以 token.actor 为准)
MNOTE_BASE_URL + MNOTE_COOKIE 读密路径废弃;仅 human/web 调试保留

6.2 CLI 稳态(目标)

# 一次安装(人)
mnote-vault doctor          # sock / token / unlock 状态
mnote-vault unlock          # 仅 L-Crypto
mnote-vault issue-token …   # 写入 token 文件

# Agent 每次取密(已知 id
mnote-vault resolve --id <id> --field password [--account-id …] [--raw]

# 未知 id
mnote-vault list
mnote-vault resolve --id <id> --field password

删除读密对 auth-e2e 的依赖。
auth-e2e 可留作「测 mnote-web UI」工具,不得再出现在 $mnote-vault skill 稳态步骤。

6.3 Skill / policy 修订点(实现阶段同步,本文先钉文案)

/home/lix/.agent-infra/vault-policy.md$mnote-vault 将改为:

  1. 读密:token + vaultd/CLI,不经 mnote-web。
  2. 仍禁扫 .mnote/vault/**
  3. 登录仍优先 loginCF 仍 human + session
  4. 故障树:vault_locked → 用户 unlockvaultd_unavailable → 起 vaultd / CLI 内嵌;不要默认 desktop:hot

6.4 Pi tools

现名 后端切换
mnote.vault.list|get|resolve|login|session 默认连 vaultd;失败策略见 §5.4
鉴权 Bearer token,不再注入 web cookie

6.5 安全行为(agent

  • 聊天 / transcript:只写 transcriptHint,不写 password / cookieHeader。
  • resolve value 仅用于当前工具链下一步(登录、填表),不回显用户除非用户明确要求。
  • 禁止把 token 贴进公开 issue / 截图。

7. mnote-web 关系与迁移阶段

7.1 阶段

阶段 内容 退出标准
A 现状 Agent → mnote-web cookie → 文件
B 并行(P0 抽出 mnote-vault-corevaultd + CLI token 路径;web 仍可直读文件 不启 3000 可 resolve smoke 绿
C 收口(P1 web /api/vault/ai/* 改为调 core 或代理 vaultdskill 去 auth-e2e 双路径一致;旧 cookie AI 路径标 deprecated
D 可选(P2 L-Crypto at-restweb unlock UI 只负责把 key 交给 vaultd 磁盘密文;无 key 不可读

7.2 代码落点(建议)

crate / 路径 职责
rust/crates/mnote-vault-core(新) index/load/parse、cipher expand、nested secret resolve、audit append、(P2encrypt
rust/crates/mnote-vaultd(新)或 mnote-vault bin UDS 服务、token 校验、issue/revoke
mnote-web vault_store 逐步 thin wrapper → coreUI 路由保留
scripts/mnote-vault-cli.js 默认 sock+token;保留 --via-web 仅调试

禁止: 在 vaultd 内重新实现第二套 frontmatter schema。

7.3 兼容

  • 文件 schemamnote.vault.credential.v1 等)为 12-2 破坏性变更。
  • 现有 AI 本条目无需迁移即可被 vaultd list/resolveL-Access)。
  • L-Crypto 若上:单独 migration 设计 + 双读窗口,不在本文展开实现细节,只保留扩展点。

8. 威胁模型(摘要)

威胁 缓解
Agent prompt 注入要求 dump 全库 按条 resolvelist 无密;rate limit;审计
Token 文件被同机其他用户读 0600 + UDS peer cred
Token 被复制到另一台机器 aud=local-vaultd + 可选绑定 machine-id;无 hmac key 则签失败
磁盘被盗 P0OS 磁盘加密依赖;P2vault at-rest
恶意进程连 sock 无有效 token 拒绝;sock 权限
日志泄漏 audit 不写 valueCLI 默认 JSON 管道、skill 禁回显
mnote-web RCE 读密不依赖 web 后,攻击面与 key 分离(P2 更明显)
用户把 vault 目录加进 agent 工作区 策略 + Pi allowlist deny;文档披露(12-1 已有)

9. 验收标准(设计完成 → 实现门闩)

9.1 P0 必须

  • mnote-web 未运行时,持有效 token 可 list + resolve 成功(AI 本已有条目)。
  • 无 token / 坏 token → 明确错误码,不回退匿名读文件。
  • resolve 响应含 valueaudit 无 value(沿用 store audit)。
  • CLI 稳态文档与 skill auth-e2e 作为读密前置。
  • 通用 file 工具读 vault 路径仍 deny(web 与 Pi 侧回归,既有策略)。
  • 单元测试:token 校验、scopestore 原有 20 测。
  • 集成 smokecargo run -p mnote-vault -- … 不启 30002026-07-23 本机绿)。

9.2 P1

  • login/session 不依赖 webcore + CLI + UDS;外站 HTTP 仅 api_first 出站,resolve 仍本地)。
  • mnote-web AI list/get/resolve/login/session thin-wrap 同一 core2026-07-23)。
  • policy / skill / Paseo appendSystemPrompt 全文切换(skill 已更新 login/session 本地路径;Paseo 段可再对齐)。

9.3 P2L-Crypto

  • unlock 前 resolve → vault_locked
  • master key 仅内存;lock 后不可 resolve。
  • token 无法离线解密 credential 文件。

10. 实现 Checklist(批准设计后执行;本文件阶段只列不写代码)

PR-1 — core 抽出

  • 新建 mnote-vault-core,从 vault_store 搬迁:load index、load credential、cipher expand、resolve_record_secret_field、AI actor root 解析。
  • mnote-web AI 路径 thin-wrap corelist/get/resolve/login/session);CRUD/workbench 仍可双路径。

PR-2 — vaultd + token

  • token issue/verifyHMAC + 文件格式 mnv1.*)。
  • UDS serverhealth/list/get/resolve/login/sessionmnote-vault serve)。
  • audit 写入(resolve/login/session 经 store)。
  • CLI 默认 local-core + tokenmnote-vault binsock 优先 fallback 内嵌)。

PR-3 — agent 面

  • 更新 vault-policy.md$mnote-vault(读密去 auth-e2elogin/session 本地 CLI)。
  • Pi tool 仍进程内 thin-wrap core(不强制走 sock;可后置切 UDS)。
  • smoke:不启 3000 → resolve / session+login reuse。
  • TESTING_REFERENCE 增补 vault-local 段落(含 session/login)。

PR-4 — login 迁出

  • login/session 进 core + CLI + UDSapi_first 出站 HTTP 在 corehuman_required 仍人 + session 回写)。
  • skill 表述:login/session 可本地、不依赖 mnote-web。

PR-5 — L-Crypto(另设计修订)

  • 密文格式、unlock UI、迁移工具。

11. 待决问题(实现前钉死;默认推荐已标)

# 问题 推荐默认
D1 UDS vs TCP UDS 默认TCP 显式开
D2 Token 算法 HMAC-SHA256 本机 secret
D3 无 daemon 时 CLI 内嵌 允许,同 token 校验
D4 login 是否进 P0 vaultd P0 不含P0 只 resolve 闭环
D5 web 是否立刻代理 vaultd P1P0 双路径并行
D6 AI root 与多 workspace P0 单默认 workspace + env 覆盖
D7 L-Crypto 时间表 不进 P0;扩展点保留
D8 token TTL 默认 90d;可 issue 无 exp 仅 dev

若用户否定推荐,在本文修订表追加一行后再开 PR-1。


12. 修订记录

日期 说明
2026-07-23 v1 初稿:本地文件 SSOT + vaultd + agent token;与 12-1 AI 策略对齐;L-Access P0 / L-Crypto P2;验收与 PR 切片
2026-07-23 P0 落地:mnote-vault-core + mnote-vault CLIissue-token/list/get/resolve);无 web smoke 绿;UDS vaultd / web 共 core 留 P1
2026-07-23 P1dlogin/session → core + CLI/UDSweb thin-wrapsession 回写 + login reuse 不依赖 mnote-web

13. 批准栏(实现启动条件)

  • 用户确认 §2 Goals / Non-Goals2026-07-23「先按12-2实现」)
  • 用户确认 §11 默认决策(含 D3 内嵌 CLI)
  • P0core + CLI 内嵌 resolvelogin / UDS / web thin-wrap 后置

P0 交付物:rust/crates/mnote-vault-corerust/crates/mnote-vaultbin mnote-vault)。