Hermes Agent 与 Codex 设置 `X-Edge-Token` 的最新做法
/research/hermes-codex-edge-token-config.md
调研时间:2026-06-23 20:02 CST 结论先行:Codex 用 provider 级
env_http_headers;Hermes 当前稳定入口是全局model.default_headers。两边都建议把 token 放环境变量,不要明文写进主配置。
这类配置一般用于把 OpenAI-compatible endpoint 放到公网 edge / WAF / 反代后面:上游模型仍然用正常的 Authorization header 做模型鉴权,edge 额外要求 X-Edge-Token 才放行。不要把 X-Edge-Token 塞进 Authorization,否则会和模型 API key / OAuth bearer 冲突。
当前版本与证据
本机验证到的版本:
- Hermes Agent:
v0.17.0 (2026.6.19),本地源码b88d0007c,远端origin/main为5ecf3bf0e(2026-06-23)。本机提示落后 upstream 382 commits,所以本文同时看了本地代码和最新origin/main。 - Codex CLI:本机
0.141.0;npm 最新查询为0.142.0。
证据来源:
- OpenAI Codex 官方文档
Advanced Configuration明确给出:
[model_providers.example]
http_headers = { "X-Example-Header" = "example-value" }
env_http_headers = { "X-Example-Features" = "EXAMPLE_FEATURES" }
即 Codex 支持 provider 级静态 header 和环境变量 header。
- Codex 官方 sample config 中,model provider 示例仍以
wire_api = "responses"为唯一支持值,并示例http_headers/env_http_headers。 - Hermes 官方配置文档确认:
config.yaml支持${VAR_NAME}环境变量替换;未设置的变量会原样保留;密钥类内容应放.env。 - Hermes 最新
origin/main源码确认:model.default_headers会覆盖 provider / SDK 默认 header,并应用到主 OpenAI client 与辅助 client。关键实现点在:agent/agent_init.py:注释写明model.default_headers in config.yamloverride provider/SDK defaults。agent/auxiliary_client.py:_apply_user_default_headers()从cfg_get(load_config(), "model", "default_headers")读取并合并 header。hermes_cli/config.py:_expand_env_vars()递归展开${VAR}。
Codex:推荐配置
Codex 的正确入口是 ~/.codex/config.toml 里的 [model_providers.<id>]。
推荐用环境变量版本:
model = "your-model-id"
model_provider = "edge-model"
[model_providers.edge-model]
name = "Edge protected OpenAI-compatible provider"
base_url = "https://edge.example.com/v1"
wire_api = "responses"
# 上游模型 API key:仍走 Authorization bearer,用 EDGE_MODEL_API_KEY 提供
env_key = "EDGE_MODEL_API_KEY"
# Edge / WAF gate:额外 header,不和 Authorization 混用
env_http_headers = { "X-Edge-Token" = "EDGE_TOKEN" }
然后在运行 Codex 的环境里设置:
export EDGE_MODEL_API_KEY='sk-...'
export EDGE_TOKEN='edge-token-...'
如果只跑 CLI,这样就够。若是 Codex App / LaunchAgent / systemd / 桌面启动器,关键是启动 Codex 的那个进程环境必须能看到 EDGE_TOKEN。常见做法:
- macOS zsh:放进
~/.zshenv,不要只放~/.zshrc。 - Linux systemd user service:用
EnvironmentFile=或systemctl --user import-environment EDGE_TOKEN EDGE_MODEL_API_KEY。 - 临时 smoke test:在同一个 shell 里 export 后直接跑
codex exec ...。
不推荐但可用的明文版本:
[model_providers.edge-model]
name = "Edge protected OpenAI-compatible provider"
base_url = "https://edge.example.com/v1"
wire_api = "responses"
env_key = "EDGE_MODEL_API_KEY"
http_headers = { "X-Edge-Token" = "edge-token-..." }
明文版本适合一次性本地试验,不适合长期配置。
Codex 需要注意的坑
- 当前 custom provider 应用
wire_api = "responses"。 旧的chat路径在新 Codex 版本里已经不可靠,0.141 时代已经会拒绝或不推荐。 - Header 名大小写不要写死校验。 Codex / HTTP client 可能把
X-Edge-Token发成x-edge-token;HTTP header 本来大小写不敏感,edge 端要 case-insensitive 读取。 - 不要把 token 写到 repo 的
.codex/config.toml。 Codex 支持项目级.codex/config.toml,但 token 这种东西只放用户 home 或环境变量。 - 如果 provider 由反代注入上游 auth,就不要设置
env_key。 否则 Codex 会先检查 env var,可能还没打到 proxy 就失败。
Hermes Agent:推荐配置
Hermes 当前稳妥入口是 ~/.hermes/config.yaml 的:
model:
provider: custom
default: your-model-id
base_url: https://edge.example.com/v1
api_key: ${EDGE_MODEL_API_KEY}
default_headers:
X-Edge-Token: ${EDGE_TOKEN}
密钥放 ~/.hermes/.env:
EDGE_MODEL_API_KEY=sk-...
EDGE_TOKEN=edge-token-...
本机路径:
- Hermes config:
/home/geekinney/.hermes/config.yaml - Hermes env:
/home/geekinney/.hermes/.env
Hermes 文档没有把 default_headers 当成高层用户配置重点展开,但最新源码已经明确支持;并且不是只打主请求,辅助调用(标题、压缩、vision routing 等 OpenAI-compatible client)也会合并这个 header。
Hermes 的作用域限制
当前 model.default_headers 是全局 model 级,不是 provider-scoped。也就是说:
- 如果当前 Hermes 只指向一个 edge-protected custom endpoint,直接用它最简单。
- 如果同一个 Hermes config 里有多个 provider,其中只有一个需要
X-Edge-Token,要小心:全局 header 可能被带到其他 OpenAI-wire provider。通常不致命,但不是最干净的设计。 - 对
anthropic_messages/bedrock_converse这类非 OpenAI client 路径,不走同一套 header 合并。
如果后续 Hermes 增加 providers.<id>.env_http_headers 这种 provider-scoped 入口,再迁过去会更优雅;截至这次调研,最新源码里可靠入口仍是 model.default_headers。
建议的统一变量命名
如果同一个 edge 给 Hermes 和 Codex 共用,变量建议统一:
EDGE_MODEL_API_KEY='sk-...'
EDGE_TOKEN='edge-token-...'
EDGE_BASE_URL='https://edge.example.com/v1'
EDGE_MODEL_ID='your-model-id'
Codex env_http_headers 只接受“header 名 → 环境变量名”的映射;Hermes config.yaml 则可以直接写 ${EDGE_TOKEN}。这两个语法不要混用。
最小 smoke test
最靠谱的验证不是“配置能加载”,而是 edge / capture server 是否真实收到了 header。
1. 用 curl 验 edge 本身
curl -fsS "$EDGE_BASE_URL/models" \
-H "X-Edge-Token: $EDGE_TOKEN"
如果上游 provider 还需要模型认证,再按它的要求额外添加认证 header。预期:带 X-Edge-Token 时返回模型列表或 provider 自己的 models JSON。
再测无 token:
curl -i "$EDGE_BASE_URL/models"
预期:401 / 403 / edge 自定义拒绝。否则 X-Edge-Token 没有起到 gate 作用。
2. Codex smoke test
EDGE_MODEL_API_KEY='sk-...' EDGE_TOKEN='edge-token-...' \
codex exec --skip-git-repo-check --cd /tmp \
--output-last-message /tmp/codex-edge-smoke.last \
'Reply exactly CODEX_EDGE_OK. Do not run commands.'
cat /tmp/codex-edge-smoke.last
预期输出:CODEX_EDGE_OK。
如果失败,优先看:
EDGE_TOKEN是否在同一进程环境;- provider id /
model_provider是否指向这段配置; wire_api = "responses";- edge 是否大小写敏感地读取 header;
- 上游是否真的支持
/v1/responses,不支持就需要 compatibility proxy。
3. Hermes smoke test
EDGE_MODEL_API_KEY='sk-...' EDGE_TOKEN='edge-token-...' \
hermes chat -q 'Reply exactly HERMES_EDGE_OK. Do not call tools.' \
--provider custom \
--model your-model-id
如果 Hermes 是 gateway / systemd 跑起来的,不要只在交互 shell export;要确认 hermes-gateway.service 的启动环境能读取 .env 或 systemd EnvironmentFile。配置变更后通常需要新 session;gateway 长驻进程如果已启动,改 model/provider/header 后需要按实际情况重启 gateway 才会加载。
推荐最终方案
对老大的环境,我建议这样落地:
- Edge token 只作为第二道门禁,保留上游
Authorization的模型 API key / OAuth bearer。 - Codex:在
~/.codex/config.toml的目标 provider 下加:
env_http_headers = { "X-Edge-Token" = "EDGE_TOKEN" }
- Hermes:在
~/.hermes/config.yaml的model.default_headers加:
default_headers:
X-Edge-Token: ${EDGE_TOKEN}
- 密钥统一放到
~/.hermes/.env/ shell env / systemd EnvironmentFile;不要把真实 token 写进 BuJo、repo、聊天记录或config.toml明文字段。 - 配完一定用
curl + Codex exec + Hermes chat三段验证。只看到配置文件里有字段,不算成功。
可直接复制的模板
~/.codex/config.toml
model = "your-model-id"
model_provider = "edge-model"
[model_providers.edge-model]
name = "Edge protected provider"
base_url = "https://edge.example.com/v1"
wire_api = "responses"
env_key = "EDGE_MODEL_API_KEY"
env_http_headers = { "X-Edge-Token" = "EDGE_TOKEN" }
~/.hermes/config.yaml
model:
provider: custom
default: your-model-id
base_url: https://edge.example.com/v1
api_key: ${EDGE_MODEL_API_KEY}
default_headers:
X-Edge-Token: ${EDGE_TOKEN}
.env / shell
EDGE_MODEL_API_KEY=sk-...
EDGE_TOKEN=edge-token-...
一句话判断
Codex 这边已经是正式文档支持的 provider 级 header;Hermes 这边是源码已支持但文档不显眼的全局 model header。实际部署时,把 X-Edge-Token 当作 edge gate,不要当模型 API key;配完抓真实请求或至少跑一次端到端 smoke test。