统一记录 AI 平台的 API Key / token / 密码,密文存储、浏览器本地解密。本文说明架构、解密设计、v2 修复内容与当前状态。
VAULT_MASTER,64 位十六进制)
口令不写在本文档里(本页是公开 URL)。直接问八戒要就行——说一句「给我保险库的口令」,它会即时读出来给你,不需要你自己记、也不需要另存到别处。
若手边有终端也可自取:
bash ~/.workbuddy/skills/account-vault/scripts/master-guard.sh --show
deepseek)过滤,点「显示」看明文、点「复制」拷走。默认脱敏显示前 6 后 4 位。
VAULT_MASTER 现在同时保存在三个物理位置,每天 08:00 自动体检,任一处丢失会用其余副本自动补回。需要时问八戒即可。详见第 8 节。
一处存、两处取的密钥保险库。敏感值一律 AES-256-GCM 加密,服务器与仓库里只有密文。
| 入口 | 形态 | 怎么解锁 |
|---|---|---|
| /accounts/ | 网页端,纯前端解密 | 浏览器输入 VAULT_MASTER |
| WorkBuddy 对话 | account-vault 技能(CLI) | 自动读 .env 的 VAULT_MASTER |
数据真相源是本地 ~/.workbuddy/accounts/vault.json;网页端的 accounts/vault.json 是它的只读副本,由同步脚本推送。
网页端与 CLI 参数完全一致,否则互相解不开:
PBKDF2(master, salt, 100000 次, SHA-256)
→ AES-256-GCM(iv 12 字节,authTag 独立存储)
master = VAULT_MASTER,一串 64 位十六进制(不是人类口令)
每条 entry 都有自己独立的随机 salt —— 必须逐条派生 key
解密全程在浏览器本地用 Web Crypto 完成,主密码与明文不离开本机
{
"entries": [
{
"name": "DeepSeek Codex",
"secret": { "salt": "…", "iv": "…", "tag": "…", "data": "…" },
"description": "DeepSeek API key,Codex 用",
"created": "2026-08-07T03:02:09.170Z",
"legacy": false // true = 旧 master 加密,已不可解
}
]
}
VAULT_MASTER(64 位 hex),点「解锁」deepseek、智谱)实时筛选,Esc 清空旧版网页端存在三个叠加的 bug,导致无论输入什么主密码都提示「密码错误」——保险库实际上一直打不开:
| # | Bug | 后果 |
|---|---|---|
| 1 | 读 entry.salt,而真实结构是 entry.secret.salt | 字段全 undefined,必然抛错 |
| 2 | 只用第一条的 salt 派生一个 key 去解全部条目 | 每条 salt 都不同,第 2 条起必失败 |
| 3 | 主密码校验只试第一条 | 第一条若不可解,整库误判「密码错误」 |
v2 的处理:兼容两种数据结构、逐条独立派生 key、校验规则改为「任一条能解开即通过」。
.env 一度缺失,助手直接生成了一个新随机 master 并用它存新 key。结果 8/5–8/6 用旧 master 加密的 3 条(阿里云百炼 / 智谱 / 硅基流动)永久不可解——旧 master 已无副本,64-hex 有 256 bit 熵,不可能爆破。
这 3 条没有被删除,而是标记 "legacy": true 保留:
list 标注「历史·不可解」,get/raw 给友好提示而非抛异常。~/.workbuddy/accounts/vault.legacy-backup-2026-08-07.json,若日后想起旧 master 仍可恢复。清理办法:到对应平台重新获取 key → 让我 add 重存 → delete 掉 legacy 条目。
.env 缺失时绝不擅自新建 master。 正确顺序是——先看 vault.json 是否已有条目;有则立刻停手并索要原 master;确为空库才可生成新的,并把 64-hex 明确交给坤哥备份。重建前必须先备份现有 vault.json。
# 存入
node ~/.workbuddy/skills/account-vault/vault.js add "服务名" "密钥值" "描述"
# 查询(脱敏 + 明文)
node ~/.workbuddy/skills/account-vault/vault.js get 关键词
# 取明文(谨慎)
node ~/.workbuddy/skills/account-vault/vault.js raw 服务名
# 列表 / 删除
node ~/.workbuddy/skills/account-vault/vault.js list
node ~/.workbuddy/skills/account-vault/vault.js delete 服务名
# 同步到线上(打包密文 + 页面 → tar over SSH → ECS)
cd ~/.workbuddy/skills/account-vault && bash sync-accounts.sh
同步脚本内置防泄漏校验:推送后回查服务端 vault.json,命中 sk- 等明文特征会告警。
网页端源码在 ~/.workbuddy/skills/account-vault/web/(index.html / app.js / vault.json),是唯一源,改完必须跑同步脚本,不要直接改 ECS 上的文件。
/accounts/ 是公开 URL,任何人都能下载 vault.json——但只有密文,明文需主密码本地解密。VAULT_MASTER 写进任何对外内容(文章 / 网页 / 聊天转发)。vault.json 单独泄露」(误传云备份 / git),不防「整台机器被拿走」。vault.json 加访问控制(Nginx Basic Auth),降低离线爆破面。设计出发点很直接:坤哥不接受「自己另存到钥匙串/备忘录,需要时自己去翻」的方案——那等于把负担推回给人,和做这套系统的初衷相反。备忘录按时间排序,几个月后照样得翻半天,本质上没解决问题。
所以正确解法不是让人去记,而是让口令本身不会丢。由 scripts/master-guard.sh 在三个物理位置维护副本:
| 位置 | 路径 | 特点 |
|---|---|---|
| [A] 主位 | ~/.workbuddy/skills/account-vault/.env | 技能实际读取处;重装技能有被覆盖风险 |
| [B] 本地独立位 | ~/.vault-keys/master.key | 不随技能目录走,权限 600 |
| [C] 异地位 | ECS /root/.vault-backup/master.key | 在 web 根之外,无任何 URL 可达,权限 600 |
加密数据 vault.json 同时备份到 ECS /root/.vault-backup/data/,保留最近 7 份——只有口令没有数据同样无用。
daily.sh 第 3 步,launchd 每天 08:00 自动执行。bash scripts/master-guard.sh # 自检 + 自愈 + 数据备份
bash scripts/master-guard.sh --show # 只打印当前 master
bash scripts/master-guard.sh --status # 只体检不改动
.env 后运行守护脚本,口令从副本自动恢复,vault.js get deepseek 正常解出明文。
accounts/vault.json 在同一台 ECS 上。这是刻意选择——实际威胁模型是「自己弄丢文件」而非「服务器被攻破」,故优先保可用性。副本置于 /root/ 下(web 根之外 + 权限 600),即使 Nginx 配置出错也不会被 HTTP 暴露。若日后威胁模型改变,可把 [C] 换成独立的第三方存储。