1Password
在进程启动时从 1Password 解析提供商 API key,而不是把它们以明文存放在 ~/.hermes/.env 里。你把 key 作为 1Password 条目保存,通过 op://vault/item/field 引用它;轮换一个凭据就变成在 1Password 里改一处。
工作原理
- 你安装官方 1Password CLI(
op)并完成认证——要么用服务账号 token(无头服务器),要么用交互式/桌面会话(你的笔记本)。 - 你在
~/.hermes/config.yaml中把环境变量名映射到op://引用。 - 每次
hermes(或网关、或 cron 任务)启动时,在~/.hermes/.env加载之后,Hermes 为每个引用运行op read,把解析出的值写进os.environ。 - 默认情况下 Hermes 会覆盖环境中已有的值,因此 1Password 是事实来源——轮换一次凭据,每个 Hermes 进程在下次启动时都会拿到。若想让
.env胜出,把override_existing翻转为false。
Hermes 绝不会代替你认证,也绝不下载 op:它只是调用你已安装、已信任的 CLI。如果 op 缺失、会话被锁定或引用有误,Hermes 会打印一行警告,然后用 .env 里已有的凭据继续——它永远不会阻塞启动。
认证
op 支持两种适合非交互的模式;Hermes 两种都能用:
- 服务账号(服务器/CI 推荐):在 1Password 中创建一个服务账号,授予它对相关保险库的读权限,并把它的 token 作为
OP_SERVICE_ACCOUNT_TOKEN导出到~/.hermes/.env。这个 token 就是凭据——像对待任何其他 bearer token 一样对待它。 - 桌面 / 交互式会话(笔记本):运行
op signin(或在 1Password 应用中启用 CLI 集成)。Hermes 会把你的OP_SESSION_*变量透传给op子进程。1Password 的缓存键包含这些会话变量,因此登录到另一个账号绝不会返回上一个身份下缓存的值。
引导 token
当你用服务账号 token 认证时,这个 token 本身就是 Hermes 在能解析任何 op:// 引用之前所需的引导凭据。它必须存在于每个解析机密的进程的 os.environ 中——包括 cron 任务(kanban.dispatch_in_gateway: false)、子进程调用、CLI 运行、macOS launchd agent 和 Docker 容器——而不仅仅是交互式网关。有三种方式让它可用,按优先级排序:
-
放在
~/.hermes/.env(推荐)。hermes secrets onepassword setup --token <token>会把 token 写入~/.hermes/.env,与 Bitwarden 的BWS_ACCESS_TOKEN完全一样。因为load_hermes_dotenv()总会加载.env,这个 token 零额外配置即可处处可用。这是最简单可靠的选项。 -
放在
~/.hermes/.op.env(已 gitignore)。 如果你更愿意把服务账号 token 排除在.env之外——例如让.env可以签入私有 dotfiles 仓库,而 token 不进版本控制——就把它放到~/.hermes/.op.env:echo 'OP_SERVICE_ACCOUNT_TOKEN=ops_...' > ~/.hermes/.op.env chmod 600 ~/.hermes/.op.envHermes 启动时会在
.env之后自动加载.op.env,并且绝不覆盖环境中已存在的 token。.op.env已被 gitignore,因此 token 永远不会进入被提交的文件。 -
通过 systemd
EnvironmentFile(Linux 网关)。 如果你在 systemd 下运行网关,可以直接把 token 注入服务环境:[Service] EnvironmentFile=-/home/youruser/.hermes/.op.env这样注入的 token 优先级最高——Hermes 检测到
OP_SERVICE_ACCOUNT_TOKEN已设置,就会完全跳过加载.op.env。
如果 token 只能通过交互式 shell 获得(op signin、.bashrc 里的 OP_SESSION_* 导出等),它不会被 cron 任务或新派生的子进程继承,那些上下文会记录一条警告并回退到 .env 已持有的凭据。对任何非交互式工作负载,请使用上面三种方式之一。
设置
1. 安装并登录 op
按 1Password CLI 上手指南操作。验证它可用:
op whoami
2. 启用集成
hermes secrets onepassword setup
这会验证 op 在 PATH 上(或用 --binary-path),记录你的账号/token 设置,检查是否有活跃会话,并把 secrets.onepassword.enabled 翻为 true。非交互参数:
hermes secrets onepassword setup \
--account my.1password.com \
--token-env OP_SERVICE_ACCOUNT_TOKEN \
--token "$OP_SERVICE_ACCOUNT_TOKEN"
3. 映射你的凭据
引用格式为 op://<vault>/<item>/<field>:
hermes secrets onepassword set OPENAI_API_KEY "op://Private/OpenAI/api key"
hermes secrets onepassword set ANTHROPIC_API_KEY "op://Private/Anthropic/credential"
4. 预览并确认
hermes secrets onepassword sync # dry-run:现在解析,展示将应用什么
hermes secrets onepassword status # 配置 + 二进制 + 引用 + 认证
从现在起,每次 hermes 调用都会在启动时解析这些引用。某个进程首次应用机密时,你会在 stderr 看到一行摘要。
CLI
| 命令 | 作用 |
|---|---|
hermes secrets onepassword setup | 验证 op、设置账号/token 环境变量、启用 |
hermes secrets onepassword status | 显示配置、二进制、认证和已配置的引用 |
hermes secrets onepassword token | 轮换服务账号 token:用 op whoami 校验,然后存入 .env |
hermes secrets onepassword set ENV_VAR "op://…" | 把一个环境变量映射到引用(存储时会剥离并校验) |
hermes secrets onepassword remove ENV_VAR | 移除一个映射 |
hermes secrets onepassword sync | Dry-run:现在解析引用并展示将应用什么 |
hermes secrets onepassword sync --apply | 解析并导出到当前 shell 环境 |
hermes secrets onepassword disable | 把 enabled 翻为 false;保留映射不动 |
op 和 1password 可作为 onepassword 的别名。
配置
~/.hermes/config.yaml 中的默认值:
secrets:
onepassword:
enabled: false
env:
OPENAI_API_KEY: "op://Private/OpenAI/api key"
ANTHROPIC_API_KEY: "op://Private/Anthropic/credential"
account: ""
service_account_token_env: OP_SERVICE_ACCOUNT_TOKEN
binary_path: ""
cache_ttl_seconds: 300
override_existing: true
| 键 | 默认值 | 作用 |
|---|---|---|
enabled | false | 总开关。为 false 时绝不调用 op。 |
env | {} | 环境变量名 → op://vault/item/field 引用的映射。名称不是合法环境变量名、或值不是 op:// 引用的条目会被跳过并警告。 |
account | "" | 作为 op read --account 传入的账号简写/登录地址。留空则用 op 的默认账号。 |
service_account_token_env | OP_SERVICE_ACCOUNT_TOKEN | Hermes 从中读取服务账号 token 的环境变量。它的值会作为 OP_SERVICE_ACCOUNT_TOKEN(op 期望的名字)导出给 op 子进程。不设置该变量则使用桌面/交互式会话。 |
binary_path | "" | op 的绝对路径。设置后按原样使用,不查 PATH——固定它,以免信任 PATH 上最先出现的任何 op。 |
cache_ttl_seconds | 300 | 解析值复用多久(进程内和磁盘上)。设为 0 可同时禁用两层缓存——完全不向磁盘写任何值。 |
override_existing | true | 为 true 时,解析值覆盖环境中已有的任何东西(从而使轮换生效)。翻为 false 则让 .env/shell 导出胜出;这些引用会在调用 op 之前被跳过。 |
失败模式
1Password 永远不会阻塞 Hermes 启动。出任何问题时,你会在 stderr 看到一行警告,Hermes 继续运行:
| 症状 | 原因 | 修复 |
|---|---|---|
the op CLI was not found on PATH | 未安装 op/不在 PATH 上 | 安装 CLI,或设置 secrets.onepassword.binary_path |
op read failed for 'op://…': … | 会话锁定、token 过期或无保险库访问权限 | op signin,运行 hermes secrets onepassword token 轮换服务账号 token,或授予服务账号访问权限 |
op read returned an empty value for 'op://…' | 引用的字段存在但为空 | 在 1Password 中修正条目/字段(空值绝不会被应用——你已有的环境变量保持原样) |
… is not an op:// secret reference | 某个映射值不是 op:// 引用 | 用正确的 op://vault/item/field 形式重新设置 |
op read timed out | 网络受阻或 1Password 缓慢 | 检查连通性/桌面应用集成 |
启动警告现在会附带一行 → 修复提示,告诉你具体用哪条命令修复。
缓存
成功且完整的拉取会缓存在进程内以及磁盘上的 <hermes_home>/cache/op_cache.json(原子写入,模式 0600),因此连续多个短生命周期的 hermes 调用不会为每个引用都重新 shell 调用 op。该缓存:
- 只保存解析出的机密值——绝不保存服务账号 token 或任何原始认证材料(认证信息被指纹化进缓存键);
- 当 token、账号、
OP_SESSION_*变量或引用集合变化时失效; - 当一次拉取中任何引用出错时不写入,因此瞬时的认证失败不会被冻结整个 TTL;
- 当
cache_ttl_seconds: 0时完全禁用——读取和写入都禁用。
安全说明
- 一个 1Password 服务账号 token 可以读取该账号有权访问的每一个机密。把它存放在
~/.hermes/.env(不是config.yaml),泄露后从 1Password 吊销并重新生成。 - 即使
override_existing: true,Hermes 也拒绝让解析值覆盖 token 环境变量本身。 op子进程只拿到一份最小的白名单环境(认证/会话变量 +PATH/HOME,以及op配置位置变量OP_CONFIG_DIR/XDG_CONFIG_HOME),而不是完整os.environ的副本,因此 dotenv 之后的提供商凭据不会全部被子进程继承。当~/.config对 Hermes 用户不可写时(容器中常见),请设置OP_CONFIG_DIR。- 引用会被校验为以
op://开头,且引用在--选项终止符之后传递,因此伪造的值不会被解析成op的标志。
何时不要用它
- 单机个人设置,
~/.hermes/.env就够了的场景。 - 无法连接 1Password 的离线隔离环境。
- CI/CD,那里已经接好了现成的机密注入机制——选一条路径,别用两条。
它的理想场景是多机机群、共享开发机、网关 VPS,或任何你想跨多套 Hermes 安装集中轮换与吊销的地方。