1. Dashboard 功能 YML 配置
环境变量
| 变量 | 默认值 | 说明 |
|---|---|---|
HERMES_DASHBOARD | (未设置) | 设为 1 启用 s6 监督下的 dashboard 服务 |
HERMES_DASHBOARD_HOST | 0.0.0.0 | 监听地址。bridge 网络下必须用 0.0.0.0 才能被 ghost_net 内其他容器访问 |
HERMES_DASHBOARD_PORT | 9119 | 监听端口 |
HERMES_DASHBOARD_INSECURE | (未设置) | 1 = 信任上游认证(nginx/oauth2-proxy),跳过 dashboard 自检;0/不设 = 开启自查模式,必须配 DashboardAuthProvider 否则拒绝启动 |
HERMES_DASHBOARD_TUI | 0 | 1 启用浏览器内嵌 CLI 终端(Chat 标签页) |
INSECURE 参数本质
--insecure 是 Hermes dashboard 自身的 CLI 参数,语义是"我承认不安全,上游已认证"。s6 脚本:
case "${HERMES_DASHBOARD_INSECURE:-}" in
1|true|TRUE|...) insecure="--insecure" ;;
# =0 不匹配任何条件,等同于没设
esac
| 设值 | 效果 | 适用场景 |
|---|---|---|
HERMES_DASHBOARD_INSECURE=1 | 传 --insecure,门禁关闭 | nginx + oauth2-proxy 已做认证,dashboard 信任上游 |
HERMES_DASHBOARD_INSECURE=0 | 不传 --insecure,门禁开启 | 需要 DashboardAuthProvider(如 Nous Portal OAuth),否则拒绝启动 |
| 不设 | 同 =0 | 同上 |
你的场景:nginx + oauth2-proxy 已在 nginx 层完成认证,dashboard 不需要再做一次,所以 =1 正确。
Docker Compose 配置
services:
hermes-agent:
image: nousresearch/hermes-agent:v2026.5.29.2
command: gateway run
environment:
- HERMES_DASHBOARD=1
- HERMES_DASHBOARD_TUI=1
- HERMES_DASHBOARD_INSECURE=1
- HERMES_DASHBOARD_HOST=0.0.0.0
networks:
- ghost_net
CLI 链路架构
┌────────────────────┐ ┌──────────────────────┐ ┌───────────────┐
│ Browser │ WebSocket │ Dashboard Server │ PTY │ Hermes TUI │
│ Dashboard Tab │ ─────────────────────> │ /api/pty │ ────────────────> │ 子进程 │
│ (xterm.js) │ <───────────────────── │ port 9119 │ <──────────────── │ CLI 完整 │
│ WebGL 渲染 │ ANSI 输出流 │ s6 监督 │ ANSI 输出 │ slash命令 │
└────────────────────┘ └──────────────────────┘ └───────────────┘
说明:
Chat 标签页 = 浏览器内完整 Hermes CLI
HERMES_DASHBOARD_TUI=1 额外 fork 一个 hermes --tui PTY 子进程(约 +200MB 内存)
Docker 镜像已预装 Node.js 22 + ptyprocess,开箱可用
2. OAuth 2.0 全链路配置
部署架构
所有容器在同一个 bridge 网络(ghost_net)内,Nginx 是唯一对外暴露点。容器间用容器名+端口互访,不设 ports 映射。
公网 → Nginx(Docker, 唯一暴露点443) ─ ghost_net ─┬─ hermes-agent:9119 (dashboard)
├─ hermes-oauth:4180 (oauth2-proxy)
└─ 其他内部服务...
认证链路
┌──────────┐ HTTPS :443 ┌──────────┐ auth_request ┌──────────────┐ HTTP ┌────────────────┐
│ Browser │ ─────────────> │ Nginx │ ────────────────> │ oauth2-proxy │ ───────> │ Hermes Agent │
│ │ (ghost_net) │ │ <──────────────── │ :4180 │ │ Dashboard:9119 │
│ │ │ │ 401 → /sign_in │ + Google │ │ │
│ │ │ │ │ OAuth App │ │ │
└──────────┘ └──────────┘ └──────────────┘ └────────────────┘
流程:
① 用户访问 dashboard → nginx 触发 auth_request
② oauth2-proxy 检测无会话 → 302 重定向到 Google 登录
③ 用户在 Google 输入账号密码 → Google 回调 oauth2-proxy
④ oauth2-proxy 校验邮箱白名单 → 通过则设置会话 cookie
⑤ 后续请求 auth_request 通过 → nginx 透传到 dashboard:9119
⑥ dashboard 自身 INSECURE=1 → 信任上游,不再二次验证
变量来源对照
| 参数 | 值来源 | 说明 |
|---|---|---|
--client-id | Google Cloud 创建 OAuth App 时提供 | 格式 xxx.apps.googleusercontent.com |
--client-secret | Google Cloud 创建 OAuth App 时提供 | 弹窗显示一次,立即保存 |
--cookie-secret | 自己生成 | python3 -c "import secrets,base64; print(base64.urlsafe_b64encode(secrets.token_bytes(32)).decode())" |
--redirect-url | 你的域名 | 格式 https://你的域名/oauth2/callback,必须与 Google Cloud 授权重定向 URI 一致 |
--upstream | 你的网络 | http://hermes-agent:9119(ghost_net 内部容器名互访) |
--authenticated-emails-file | 你自己管理 | 指向 emails.txt,加第二个人只改这个文件即可 |
前提:Google Cloud 注册 OAuth App
- 访问 https://console.cloud.google.com/apis/credentials
- 创建 OAuth 2.0 Client ID,应用类型 Web application
- 名称随意(如 "Hermes Dashboard")
- 授权重定向 URI:
https://你的域名/oauth2/callback - 点创建,复制 Client ID + Client Secret(只显示一次,立即保存)
Nginx 配置(Docker 内部)
server {
listen 443 ssl;
server_name hermes.atibm.com;
ssl_certificate /etc/letsencrypt/live/atibm.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/atibm.com/privkey.pem;
# 认证校验:请求先发给 oauth2-proxy(容器名互访)
auth_request /oauth2/auth;
error_page 401 = /oauth2/sign_in;
# dashboard 转发
location / {
proxy_pass http://hermes-agent:9119;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
# oauth2-proxy 回调端点(容器名互访)
location /oauth2/ {
proxy_pass http://hermes-oauth:4180;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Docker Compose 配置
services:
hermes-agent:
image: nousresearch/hermes-agent:v2026.5.29.2
command: gateway run
environment:
- HERMES_DASHBOARD=1
- HERMES_DASHBOARD_TUI=1
- HERMES_DASHBOARD_INSECURE=1 # 信任上游 nginx 认证
- HERMES_DASHBOARD_HOST=0.0.0.0
networks:
- ghost_net
hermes-oauth:
image: quay.io/oauth2-proxy/oauth2-proxy:latest-alpine
container_name: hermes-oauth
restart: unless-stopped
networks:
- ghost_net
# 不设 ports:nginx 在同一网络,通过容器名 hermes-oauth:4180 访问
volumes:
- ./data/oauthemails.txt:/etc/oauth2-proxy/emails.txt:ro
command:
- --provider=google
- --client-id=你的_CLIENT_ID
- --client-secret=你的_CLIENT_SECRET
- --cookie-secret=生成的32字节base64
- --redirect-url=https://hermes.atibm.com/oauth2/callback
- --upstream=http://hermes-agent:9119
- --email-domain=*
- --authenticated-emails-file=/etc/oauth2-proxy/emails.txt
- --pass-authorization-header=true
- --set-xauthrequest=true
networks:
ghost_net:
external: true
邮箱白名单文件 emails.txt
you@gmail.com
zhangsan@gmail.com
加第二个用户:只改 emails.txt 加一行邮箱即可,不需要去 Google Cloud 后台修改任何东西。第二个人用自己的日常 Google 账号登录,不需要是开发者。
重要说明
- dashboard 无用户概念:不同用户登录后看到的是同一个 dashboard、同一份会话列表、同一个 Chat 终端。认证只解决"谁能进",不解决"谁做了什么"
- oauth2-proxy 与 Hermes 内置 OAuth 门禁是不同层面的事:oauth2-proxy 在 nginx 层做认证拦截;
HERMES_DASHBOARD_INSECURE=1是 dashboard 信任上游不做二次检查。两者配合使用才是正确姿势 - 容器名互访:nginx 在 ghost_net 内时,用
hermes-oauth:4180而非127.0.0.1:4180,不需要ports映射
3. API Server 功能 YML 配置
架构图
┌────────────────────┐ POST /v1/chat/completions ┌────────────────────┐
│ 第三方前端 │ ────────────────────────────> │ Hermes Agent │
│ Open Web UI │ Authorization: Bearer *** │ API Server │
│ LobeChat │ <──────────────────────────── │ port 8642 │
│ LibreChat │ 流式响应 + tool call 进度 │ 完整工具集 │
└────────────────────┘ │ (终端/文件/搜索) │
└────────────────────┘
环境变量
| 变量 | 说明 |
|---|---|
API_SERVER_ENABLED=true | 启用 OpenAI 兼容 API |
API_SERVER_KEY=your-api-key | Bearer token 认证(必须) |
API_SERVER_HOST | 默认 127.0.0.1,对外暴露需设为 0.0.0.0 |
API_SERVER_PORT | 默认 8642 |
API_SERVER_CORS_ORIGINS | 浏览器直连时必设,限制允许的来源 |
Docker Compose 配置
environment:
- API_SERVER_ENABLED=true
- API_SERVER_KEY=your-api-key
# - API_SERVER_HOST=0.0.0.0 # 如需外部访问
# - API_SERVER_CORS_ORIGINS=http://localhost:3000
- 端口 8642,与 Dashboard 端口 9119 独立,互不依赖
- API Server 不启动不影响 Dashboard 任何功能
- API Server 暴露完整工具集(含终端),必须设置
API_SERVER_KEY