Hermes 学会用你的密码库了:替你登录、填卡、收 2FA 验证码

你让 Hermes 帮你登录公司后台改个配置,它在登录页前停下了。不是因为它不会点,而是因为密码只有你知道。于是你把它复制进聊天框——从这一刻起,这段明文躺在会话记录里、日志里,以及下一轮要发给模型的上下文里。为了省一次点击,你付出的代价是把密码交给了整条链路。
Hermes 刚合并的浏览器凭据库(credential vault)就是冲这件事来的:Agent 自己走到登录页,标识符(邮箱/用户名)它自己敲,密码由 Hermes 在服务端注入页面,模型看到的结果只有一行 {filled_fields: 1, origin: "https://github.com"}。密码不进对话、不进日志、不进会话数据库,也不出现在任何进程的命令行参数里。
它是怎么分工的
整套东西分成两层:一层是存登录信息的库,一层是浏览器里干活的工具。Agent 拿到的始终只是句柄和可见的标识符元数据,真正的密码在填充那一刻才由后端解出来,直接写进页面。
| 工具 | 干什么 |
|---|---|
browser_vault_list |
列出可用的登录项(只有标签、标识符、站点,没有密码) |
browser_vault_save_login |
在登录页上就地保存一条新登录 |
browser_vault_fill |
把某条登录注入当前页面(校验 origin) |
browser_vault_enter_code |
处理二次验证码那一格 |
browser_vault_unlock |
解锁外部密码管理器(1Password / Bitwarden) |
存储落在 ~/.hermes/vault/ 下(Fernet 密钥 + vault 文件,权限都是 0600),并且按 profile 隔离——如果你在用多 profile 干活,每个 profile 的凭据是分开的,这一点和真实 profile 浏览是同一套思路。
第一次:它在登录页当场问你
这里最值得称道的是“零设置”。你不需要先学 hermes vault add,也不用去设置里找个开关——直接说“登录 GitHub”,Agent 走到登录页发现这个站点没有存过登录,就会在你的界面里弹一个掩码提示:
- CLI / TUI:sudo 弹窗那种两步面板,先显示邮箱/用户名(明文可见),再输入密码(不回显)。
- 桌面端:一张“要保存你的 github.com 登录吗?“的卡片,带标注清楚的 Email or username 字段和掩码密码字段,两个按钮:Don’t save / Save & sign in。
你说保存,Hermes 就把它加密、绑定到页面 origin、立刻填进去;你说不保存,返回 save_declined,Agent 这一轮不再追问。密码从始至终没有经过聊天内容——它是弹窗里输入的,不是在对话里打的。
注意这里有个设计细节:这些工具是跟着浏览器工具集走的,而不是“等库里有了东西才出现”。因为库空的时候恰恰是最需要“保存登录”的时候——早先的版本就是把这个功能藏在“库里有项目”后面,结果没人发现它。
2FA:三种情况,三种处理
密码过了还有验证码这道坎。之前 Agent 走到这是真的没辙:登录分类器故意排除了验证码输入框(密码绝不能落进 OTP 框),而第二步又没有对应工具,唯一能做的就是在聊天里问你。
现在按情况分三路:
| 站点要什么 | Hermes 怎么做 |
|---|---|
| 验证码,且保存的登录里带了 authenticator key(本地库,或 1Password / Bitwarden 条目里的 TOTP 种子) | 自己算出当前验证码并填入,不问你 |
| 短信 / 邮件 / App 收到的一次性码,没存 key | 在你的界面弹出小提示(“github.com 的验证码”),你输入,它填入;验证码同样不进对话 |
| Passkey、硬件密钥、App 里点批准 | 没有东西可输;Agent 让你在自己设备上完成,然后等页面继续 |
authenticator key 就是网站开启 2FA 时给的那个“设置密钥”或 otpauth:// 链接。加进去的地方在设置 → Passwords & Logins → Add(可选字段),或者 hermes vault add 里那一步;带 key 的条目会显示 2FA auto 徽章。
填写验证码时它还挺谨慎:只有在页面上的控件确实是“刚好等于验证码位数、每个都是 maxlength=1、在同一个表单里且相邻”时才会逐位输入;否则整串一次性填进去——这样页面上那些促销码、邮编、推荐码之类的输入框不会被数字喷一脸。
已经在用 1Password 或 Bitwarden?什么都不用开
如果你装了 op 或 bw 命令行工具并且已经登录,Hermes 会自动识别它,里面的网站登录项直接变成可填充的句柄,和本地库里的条目并列。句柄按后端加前缀区分(本地是 vault_…,1Password 是 op:…,Bitwarden 是 bw:…),所以工具侧不需要为不同来源改 schema。
第一次用到里面的登录时,它会让你输入主密码解锁——掩码提示,每个会话一次,空闲 30 分钟过期。主密码是交给密码管理器自己的 CLI 的:1Password 走 op signin 的标准输入,Bitwarden 走 bw unlock --passwordenv 的子进程环境变量。Hermes 只在内存里留会话 token,既不落盘也不进环境变量备份,Agent 看不到主密码、token 和任何一条登录。
想干脆不用某个被识别到的管理器:hermes vault sources --disable bitwarden,或者去设置里那个开关。
付款和地址:每一笔刷卡都要你点头
卡和地址的存储方式跟登录一样,也是保存一次、绑定到结账站点、只在该站点填充。区别在确认环节:每一次填卡都会先问你,走的是和危险命令同一套审批提示,你拒绝就什么都不写。地址填充不需要确认。
另外一条边界值得记住:无人值守的会话填不了卡。cron、webhook、API server 这些地方没人能点“确认”,所以这类填充会被直接拒绝——也就是说,一段提示注入哪怕真把 Agent 带到了结账页,它能“问”,但花不出去钱。这套思路和无人值守审批是同一个原则:没人应答的时候,默认拒绝而不是默认放行。
命令行速查
桌面端在设置 → Passwords & Logins里管理一切;命令行这边是四个子命令:
# 交互式添加一条登录 / 卡 / 地址(密码走 getpass,永远不作为命令行参数)
hermes vault add
hermes vault add --kind payment # 支付卡
hermes vault add --kind address # 收货地址
# 列出条目:只有句柄、来源、类型、标签、标识符、origin,没有密码
hermes vault list
# 按句柄删除
hermes vault rm vault_3f9c1a
# 看看哪些密码管理器被识别到了;关闭或重新启用其中一个
hermes vault sources
hermes vault sources --disable onepassword
hermes vault sources --enable onepassword
新登录项的名字(label)、站点 origin、登录标识符是可见元数据——Agent 能读到、能自己敲进页面。密码和卡号不是。
顺带说清一个容易混淆的点:这里的 vault 和 v0.19 那个外部 SecretSource不是一回事。SecretSource 管的是给 Hermes 自己用的 API Key(从 Bitwarden Secrets Manager / 1Password 读进环境变量),配置在 secrets 段;浏览器凭据库管的是网站上的登录、银行卡、地址,配置在 vault 段。名字像,用途完全不同。
没人应答的会话(cron / webhook / API)
这类会话没人能回答提示,行为是明确区分过的:
- 本地已保存的登录:照常可用,不需要提示。
- 被锁住的外部密码管理器:返回
unavailable_in_this_session(这个会话解锁不了)。 - 这个站点没存过登录:返回
prompt_unavailable(没人能回答)。
想让无人值守场景也能用 1Password,可以给它一个 service account token——把它放进 OP_SERVICE_ACCOUNT_TOKEN,就不用弹主密码了:
vault:
onepassword:
enabled: true # 想让检测到的管理器停用就设 false
account: "" # 传给 `op --account` 的账号简写;留空用默认
service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN
bitwarden:
enabled: true
binary_path: "" # bw 的绝对路径;留空走 PATH
它保证什么,不保证什么
值得说清楚,因为“模型看不到密码”这句话很容易被过度解读。
保证:密码不通过 Hermes 进入模型上下文——不在工具结果里、不在日志里、不在会话数据库里,也不在任何进程的 argv 里。填充走的是受管浏览器会话的直接 CDP socket,写之前还会在页面里再校验一遍:页面 origin 必须和保存时完全一致,否则拒绝。
不保证:它防不了页面自己。密码一旦被填进某个网站,那个网站(和它跑的任何脚本)就拿到了它——和你自己手打进去完全一样。用云浏览器后端的话,厂商的浏览器照常能看到页面。origin 绑定防的是“填错站”,不是“防住被攻陷的对站”。
存储本身是本机加密的,这和opt-in 的系统钥匙串加密是两套东西:凭据库用 Fernet 密钥 + 0600 文件,不依赖系统钥匙串。
版本状态:目前只在 main 上
截至写下这篇,这套功能还没有进入任何 tagged 版本:最新的 v0.21.1(tag v2026.9.7,9 月 7 日发布)里没有 agent/vault_backends/、tools/browser_vault_tool.py 和 hermes_cli/vault.py 这些文件,它们是在 9 月 10 日合并进 main 的。
所以:
- 用
hermes update升到 v0.21.1,还拿不到这套能力,要等下一个 tagged 版本把它带上。 - 想现在就用,需要跟踪
main(git 安装方式),并接受主干代码的日常波动。
不管哪种情况,重装或升级之后的第一步都是同一句话:让 Agent 去登录某个网站,然后看它会不会在登录页前,第一次问你要不要把这个登录存下来。