← 返回公开文章

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 冲突。

当前版本与证据

本机验证到的版本:

证据来源:

  1. 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。

  1. Codex 官方 sample config 中,model provider 示例仍以 wire_api = "responses" 为唯一支持值,并示例 http_headers / env_http_headers。
  2. Hermes 官方配置文档确认:config.yaml 支持 ${VAR_NAME} 环境变量替换;未设置的变量会原样保留;密钥类内容应放 .env。
  3. Hermes 最新 origin/main 源码确认:model.default_headers 会覆盖 provider / SDK 默认 header,并应用到主 OpenAI client 与辅助 client。关键实现点在:
    • agent/agent_init.py:注释写明 model.default_headers in config.yaml override 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。常见做法:

不推荐但可用的明文版本:

[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 需要注意的坑

  1. 当前 custom provider 应用 wire_api = "responses"。 旧的 chat 路径在新 Codex 版本里已经不可靠,0.141 时代已经会拒绝或不推荐。
  2. Header 名大小写不要写死校验。 Codex / HTTP client 可能把 X-Edge-Token 发成 x-edge-token;HTTP header 本来大小写不敏感,edge 端要 case-insensitive 读取。
  3. 不要把 token 写到 repo 的 .codex/config.toml。 Codex 支持项目级 .codex/config.toml,但 token 这种东西只放用户 home 或环境变量。
  4. 如果 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 文档没有把 default_headers 当成高层用户配置重点展开,但最新源码已经明确支持;并且不是只打主请求,辅助调用(标题、压缩、vision routing 等 OpenAI-compatible client)也会合并这个 header。

Hermes 的作用域限制

当前 model.default_headers 是全局 model 级,不是 provider-scoped。也就是说:

如果后续 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。

如果失败,优先看:

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 才会加载。

推荐最终方案

对老大的环境,我建议这样落地:

  1. Edge token 只作为第二道门禁,保留上游 Authorization 的模型 API key / OAuth bearer。
  2. Codex:在 ~/.codex/config.toml 的目标 provider 下加:
   env_http_headers = { "X-Edge-Token" = "EDGE_TOKEN" }
  1. Hermes:在 ~/.hermes/config.yaml 的 model.default_headers 加:
   default_headers:
     X-Edge-Token: ${EDGE_TOKEN}
  1. 密钥统一放到 ~/.hermes/.env / shell env / systemd EnvironmentFile;不要把真实 token 写进 BuJo、repo、聊天记录或 config.toml 明文字段。
  2. 配完一定用 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。