字节笔记本
2026年6月21日
hermes教程-Web 仪表盘
快速开始
hermes dashboard这会启动一个本地 Web 服务器,并在浏览器中打开 http://127.0.0.1:9119。仪表盘完全运行在你的机器上——没有数据离开 localhost。
选项
| 标志 | 默认值 | 描述 |
|---|---|---|
--port | 9119 | Web 服务器运行的端口 |
--host | 127.0.0.1 | 绑定地址 |
--no-open | — | 不自动打开浏览器 |
--insecure | off | 允许绑定到非 localhost 主机(危险——会在网络上暴露 API 密钥;需配合防火墙和强认证使用) |
--isolated | off | 当从命名配置文件启动时(worker dashboard),运行一个专用的按配置文件服务器,而不是路由到机器仪表盘 |
## 自定义端口
hermes dashboard --port 8080
## 绑定到所有接口(在共享网络上谨慎使用)
hermes dashboard --host 0.0.0.0
## 启动时不打开浏览器
hermes dashboard --no-open管理多个配置文件
仪表盘是一个机器级别的管理界面:一个服务器管理机器上的每个配置文件。侧边栏中的配置文件切换器(当存在多个配置文件时可见)决定管理页面读取和写入哪个配置文件——配置、API 密钥、技能、MCP、模型和聊天标签页都遵循它。当选择了除仪表盘自身配置文件以外的其他配置文件时,会显示一个琥珀色横幅,标明被管理的配置文件,从而明确写入目标。
选择项存在于 URL 中(?profile=<name>),因此像 http://127.0.0.1:9119/skills?profile=worker 这样的深度链接会预先选择切换器,并在刷新后保持不变。
从配置文件别名启动仪表盘会路由到机器仪表盘,而不是启动第二个服务器:
worker dashboard
## → 已运行:在浏览器中打开 ?profile=worker
## → 未运行:启动机器仪表盘,并预先选择 "worker"传递 --isolated 可选择退出,并运行一个限定于该配置文件的专用服务器(统一前的行为——如果你有意用不同的认证暴露不同配置文件的仪表盘,这很有用)。
聊天标签页也跟随切换器:一个限定范围的聊天会使用所选配置文件的 HERMES_HOME 生成其 PTY 子进程,因此对话使用该配置文件的模型、技能、内存和会话历史运行。切换配置文件会启动一个新的终端会话。
哪些内容保持按配置文件独立,且不被切换器吸收:网关进程(通过 hermes -p <name> gateway … 管理)、每个配置文件的会话数据库以及 cron 调度器(Cron 页面已经通过自己的过滤器跨配置文件聚合)。
前提条件
默认的 hermes-agent 安装不包含 HTTP 栈或 PTY 辅助程序——这些是可选的附加组件。Web 仪表盘需要 FastAPI 和 Uvicorn(web 附加组件)。聊天标签页还需要 ptyprocess 来在伪终端后面生成嵌入式 TUI(POSIX 上的 pty 附加组件)。同时安装两者:
pip install 'hermes-agent[web,pty]'web 附加组件会拉取 FastAPI/Uvicorn;pty 会拉取 ptyprocess(POSIX)或 pywinpty(原生 Windows——注意嵌入式 TUI 本身仍然需要 WSL)。pip install hermes-agent[all] 包含这两个附加组件,如果你还需要消息/语音等功能,这是最简单的路径。
当你运行 hermes dashboard 而没有安装依赖时,它会告诉你需要安装什么。如果前端尚未构建且 npm 可用,它会在首次启动时自动构建。
聊天标签页是每次 hermes dashboard 启动的一部分——嵌入式浏览器聊天面板(通过 PTY/WebSocket 运行 TUI)始终可用,无需额外标志。
页面
状态
登录页面显示你安装的实时概览:
- Agent 版本和发布日期
- 网关状态——运行/停止、PID、已连接平台及其状态
- 活跃会话——过去 5 分钟内活跃的会话数
- 最近会话——最近 20 个会话的列表,包含模型、消息数、令牌使用量和对话预览
状态页面每 5 秒自动刷新一次。
聊天
聊天标签页将完整的 Hermes TUI(与 hermes --tui 相同的界面)直接嵌入到浏览器中。你在终端 TUI 中能做的一切——斜杠命令、模型选择器、工具调用卡片、Markdown 流式输出、clarify/sudo/approval 提示、皮肤主题——在这里都完全相同,因为仪表盘正在运行真正的 TUI 二进制文件,并通过 xterm.js 及其 WebGL 渲染器渲染其 ANSI 输出,实现像素完美的单元格布局。
工作原理:
/api/pty打开一个经过仪表盘会话令牌认证的 WebSocket- 服务器在 POSIX 伪终端后面生成
hermes --tui - 按键传输到 PTY;ANSI 输出流回浏览器
- xterm.js 的 WebGL 渲染器将每个单元格绘制到整数像素网格上;鼠标跟踪(SGR 1006)、宽字符(Unicode 11)和制表符绘制字形都原生渲染
- 调整浏览器窗口大小会通过
@xterm/addon-fit插件调整 TUI 大小
恢复现有会话: 从会话标签页,点击任何会话旁边的播放图标(▶)。这会跳转到 /chat?resume=<id> 并使用 --resume 启动 TUI,加载完整历史记录。
前提条件:
- Node.js(与
hermes --tui相同的要求;TUI 包在首次启动时构建) ptyprocess——由pty附加组件安装(pip install 'hermes-agent[web,pty]',或[all]涵盖两者)- POSIX 内核(Linux、macOS 或 WSL2)。
/chat终端面板特别需要 POSIX PTY——原生 Windows Python 没有等效项,因此在原生 Windows 安装上,仪表盘的其余部分(会话、作业、指标、配置编辑器)可以工作,但/chat标签页会显示一个横幅,告诉你使用 WSL2 来使用该功能。
关闭浏览器标签页后,PTY 会在服务器端被干净地回收。重新打开会生成一个新的会话。
要将 Hermes Desktop 指向另一台机器上运行的仪表盘,而不是其自带的本地后端,请参阅下面的远程后端部分。
将 Hermes Desktop 连接到远程后端
Hermes Desktop 通常启动自己的本地后端,但它也可以通过设置 → 网关 → 远程网关连接到远程机器(虚拟机、家庭实验室机器等)上运行的仪表盘。这是最常见的 "Desktop 说后端已就绪但聊天始终无法工作" 报告来源,因为 Desktop 的就绪检查验证的内容少于实时聊天连接实际需要的内容。
信息——前提条件:远程主机上必须运行
hermes dashboardDesktop 连接的 "远程后端" 就是远程机器上运行的
hermes dashboard进程——与本页面记录的服务器相同。它必须启动并可达,以下步骤才有意义;Desktop 是连接到它,而不是为你启动它。请使用systemd/tmux等工具保持其运行,以便在注销和重启后仍然存在。网关(Telegram/Discord/Slack 等)是一个独立的长时间运行进程——如果你依赖消息渠道,请单独启动它;它不是桌面应用连接的对象。
Desktop 的 "远程后端已就绪" 探测仅访问 GET /api/status,这是一个公共端点——只要主机上运行着任何仪表盘,它就会响应。实时聊天连接是一个独立的 WebSocket,连接到 /api/ws(和 /api/pty),并且该套接字受状态探测从未触及的两个额外检查控制:
- 你必须经过身份验证。 当仪表盘绑定到非回环地址时,它会启用其认证门。使用用户名和密码保护它(内置的用户名/密码提供程序);Desktop 登录一次,并通过一次性票据重用生成的会话用于 WebSocket。如果没有配置提供程序,非回环仪表盘在启动时失败关闭。
- 绑定主机必须允许客户端并与 Host 头匹配。 回环绑定(
127.0.0.1)只接受回环客户端,因此远程机器无论凭据如何都会在套接字层被拒绝。绑定到非回环地址(--host 0.0.0.0),以便对等 IP 检查允许远程客户端通过。你在 Desktop 中输入的远程 URL 必须通过仪表盘绑定的同一主机访问它——DNS 重新绑定保护要求 Host 头匹配。
远程仪表盘设置
设置用户名和密码,然后运行绑定到可达地址的仪表盘。对于 systemd 服务:
[Service]
EnvironmentFile=%h/.hermes/.env
ExecStart=/path/to/venv/bin/python -m hermes_cli.main dashboard \
--host 0.0.0.0 --port 9119 --no-open其中 ~/.hermes/.env 包含:
HERMES_DASHBOARD_BASIC_AUTH_USERNAME=admin
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=choose-a-strong-password
HERMES_DASHBOARD_BASIC_AUTH_SECRET=<32+ random bytes; openssl rand -base64 32>然后在 Desktop 中输入远程 URL(例如 http://VM_IP:9119)并使用该用户名和密码登录。有关完整配置,请参阅用户名/密码提供程序部分。
提示——在重试 Desktop 之前验证门已开启
从任何机器检查仪表盘是否通告了用户名/密码提供程序:
bashcurl -s http://VM_IP:9119/api/status | jq '.auth_required, .auth_providers' # true # ["basic"]
auth_required: true且提供程序列表中有"basic"→ Desktop 的登录流程将正常工作。auth_required: false→ 绑定是回环,或门未启用。绑定到非回环地址。auth_required: true但没有"basic"提供程序 → 用户名/密码环境变量未加载。先修复这些。
如果 /api/status 显示门已开启且提供程序为 "basic",但 Desktop 在登录后仍然无法连接,则问题超出了基本设置——获取一份新的 desktop.log(设置 → 网关 → 打开日志)以及同一重试窗口内的仪表盘日志,查找 /api/ws 关闭代码(4403 = 聊天 WS 被请求保护拒绝,例如 Host/对等不匹配;4401 = WS 票据未通过认证)。
配置
一个基于表单的 config.yaml 编辑器。所有 150+ 个配置字段从 DEFAULT_CONFIG 自动发现,并按标签页分类组织:

- model — 默认模型、提供程序、基础 URL、推理设置
- terminal — 后端(local/docker/ssh/modal)、超时、shell 偏好
- display — 皮肤、工具进度、恢复显示、旋转器设置
- agent — 最大迭代次数、网关超时、服务层级
- delegation — 子代理限制、推理努力
- memory — 提供程序选择、上下文注入设置
- approvals — 危险命令批准模式(ask/yolo/deny)
- 以及更多——config.yaml 的每个部分都有对应的表单字段
具有已知有效值的字段(终端后端、皮肤、批准模式等)呈现为下拉菜单。布尔值呈现为开关。其他所有内容都是文本输入。
操作:
- 保存 — 立即将更改写入
config.yaml - 重置为默认值 — 将所有字段恢复为默认值(直到点击保存才保存)
- 导出 — 将当前配置下载为 JSON
- 导入 — 上传 JSON 配置文件以替换当前值
提示
配置更改在下一次代理会话或网关重启后生效。Web 仪表盘编辑的是与
hermes config set和网关读取的同一个config.yaml文件。
API 密钥
管理存储 API 密钥和凭据的 .env 文件。密钥按类别分组:
- LLM 提供程序 — OpenRouter、Anthropic、OpenAI、DeepSeek 等
- 工具 API 密钥 — Browserbase、Firecrawl、Tavily、ElevenLabs 等
- 消息平台 — Telegram、Discord、Slack 机器人令牌等
- 代理设置 — 非秘密环境变量,如
API_SERVER_ENABLED
每个密钥显示:
- 当前是否已设置(带有值的脱敏预览)
- 用途描述
- 指向提供程序注册/密钥页面的链接
- 用于设置或更新值的输入字段
- 用于删除的按钮
高级/很少使用的密钥默认隐藏,可通过切换显示。
会话
浏览和检查所有代理会话。每行显示会话标题、源平台图标(CLI、Telegram、Discord、Slack、cron)、模型名称、消息数、工具调用次数以及上次活跃时间。实时会话标有脉冲徽章。
- 搜索 — 使用 FTS5 对所有消息内容进行全文搜索。结果显示高亮片段,并在展开时自动滚动到第一个匹配消息。
- 统计 — 摘要栏显示总会话数、存储中活跃的会话数、已归档计数、总消息数以及按来源的细分。
- 展开 — 点击会话加载其完整消息历史。消息按角色(用户、助手、系统、工具)着色,并作为 Markdown 渲染,带有语法高亮。
- 工具调用 — 带有工具调用的助手消息显示可折叠块,包含函数名称和 JSON 参数。
- 重命名 — 内联设置或清除会话标题(铅笔图标)。
- 导出 — 将会话(元数据 + 完整消息历史)下载为 JSON(下载图标)。
- 修剪 — 标题中的 "修剪旧会话" 按钮删除结束时间早于 N 天的会话。
- 删除 — 使用垃圾桶图标删除会话及其消息历史。

日志
查看代理、网关和错误日志文件,支持过滤和实时跟踪。
- 文件 — 在
agent、errors和gateway日志文件之间切换 - 级别 — 按日志级别过滤:ALL、DEBUG、INFO、WARNING 或 ERROR
- 组件 — 按源组件过滤:all、gateway、agent、tools、cli 或 cron
- 行数 — 选择显示的行数(50、100、200 或 500)
- 自动刷新 — 切换实时跟踪,每 5 秒轮询新日志行
- 颜色编码 — 日志行按严重性着色(红色表示错误,黄色表示警告,暗淡表示调试)
分析
从会话历史计算的使用量和成本分析。选择时间段(7、30 或 90 天)以查看:
- 摘要卡片 — 总令牌数(输入/输出)、缓存命中百分比、总估计或实际成本、总会话数及每日平均值
- 每日令牌图表 — 堆叠条形图,显示每天的输入和输出令牌使用量,悬停工具提示显示细分和成本
- 每日细分表 — 日期、会话数、输入令牌、输出令牌、缓存命中率和每天的成本
- 按模型细分 — 表格显示每个使用的模型、其会话数、令牌使用量和估计成本
Cron
创建和管理按计划重复运行代理提示的定时 cron 作业。
- 创建 — 填写名称(可选)、提示、cron 表达式(例如
0 9 * * *)和交付目标(local、Telegram、Discord、Slack 或 email) - 作业列表 — 每个作业显示其名称、提示预览、计划表达式、状态徽章(已启用/已暂停/错误)、交付目标、上次运行时间和下次运行时间
- 暂停/恢复 — 在作业的活跃和暂停状态之间切换
- 编辑 — 打开预填充的模态框以更改作业的提示、计划、名称或交付目标
- 立即触发 — 立即执行作业,超出其正常计划
- 删除 — 永久移除 cron 作业
配置文件
创建和管理配置文件——具有自己配置、技能和会话的独立 Hermes 实例。
- 配置文件卡片 — 每个显示其模型/提供程序、技能数量、网关状态、描述和徽章(活跃、默认、别名)
- 创建 — 名称 + 可选地从默认克隆/克隆所有/不包含捆绑技能、描述和模型;专用的配置文件构建器页面(
/profiles/new)提供完整流程(模型、MCP、技能) - 管理技能和工具 — 跳转到限定于该配置文件的技能页面(设置侧边栏配置文件切换器)
- 设置为活跃 — 翻转粘性默认值,未来的 CLI/网关运行会采用(与
hermes profile use相同)。这不会改变仪表盘管理的内容——那是配置文件切换器的工作 - 编辑模型/描述/SOUL — 内联编辑器写入该配置文件
- 重命名/删除 — 仅限命名配置文件
技能
浏览、搜索和切换已安装的技能和工具集,并从中心安装新的。技能从 ~/.hermes/skills/ 加载,并按类别分组。
- 搜索 — 按名称、描述或类别过滤已安装的技能和工具集
- 类别过滤器 — 点击类别药丸以缩小列表(例如 MLOps、MCP、Red Teaming、AI)
- 切换 — 使用开关启用或禁用单个技能。更改在下一次会话生效。
- 工具集 — 一个单独的视图显示内置工具集(文件操作、网页浏览等),包含其活跃/非活跃状态、设置要求和包含的工具列表
- 浏览中心 — 第三个视图在所有来源中搜索技能中心(与
hermes skills search相同),通过标识符安装任何结果,并显示实时安装日志,并提供 "全部更新" 按钮以刷新已安装的技能。

MCP
无需 CLI 即可管理 MCP 服务器。与 hermes mcp 读取的 config.yaml 中的 mcp_servers 块相同。
你的 MCP 服务器:
- 添加 — 注册 HTTP/SSE 服务器(URL)或 stdio 服务器(命令 + 参数),对于 stdio 服务器可选的
KEY=VALUE环境变量 - 启用/禁用 — 切换服务器开关而不删除它。禁用的服务器保留在配置中,以便以后重新启用。在下一次网关重启后生效。
- 测试 — 连接到服务器,列出其工具,然后断开连接——在代理依赖它之前验证连接
- 移除 — 从配置中删除服务器
- 秘密形状的环境值在列表视图中被脱敏
目录: 浏览 Nous 批准的 MCP 服务器(捆绑的 optional-mcps/ 目录),并一键安装其中任何一个。需要 API 密钥的条目会内联提示输入;值会写入 .env。这与 hermes mcp catalog / hermes mcp install 使用的目录相同。

Webhooks
管理动态 webhook 订阅。必须先在消息设置中启用 webhook 平台;页面在未启用时会显示提示。
- 创建 — 名称、描述、事件过滤器、交付目标、可选的直接交付模式以及代理提示。创建时页面会显示路由 URL 和一次性 HMAC 密钥供复制。
- 启用/禁用 — 切换订阅开关。禁用的路由保留在订阅文件中,但网关会拒绝其传入事件(403)。网关会热重载该文件,因此更改在下一次事件生效——无需重启。
- 列表 — 每个订阅显示其 URL、事件和交付目标
- 删除 — 移除订阅

配对
无需 CLI 即可批准和撤销消息用户——远程管理员如何将 Telegram/Discord 等用户加入配对网关。与 hermes pairing 完全一致。
- 待处理请求 — 每个显示平台、代码、用户和时长,带有批准按钮
- 已批准用户 — 每个显示平台和用户,带有撤销按钮
- 清除待处理 — 删除所有未完成的配对代码

渠道
从浏览器将 Hermes 连接到任何消息平台——与 hermes setup gateway 完全一致。页面列出每个支持的渠道(Telegram、Discord、Slack、Matrix、Mattermost、WhatsApp、Signal、BlueBubbles/iMessage、Email、SMS/Twilio、DingTalk、Feishu/Lark、WeCom、WeChat、QQ Bot、Yuanbao,以及 API 服务器和 webhook 端点),并显示其实时连接状态。
- 配置 — 打开一个按平台定制的表单,包含该渠道所需的字段(机器人令牌、应用令牌、服务器 URL、白名单等)。秘密渲染为密码输入,并存储为脱敏;留空字段保留现有值。必填字段被标记并验证。"设置指南" 链接指向平台的凭据文档。
- 启用/禁用 — 切换渠道开关。凭据保留在磁盘上;只有活跃状态改变。
- 测试 — 检查渠道是否已配置、启用,并且网关报告实时连接。
- 重启网关 — 凭据写入
~/.hermes/.env,启用标志写入config.yaml;网关在下一次重启时连接每个启用的渠道,你可以直接从页面触发重启。

系统
一个整合的管理面板,用于安装范围的操作:
- 主机 — 实时系统统计:操作系统/内核、架构、主机名、Python 和 Hermes 版本、CPU 核心数 + 利用率、内存、Hermes 主目录的磁盘使用量、运行时间和负载平均值。(CPU/内存/磁盘来自已安装的
psutil;身份字段始终显示。)Hermes 版本显示更新状态徽章(最新 / 落后 N 个提交)和检查更新按钮。当 git 或 pip 安装有可用更新时,立即更新按钮会打开一个确认对话框——显示将拉取多少个提交——然后在后台运行hermes update。在 Docker/Nix/Homebrew 安装上,仪表盘无法就地应用更新,因此会显示正确的带外命令。 - Nous Portal — 登录状态、活跃推理提供程序以及工具网关路由表(哪些工具通过 Portal 运行 vs. 本地运行),带有管理订阅的链接。
hermes portal的只读镜像。 - 技能管理员 — 后台技能维护状态(活跃/暂停、间隔、上次运行),带有暂停/恢复和立即运行按钮。镜像
hermes curator。 - 网关 — 启动、停止和重启消息网关,带有实时状态(运行/停止、PID、状态)
- 内存 — 选择外部内存提供程序(或仅内置),并重置内置的
MEMORY.md/USER.md存储 - 凭据池 — 添加和删除代理轮换使用的 API 密钥(按提供程序)。密钥在列表中脱敏;原始值仅到达代理。
- 操作 — 运行
doctor、安全审计、创建备份、从备份存档恢复、更新技能、显示系统提示大小细分、生成支持转储或迁移已弃用设置的配置。每个操作都会生成一个后台操作,其实时日志流式传输到页面。 - 检查点 — 查看
/rollback影子存储大小并修剪它 - Shell 钩子 — 列出已配置的钩子及其同意 + 可执行状态,创建钩子(事件、命令、匹配器、超时,带有可选的同意授予),并移除一个。钩子运行任意命令,因此创建表单带有安全警告,并且钩子仅在授予同意后触发。



创建 shell 钩子(注意同意复选框和运行任意命令的警告):

警告——安全
Web 仪表盘读取和写入你的
.env文件,其中包含 API 密钥和秘密。它默认绑定到127.0.0.1——只能从本地机器访问。如果你绑定到0.0.0.0,你网络上的任何人都可以查看和修改你的凭据。仪表盘本身没有认证。
/reload 斜杠命令
仪表盘 PR 还在交互式 CLI 中添加了 /reload 斜杠命令。通过 Web 仪表盘(或直接编辑 .env)更改 API 密钥后,在活跃的 CLI 会话中使用 /reload 以在不重启的情况下应用更改:
You → /reload
Reloaded .env (3 var(s) updated)这会重新读取 ~/.hermes/.env 到运行进程的环境中。当你通过仪表盘添加了新的提供程序密钥并希望立即使用时很有用。
REST API
Web 仪表盘暴露了一个 REST API,供前端使用。你也可以直接调用这些端点进行自动化:
提示——配置文件限定端点
管理端点系列——
/api/config、/api/env、/api/skills、/api/tools/toolsets、/api/mcp和/api/model/{info,options,auxiliary,set}—— 接受可选的?profile=<name>查询参数(或写入时 JSON 体中的"profile"), 将读取/写入限定于该配置文件的HERMES_HOME。省略 = 仪表盘自身的配置文件。 未知的配置文件名称返回404。/api/ptyWebSocket 接受相同的参数,以在所选配置文件下生成聊天。
GET /api/status
返回代理版本、网关状态、平台状态和活跃会话数。
GET /api/sessions
返回最近 20 个会话及其元数据(模型、令牌数、时间戳、预览)。
GET /api/config
以 JSON 形式返回当前 config.yaml 内容。
GET /api/config/defaults
返回默认配置值。
GET /api/config/schema
返回描述每个配置字段的模式——类型、描述、类别以及适用的选择选项。前端使用此模式为每个字段渲染正确的输入小部件。
PUT /api/config
保存新配置。请求体:{"config": {...}}。
GET /api/env
返回所有已知环境变量及其设置/未设置状态、脱敏值、描述和类别。
PUT /api/env
设置环境变量。请求体:{"key": "VAR_NAME", "value": "secret"}。
DELETE /api/env
移除环境变量。请求体:{"key": "VAR_NAME"}。
GET /api/sessions/{session_id}
返回单个会话的元数据。
GET /api/sessions/{session_id}/messages
返回会话的完整消息历史,包括工具调用和时间戳。
GET /api/sessions/search
跨消息内容进行全文搜索。查询参数:q。返回匹配的会话 ID 及高亮片段。
DELETE /api/sessions/{session_id}
删除会话及其消息历史。
GET /api/logs
返回日志行。查询参数:file(agent/errors/gateway)、lines(数量)、level、component。
GET /api/analytics/usage
返回令牌使用量、成本和会话分析。查询参数:days(默认 30)。响应包括每日细分和按模型聚合。
GET /api/cron/jobs
返回所有已配置的 cron 作业及其状态、计划和运行历史。
POST /api/cron/jobs
创建新的 cron 作业。请求体:{"prompt": "...", "schedule": "0 9 * * *", "name": "...", "deliver": "local"}。
POST /api/cron/jobs/{job_id}/pause
暂停 cron 作业。
POST /api/cron/jobs/{job_id}/resume
恢复暂停的 cron 作业。
POST /api/cron/jobs/{job_id}/trigger
立即触发 cron 作业,超出其计划。
DELETE /api/cron/jobs/{job_id}
删除 cron 作业。
GET /api/skills
返回所有技能及其名称、描述、类别和启用状态。
PUT /api/skills/toggle
启用或禁用技能。请求体:{"name": "skill-name", "enabled": true}。
GET /api/tools/toolsets
返回所有工具集及其标签、描述、工具列表和活跃/已配置状态。
管理端点
这些为 MCP、渠道、Webhooks、配对和系统页面提供支持。它们与 /api/ 的其他部分位于同一认证门后。
| 方法和路径 | 用途 |
|---|---|
GET /api/mcp/servers | 列出已配置的 MCP 服务器(环境值脱敏) |
POST /api/mcp/servers | 添加服务器。请求体:{name, url?, command?, args?, env?, auth?} |
POST /api/mcp/servers/{name}/test | 连接、列出工具、断开连接 |
PUT /api/mcp/servers/{name}/enabled | 启用/禁用服务器 |
DELETE /api/mcp/servers/{name} | 移除服务器 |
GET /api/mcp/catalog | 浏览 Nous 批准的 MCP 目录 |
POST /api/mcp/catalog/install | 安装目录条目(包含所需环境变量) |
GET /api/messaging/platforms | 列出每个消息渠道及其状态 + 按平台设置字段 |
PUT /api/messaging/platforms/{id} | 配置渠道。请求体:{enabled?, env?, clear_env?}(env 写入 .env,enabled 写入 config.yaml) |
POST /api/messaging/platforms/{id}/test | 报告渠道是否已配置、启用和连接 |
GET /api/pairing | 列出待处理和已批准的消息用户 |
POST /api/pairing/approve | 批准代码。请求体:{platform, code} |
POST /api/pairing/revoke | 撤销用户。请求体:{platform, user_id} |
POST /api/pairing/clear-pending | 删除所有待处理代码 |
GET /api/webhooks | 列出订阅 + 平台启用状态 |
POST /api/webhooks | 创建订阅(返回一次性密钥) |
DELETE /api/webhooks/{name} | 移除订阅 |
GET /api/credentials/pool | 列出池化轮换密钥(脱敏) |
POST /api/credentials/pool | 添加密钥。请求体:{provider, api_key, label?} |
DELETE /api/credentials/pool/{provider}/{index} | 移除密钥(基于 1 的索引) |
GET /api/memory | 活跃提供程序 + 可用提供程序 + 内置文件大小 |
PUT /api/memory/provider | 选择提供程序(空 = 仅内置) |
POST /api/memory/reset | 重置内置内存。请求体:{target: all|memory|user} |
POST /api/gateway/start · /stop · /restart | 网关生命周期(后台运行) |
POST /api/ops/doctor · /security-audit · /backup · /import | 诊断和维护(后台运行;通过 /api/actions/{name}/status 跟踪) |
GET /api/ops/hooks | 已配置的 shell 钩子 + 白名单状态 |
GET /api/ops/checkpoints · POST .../prune | 检查/修剪 /rollback 存储 |
POST /api/ops/hooks · DELETE /api/ops/hooks | 创建/移除 shell 钩子(需同意) |
GET /api/system/stats | 主机统计——操作系统、CPU、内存、磁盘、运行时间 |
GET /api/hermes/update/check | 报告更新可用性(落后提交数、安装方法),不应用。对于落后的 git/pip 安装,还返回 commits 列表(sha、summary、author、at),显示更改内容。?force=1 打破 6 小时缓存 |
GET /api/curator · PUT .../paused · POST .../run | 技能管理员状态 + 暂停/恢复 + 运行 |
GET /api/portal | Nous Portal 认证 + 工具网关路由(只读) |
POST /api/ops/prompt-size · /dump · /config-migrate | 诊断(后台运行) |
PUT /api/webhooks/{name}/enabled | 启用/禁用 webhook 路由 |
POST /api/skills/hub/install · /uninstall · /update | 技能中心操作(后台运行) |
GET /api/skills/hub/search | 在所有来源中搜索技能中心 |
GET /api/sessions/stats | 会话存储统计 |
PATCH /api/sessions/{id} | 重命名/归档会话 |
GET /api/sessions/{id}/export | 导出会话(元数据 + 消息)为 JSON |
POST /api/sessions/prune | 删除结束时间早于 N 天的会话 |
PUT /api/cron/jobs/{id} | 编辑 cron 作业的提示/计划/名称/交付 |
认证(门控模式)
当仪表盘绑定到公共或非回环地址——任何不是 127.0.0.1 / localhost 的地址时——Hermes Agent 会启用一个认证门。每个请求必须携带一个经过验证的会话 cookie,否则会被重定向到登录页面。内置三个提供程序:
- 用户名/密码 —— 在自托管/本地/家庭实验室仪表盘上添加认证的最简单方式。无需外部身份提供程序。仅在受信任的网络或 VPN 后使用——不要用于公共互联网暴露。
- OAuth (Nous Portal) —— 用于托管部署和任何可通过公共互联网访问的仪表盘,也是远程 Hermes Desktop 连接的推荐路径。每次登录都通过你的 Nous 账户验证,因此该提供程序适用于面向互联网的使用。
- 自托管 OIDC —— 用于通过标准 OpenID Connect(Keycloak、Auth0、Okta、Google、GitHub 通过 OIDC 桥等)自带身份提供程序。不涉及 Nous Portal;当由符合标准的 OIDC 服务器前端时,适用于公共互联网暴露。
绑定到回环的操作员拥有的仪表盘不受影响——无需认证,无登录页面。
门何时启用
| 标志 | 认证门 | 使用场景 |
|---|---|---|
hermes dashboard(默认——绑定到 127.0.0.1) | 关闭 | 本地开发 |
hermes dashboard --host 0.0.0.0 | 开启 | 远程/生产——使用用户名/密码提供程序或 OAuth 保护 |
门开启当且仅当:
- 绑定主机不是
127.0.0.1、::1、localhost或0.0.0.0且 --insecure标志未设置。
危险——
--insecure完全禁用认证
--insecure跳过门,提供一个未经认证的仪表盘,可以读取/写入你的.env(API 密钥、秘密)并运行代理命令。不要将其用于远程连接。 要将仪表盘暴露给另一台机器,请配置用户名/密码提供程序(或 OAuth)并保持--insecure关闭。该标志仅作为完全受信任、防火墙隔离的单主机网络上的最后逃生舱口存在。
失败关闭语义
如果门会启用但没有注册 DashboardAuthProvider(没有 Nous 插件,没有自定义插件),hermes dashboard 会拒绝绑定并显示明确的错误消息。没有 "默认拒绝但接受所有" 的回退——配置错误的门控仪表盘永远不会启动。
默认提供程序:Nous Research
捆绑的 plugins/dashboard_auth/nous 插件始终安装并自动加载。当配置了客户端 ID 时,它会自动注册一个名为 nous 的 DashboardAuthProvider。
因为每次登录都通过 Nous Portal 验证并由你的 Nous 账户保护,Nous 提供程序是适合将仪表盘暴露到公共互联网的提供程序。
注册仪表盘
要使用 Nous 提供程序,你需要一个 OAuth 客户端 ID(格式 agent:{id})。有两种获取方式:
-
CLI——
hermes dashboard register。 在仪表盘所在的主机上运行。它会解析你现有的 Nous 登录(如果尚未登录,先运行hermes setup),向 Portal 注册一个自托管 OAuth 客户端,并将HERMES_DASHBOARD_OAUTH_CLIENT_ID写入~/.hermes/.env。可选标志:--name(人类可读标签,否则自动生成)和--redirect-uri(面向互联网主机的公共 HTTPS 回调 URL)。bashhermes dashboard register
✓ Registered dashboard "swift_falcon"
…writes HERMES_DASHBOARD_OAUTH_CLIENT_ID to ~/.hermes/.env
- **GUI——本地仪表盘页面。** 在 Nous Portal 中打开 [`/local-dashboards`](https://portal.nousresearch.com/local-dashboards) 以注册、命名、管理和撤销自托管仪表盘。将生成的 `agent:{id}` 客户端 ID 复制到 `HERMES_DASHBOARD_OAUTH_CLIENT_ID`(环境变量)或 `dashboard.oauth.client_id`(config.yaml)。这也是撤销通过 CLI 注册的仪表盘的地方。
#### 配置
插件从两个来源读取,当环境变量非空时优先。
**`config.yaml`** —— 规范来源:
```yaml
dashboard:
oauth:
client_id: agent:01HXYZ… # 启用门所需环境变量 —— 操作员覆盖:
| 环境变量 | 覆盖 | 格式 | 由谁提供 |
|---|---|---|---|
HERMES_DASHBOARD_OAUTH_CLIENT_ID | dashboard.oauth.client_id | agent:{instance_id} | hermes dashboard register |
根据 Hermes Agent 约定(~/.hermes/.env 仅用于 API 密钥/秘密),config.yaml 是推荐设置这些值的位置,用于本地开发、本地部署以及任何你直接控制的部署。环境变量路径的存在是为了让托管平台的秘密注入可以推送每个部署的 client_id,而无需任何人编辑镜像内的 config.yaml——这是其主要目的。
空的环境值被视为未设置,因此已配置但未填充的平台秘密不会意外地覆盖有效的 config.yaml 条目。
如果两个来源都没有提供 client_id,插件会报告具体原因,仪表盘的失败关闭绑定错误会明确告诉你需要修复什么:
Refusing to bind dashboard to 0.0.0.0 — the OAuth auth gate engages on
non-loopback binds, but no auth providers are registered.
Bundled providers reported these issues:
• nous: HERMES_DASHBOARD_OAUTH_CLIENT_ID is not set (and
dashboard.oauth.client_id in config.yaml is empty). The Nous Portal
provisions this env var (shape 'agent:{instance_id}') when it
deploys a Hermes Agent instance — set it to your provisioned
client id (either as an env var or under dashboard.oauth.client_id
in config.yaml), or pass --insecure to skip the OAuth gate entirely.
Or pass --insecure to skip the auth gate (NOT recommended on untrusted
networks).工作示例:Nous Research
从已登录的 Hermes 安装到 Nous 门控仪表盘,三步完成。
1. 登录并注册仪表盘。 hermes dashboard register 使用你现有的 Nous 登录来配置 OAuth 客户端,并将 HERMES_DASHBOARD_OAUTH_CLIENT_ID 写入 ~/.hermes/.env:
hermes setup # 如果你尚未登录 Nous Portal
hermes dashboard register
## ✓ Registered dashboard "swift_falcon"
## …writes HERMES_DASHBOARD_OAUTH_CLIENT_ID to ~/.hermes/.env2. 在可达地址上运行仪表盘。 非回环绑定且不带 --insecure 会启用 OAuth 门,刚写入的 client_id 会激活 nous 提供程序:
hermes dashboard --host 0.0.0.0 --port 9119 --no-open3. 登录。 打开 http://<host>:9119/,你将被重定向到 /login。点击 Sign in with Nous Research → 在 Portal 认证 → 返回已认证的仪表盘。从任何机器验证门:
curl -s http://<host>:9119/api/status | jq '.auth_required, .auth_providers'
## true
## ["nous"]GET /api/auth/me 随后返回已验证的会话(provider: nous)。对于面向互联网的主机,使用 --redirect-uri https://hermes.example.com/auth/callback 注册,并设置 HERMES_DASHBOARD_PUBLIC_URL 以便 OAuth 回调解析到你的公共 URL(参见公共 URL 覆盖)。
用户名/密码提供程序(无 OAuth IDP)
如果你不想设置 OAuth 身份提供程序——一个自托管的 "只需给我的仪表盘加个密码" 部署——捆绑的 plugins/dashboard_auth/basic 插件会注册一个名为 basic 的 DashboardAuthProvider,它使用用户名和密码进行认证,而不是 OAuth 重定向。
它插入与 OAuth 提供程序相同的门:门在非回环绑定且不带 --insecure 时启用,登录页面为此提供程序渲染一个凭据表单(而不是 "使用 X 登录" 按钮),并且登录之后的所有内容——会话 cookie、透明刷新、WS 票据、注销、审计日志——与 OAuth 路径相同。会话是提供程序自身铸造的无状态 HMAC 签名令牌,因此没有数据库,也没有外部 IDP。密码哈希使用标准库 scrypt(无第三方依赖)。
警告——仅在受信任网络上使用,不要用于公共互联网
用户名/密码提供程序适用于受信任网络上的自托管/本地/家庭实验室仪表盘,或仅通过 VPN 可达的仪表盘。它使用单个共享凭据进行保护,没有外部身份提供程序、MFA 或背后的用户账户,因此不适合将仪表盘直接暴露到公共互联网。对于面向互联网的仪表盘,请使用 Nous Research 提供程序(或你自己的自托管 OIDC / 自定义 OAuth 提供程序)。
配置
与 Nous 提供程序类似,它从 config.yaml(规范)读取,环境变量非空时优先。它仅在配置了 username 加上 password_hash(首选)或 password 时激活——否则为无操作,因此 OAuth 用户和回环/--insecure 操作员不受影响。
config.yaml:
dashboard:
basic_auth:
username: admin
## 首选——静态时不存储明文。计算方式:
## python -c "from plugins.dashboard_auth.basic import hash_password; print(hash_password('PW'))"
password_hash: "scrypt$16384$8$1$…$…"
## ...或明文密码(加载时在内存中哈希;静态时安全性较低):
## password: "s3cret"
secret: "<32+ random bytes, base64 or hex>" # 令牌签名密钥
session_ttl_seconds: 43200 # 可选;访问令牌生命周期(默认 12h)环境变量覆盖:
| 环境变量 | 覆盖 | 备注 |
|---|---|---|
HERMES_DASHBOARD_BASIC_AUTH_USERNAME | dashboard.basic_auth.username | 激活所需 |
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD_HASH | dashboard.basic_auth.password_hash | 首选(静态时不存储明文) |
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD | dashboard.basic_auth.password | 明文;覆盖配置中的 password_hash,因此你可以通过环境变量轮换 |
HERMES_DASHBOARD_BASIC_AUTH_SECRET | dashboard.basic_auth.secret | 令牌签名密钥 |
HERMES_DASHBOARD_BASIC_AUTH_TTL_SECONDS | dashboard.basic_auth.session_ttl_seconds | 访问令牌生命周期 |
注意——设置显式
secret以实现稳定会话当
secret为空时,会生成一个随机的每进程签名密钥。这对于单个进程没问题,但意味着每次重启都会使所有会话失效,并且会话不能跨多个工作进程。为重启持久/多工作进程部署设置显式secret。
/auth/password-login 端点按客户端 IP 进行速率限制(默认 10 次尝试/分钟 → HTTP 429),并且对于未知用户和错误密码都返回单个通用 401 Invalid credentials,因此不能用作用户名枚举预言机。
工作示例:用户名/密码
从零开始到受密码保护的仪表盘,在受信任网络上三步完成。
1. 在 ~/.hermes/.env 中设置凭据。 对密码进行哈希处理,以便静态时不存储明文,并设置稳定的签名密钥,以便会话在重启后持续存在:
## 计算所选密码的 scrypt 哈希:
HASH=$(python -c "from plugins.dashboard_auth.basic import hash_password; print(hash_password('choose-a-strong-password'))")
cat >> ~/.hermes/.env <<EOF
HERMES_DASHBOARD_BASIC_AUTH_USERNAME=admin
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD_HASH=$HASH
HERMES_DASHBOARD_BASIC_AUTH_SECRET=$(openssl rand -base64 32)
EOF
chmod 600 ~/.hermes/.env2. 在可达地址上运行仪表盘。 非回环绑定且不带 --insecure 会启用门,用户名 + 哈希会激活 basic 提供程序:
hermes dashboard --host 0.0.0.0 --port 9119 --no-open3. 登录。 打开 http://<host>:9119/,你将被重定向到 /login——一个凭据表单(而不是 "使用 X 登录" 按钮)。输入 admin / 你的密码 → 返回已认证的仪表盘。从任何机器验证门:
curl -s http://<host>:9119/api/status | jq '.auth_required, .auth_providers'
## true
## ["basic"]GET /api/auth/me 随后返回已验证的会话(provider: basic)。将其放在 VPN 后面——参见上面的警告;对于公共主机,请使用 Nous Research 或自托管 OIDC 提供程序。
编写你自己的密码提供程序
basic 只是扩展点的一个实现。任何插件都可以注册密码提供程序:在你的 DashboardAuthProvider 子类上设置 supports_password = True,并实现 complete_password_login(*, username, password) -> Session(拒绝时引发 InvalidCredentialsError,如果后端存储不可用则引发 ProviderError)。OAuth 的 start_login / complete_login 方法可以保留为 NotImplementedError 存根,用于纯密码提供程序。这是 LDAP 绑定、凭据数据库或任何其他非重定向认证方案的路径——框架为你处理表单、路由、cookie 和刷新。
自托管 OIDC 提供程序
如果你运行自己的身份提供程序,捆绑的 plugins/dashboard_auth/self_hosted 插件使用标准 OpenID Connect 对仪表盘进行认证——无需每个 IDP 的代码,不涉及 Nous Portal。它已通过验证,可与任何符合标准的 OIDC 服务器配合使用:
Authentik · Keycloak · Zitadel · Authelia · Auth0 · Okta · Google · …
与 Nous 提供程序一样,它会自动加载,并且仅在配置后注册自身,因此对于回环/--insecure 仪表盘是无操作的。
配置
配置一个 issuer 和一个 client_id(公共 PKCE 客户端——无客户端密钥)。插件从 {issuer}/.well-known/openid-configuration 获取 IDP 的 authorization_endpoint、token_endpoint 和 jwks_uri,因此你永远不需要硬编码端点 URL。
config.yaml —— 规范来源:
dashboard:
oauth:
provider: self-hosted
self_hosted:
issuer: https://auth.example.com/application/o/hermes/ # 必需
client_id: hermes-dashboard # 必需
scopes: "openid profile email" # 可选(这是默认值)环境变量 —— 操作员覆盖(环境变量非空时覆盖 config.yaml;空值视为未设置):
| 环境变量 | 覆盖 | 备注 |
|---|---|---|
HERMES_DASHBOARD_OIDC_ISSUER | dashboard.oauth.self_hosted.issuer | OIDC 发行者 URL——必需 |
HERMES_DASHBOARD_OIDC_CLIENT_ID | dashboard.oauth.self_hosted.client_id | 公共客户端 ID——必需 |
HERMES_DASHBOARD_OIDC_SCOPES | dashboard.oauth.self_hosted.scopes | 默认为 openid profile email |
在你的 IDP 中,注册一个公共应用程序/客户端,使用授权码 + PKCE (S256) 授权类型,并将仪表盘的回调添加为允许的重定向 URI。回调是 <dashboard public URL>/auth/callback(参见公共 URL 覆盖了解仪表盘如何在代理后派生其公共 URL)。
它验证什么
提供程序根据发现的 jwks_uri 验证 OpenID Connect ID 令牌(RS256/ES256),并将 iss 和 aud 声明固定到你配置的 issuer 和 client_id。标准 OIDC 声明映射到仪表盘会话:
| 会话字段 | 声明 |
|---|---|
user_id | sub(必需) |
email | email |
display_name | name → preferred_username → nickname → email |
org_id | org_id / organization,否则为连接的 groups |
ID 令牌是建立身份的依据——访问令牌被视为不透明(OIDC 规范不要求它是 JWT)。端点 URL 必须是 HTTPS(回环 http:// 允许用于本地开发 IDP),并且发现文档通告的 issuer 必须与你配置的匹配(允许尾部斜杠差异)。当 IDP 颁发刷新令牌时,它们用于通过标准 refresh_token 授权进行静默重新认证;注销会调用 IDP 的 RFC 7009 revocation_endpoint(如果已通告)。
机密客户端(具有
client_secret的客户端)尚不支持——配置一个公共 + PKCE 客户端,这是面向浏览器的仪表盘的典型选择。
工作示例:Keycloak
Keycloak 是最容易搭建的自托管 OIDC 服务器之一,用于本地测试——它在开发模式下作为单个容器运行(内存数据库),并暴露教科书式的 OIDC 发现。本演练让你在几分钟内从零开始到可工作的仪表盘登录。
1. 使用预配置的领域运行 Keycloak。 将此领域导出保存为 realm-hermes.json——它定义了一个 hermes 领域、一个公共 PKCE 客户端(hermes-dashboard)和一个测试用户,所有这些都在启动时导入,因此无需在管理 UI 中点击:
{
"realm": "hermes",
"enabled": true,
"clients": [
{
"clientId": "hermes-dashboard",
"name": "Hermes Agent Dashboard",
"enabled": true,
"publicClient": true,
"standardFlowEnabled": true,
"protocol": "openid-connect",
"redirectUris": ["http://localhost:9119/auth/callback"],
"webOrigins": ["http://localhost:9119"],
"attributes": { "pkce.code.challenge.method": "S256" }
}
],
"users": [
{
"username": "testuser",
"enabled": true,
"emailVerified": true,
"email": "testuser@example.com",
"firstName": "Test",
"lastName": "User",
"credentials": [
{ "type": "password", "value": "testpassword", "temporary": false }
]
}
]
}启动它(Keycloak 26+),将该文件挂载到导入目录:
docker run --rm -p 8080:8080 \
-e KC_BOOTSTRAP_ADMIN_USERNAME=admin \
-e KC_BOOTSTRAP_ADMIN_PASSWORD=admin \
-v "$PWD/realm-hermes.json:/opt/keycloak/data/import/realm-hermes.json:ro" \
quay.io/keycloak/keycloak:26.0 \
start-dev --import-realm启动后,领域在 http://localhost:8080/realms/hermes/.well-known/openid-configuration 通告标准 OIDC 发现(发行者 http://localhost:8080/realms/hermes)。管理控制台位于 http://localhost:8080/(admin / admin)。
2. 将仪表盘指向它。 自托管插件允许回环 http:// 发行者(对于非回环发行者需要 HTTPS),因此本地 Keycloak 可以直接使用:
export HERMES_DASHBOARD_OIDC_ISSUER="http://localhost:8080/realms/hermes"
export HERMES_DASHBOARD_OIDC_CLIENT_ID="hermes-dashboard"
export HERMES_DASHBOARD_PUBLIC_URL="http://localhost:9119"
hermes dashboard --host 0.0.0.0 --port 9119 --no-openHERMES_DASHBOARD_PUBLIC_URL 告诉仪表盘其 OAuth 回调是 http://localhost:9119/auth/callback——上面领域注册的重定向 URI。绑定到 0.0.0.0(非回环绑定)且不带 --insecure 是启用 OAuth 门的方式。
3. 登录。 打开 http://localhost:9119/,你将被重定向到 /login。点击 Sign in with Self-Hosted OIDC → 在 Keycloak 上以 testuser / testpassword 认证 → 返回已认证的仪表盘。侧边栏显示 Logged in as Test User via self-hosted,GET /api/auth/me 返回已验证的会话(provider: self-hosted,email: testuser@example.com)。
如果你在不同的主机或端口上绑定或浏览,请将该来源的
…/auth/callback添加到 Keycloak 管理控制台中客户端的 Valid redirect URIs(Clients → hermes-dashboard → Settings)。相同的模式适用于 Authentik、Zitadel、Authelia 和其他 OIDC 服务器——只有发行者 URL 和客户端注册 UI 不同。
公共 URL 覆盖
默认情况下,仪表盘从请求中重建 OAuth 回调 URL——X-Forwarded-Host + X-Forwarded-Proto + X-Forwarded-Prefix(当 uvicorn 配置了 proxy_headers=True 时,start_server 在门控下会启用)。这在正确设置所有三个标头的反向代理后面开箱即用。
对于部署在不能可靠转发这些标头的反向代理后面(手动 nginx 设置、本地入口、具有部分代理链的自定义域名部署),设置 dashboard.public_url(或 HERMES_DASHBOARD_PUBLIC_URL)为仪表盘被访问的完整公共 URL:
dashboard:
public_url: "https://dashboard.example.com/hermes"设置后,OAuth 回调 URL 变为 <public_url>/auth/callback 逐字——在该代码路径上忽略 X-Forwarded-Prefix,因为操作员已显式声明了公共 URL。这是有意为之:将前缀叠加在 public_url 上会导致常见情况下的双重前缀,因为前缀已经内置于 public_url 中。
与其他仪表盘设置相同的优先级——环境变量覆盖 config.yaml:
| 来源 | 覆盖路径 | 何时使用 |
|---|---|---|
config.yaml 中的 dashboard.public_url | HERMES_DASHBOARD_PUBLIC_URL | 本地开发/本地部署(规范) |
HERMES_DASHBOARD_PUBLIC_URL 环境变量 | — | 托管平台秘密/CI |
| (未设置) | — | 默认——从 X-Forwarded-* 标头重建 |
验证会拒绝没有 http:// / https:// 方案、没有主机或包含引号/尖括号/空白/控制字符的值。格式错误的值会静默回退到标头重建,以便登录流程继续工作,而不是将用户发送到恶意 URL。
注意:
public_url仅覆盖 OAuth 回调 URL。Securecookie 标志仍由request.url.scheme控制(在proxy_headers下为 X-Forwarded-Proto),因此在 TLS 终止的公共部署上使用http://的public_url会产生非安全 cookie。这是操作员的陷阱——请将public_url与正确的上游 TLS 终止配合使用。
OAuth 流程
提供程序实现了 Nous Portal OAuth 合同 v1——授权码授权 + PKCE (S256):
- 用户访问
/但没有会话 cookie → 门重定向到/login。 - 登录页面显示 "Continue with Nous Research" 按钮 →
/auth/login?provider=nous。 - 服务器将 PKCE 状态存储在短期 cookie 中,将用户重定向到
https://portal.nousresearch.com/oauth/authorize?…。 - 用户在 Portal 上认证,到达
/auth/callback?code=…&state=…。 - 服务器在
POST /api/oauth/token处交换代码为访问令牌,根据 Portal 的 JWKS(/.well-known/jwks.json)验证 JWT 签名,并设置hermes_session_atcookie。 - 用户被重定向到
/(或通过next=查询参数重定向到原始深度链接路径)。
访问令牌的 TTL 为 15 分钟。合同 v1 中没有刷新令牌——当令牌过期时,SPA 的 fetch 包装器检测到 401 信封并全页导航回 /login 以重新运行流程。
设置的 Cookie
| 名称 | 生命周期 | 备注 |
|---|---|---|
hermes_session_at | 令牌 TTL(15 分钟) | HttpOnly, SameSite=Lax, Secure-when-HTTPS |
hermes_session_pkce | 10 分钟 | HttpOnly;在往返过程中保存 PKCE 验证器 + 提供程序提示 |
hermes_session_rt | v1 中未使用 | 为向前兼容保留;当 refresh_token 为空时不写入 |
所有三个都是 Path=/ 和 SameSite=Lax。当仪表盘通过 HTTPS 访问时(通过请求 URL 方案检测——在 proxy_headers=True 下尊重来自上游 TLS 终止器的 X-Forwarded-Proto),会设置 Secure 标志。
注销
侧边栏小部件显示 Logged in as <user_id…> via nous,带有一个注销图标。点击它会 POST /auth/logout,清除所有仪表盘认证 cookie,并重定向回 /login。
审计日志
每次登录开始、成功、失败和会话验证失败都会作为 JSON 行写入 $HERMES_HOME/logs/dashboard-auth.log。敏感字段(access_token、refresh_token、code、code_verifier、state、Authorization 标头)在记录前会被脱敏。
自定义提供程序
要插入非 Nous 的 OAuth 提供程序(例如 Google、GitHub、自定义 OIDC),创建一个注册 DashboardAuthProvider 的插件:
## ~/.hermes/plugins/dashboard-auth-myidp/__init__.py
from hermes_cli.dashboard_auth import DashboardAuthProvider, Session, LoginStart
class MyIdPProvider(DashboardAuthProvider):
name = "myidp"
display_name = "My Identity Provider"
def start_login(self, *, redirect_uri): ...
def complete_login(self, *, code, state, code_verifier, redirect_uri): ...
def verify_session(self, *, access_token): ...
def refresh_session(self, *, refresh_token): ...
def revoke_session(self, *, refresh_token): ...
def register(ctx):
ctx.register_dashboard_auth_provider(MyIdPProvider())登录页面列出所有已注册的提供程序;可以堆叠多个提供程序,用户在 /login 处选择一个。
验证门已开启
## 快速环境变量路径。
HERMES_DASHBOARD_OAUTH_CLIENT_ID=agent:test \
hermes dashboard --host 0.0.0.0
## 或通过 config.yaml 的等效方式(推荐用于本地开发/本地部署):
## ## dashboard:
## oauth:
## client_id: agent:test
## ## 然后只需:
hermes dashboard --host 0.0.0.0
## 访问 /api/status 查看门状态:
curl -s http://127.0.0.1:9119/api/status | jq '.auth_required, .auth_providers'
## true
## ["nous"]仪表盘的 React StatusPage 在 "Web server" 下显示相同的字段。侧边栏的 AuthWidget 在你登录后显示当前身份。
将 Hermes Desktop 连接到远程后端
Hermes Desktop 可以驱动另一台机器(VPS、家庭服务器、Tailscale 后面的 Mini)上运行的 Hermes 后端。在应用中,这位于设置 → 网关 → 远程网关,它要求输入远程 URL 和登录方式。(关于桌面应用本身——安装、设置、聊天——请参阅 Hermes Desktop 页面。)
你使用其中一个内置认证提供程序保护远程仪表盘,桌面应用会根据后端通告的提供程序进行登录。对于超出你自身机器范围的后端——VPS、公共主机、任何面向互联网的——推荐提供程序是 OAuth (Nous Portal)(使用 hermes dashboard register 注册它,并使用 Sign in with Nous Research 登录)。内置的用户名/密码提供程序 是当后端在受信任的 LAN 上或仅通过 VPN 可达时的最快选项,但不适合直接暴露到公共互联网。将仪表盘绑定到非回环地址会启用其认证门;登录后,Desktop 会自动为聊天 WebSocket 重用会话——无需复制或粘贴令牌。
下面的配方使用用户名/密码路径,因为这是在受信任网络上搭建的最快方式;对于 OAuth 路径,请参阅默认提供程序:Nous Research。
在后端(远程机器)
## 1. 在 ~/.hermes/.env 中设置仪表盘登录凭据(秘密文件,0600)。
cat >> ~/.hermes/.env <<'EOF'
HERMES_DASHBOARD_BASIC_AUTH_USERNAME=admin
HERMES_DASHBOARD_BASIC_AUTH_PASSWORD=choose-a-strong-password
## 推荐:稳定的签名密钥,以便会话在重启后持续存在。
HERMES_DASHBOARD_BASIC_AUTH_SECRET=$(openssl rand -base64 32)
EOF
chmod 600 ~/.hermes/.env
## 2. 运行绑定到可达地址的仪表盘。非回环绑定会启用认证门;用户名/密码提供程序处理登录。
hermes dashboard --no-open --host 0.0.0.0 --port 9119希望静态时不存储明文?使用 HERMES_DASHBOARD_BASIC_AUTH_PASSWORD_HASH 配合 scrypt 哈希——请参阅用户名/密码提供程序了解完整配置。
如果你将仪表盘作为 systemd 服务运行,当单元具有 EnvironmentFile=%h/.hermes/.env 时,~/.hermes/.env 会自动加载,因此凭据在启动时就在环境中。
警告
仪表盘读取和写入你的
.env(API 密钥、秘密),并且可以运行代理命令。此处显示的用户名/密码设置适用于受信任的网络——切勿将受密码保护的仪表盘直接暴露到开放互联网。将其放在 VPN 后面。Tailscale 是干净的选择:绑定到机器的 tailscale IP(--host <tailscale-ip>),并使用http://<tailscale-ip>:9119作为远程 URL。只有你的 tailnet 上的设备可以访问它。要通过公共互联网访问后端,请改用 OAuth (Nous Portal) 提供程序。
在 Hermes Desktop 中
设置 → 网关 → 远程网关:
- 远程 URL —
http://<backend-host>:9119(如果你用反向代理前端,支持路径前缀如/hermes) - 登录 — 应用检测到用户名/密码网关并显示登录按钮;点击它并输入步骤 1 中的凭据
- 保存并重新连接 — 将桌面 shell 切换到远程后端
会话会自动刷新,并在后端设置了 HERMES_DASHBOARD_BASIC_AUTH_SECRET 时在重启后持续存在。
环境变量覆盖
除了应用内设置,你还可以在启动桌面之前通过环境变量将其指向后端。当设置了 HERMES_DESKTOP_REMOTE_URL 时,它会覆盖保存的应用内 URL(网关设置面板显示 "env override" 徽章并禁用编辑);你仍然需要从面板使用用户名和密码登录。
| 环境变量 | 值 |
|---|---|
HERMES_DESKTOP_REMOTE_URL | http://<backend-host>:9119 |
故障排除
- "远程网关不完整" —— 你尚未输入远程 URL。
- 登录失败,401 / "无效凭据" —— 用户名或密码与后端的
HERMES_DASHBOARD_BASIC_AUTH_USERNAME/HERMES_DASHBOARD_BASIC_AUTH_PASSWORD不匹配。后端对未知用户和错误密码返回相同的通用错误,因此请检查两者。使用curl -s http://<host>:9119/api/status | jq '.auth_required, .auth_providers'确认门——应报告true并包含"basic"。 - 没有 "登录" 按钮——它要求输入会话令牌 —— 用户名/密码提供程序未激活(
/api/status不会列出"basic")。确保设置了用户名和密码(或密码哈希),并且仪表盘进程已加载它们。 - 每次重启后都注销 —— 将
HERMES_DASHBOARD_BASIC_AUTH_SECRET设置为稳定值;否则签名密钥会在每次启动时重新生成。 - 连接被拒绝/超时 —— 后端绑定到
127.0.0.1(默认)而不是可达地址,或者防火墙/VPN 阻止了端口。绑定到0.0.0.0或 tailscale IP,并向你的受信任网络开放端口。
CORS
Web 服务器将 CORS 限制为仅 localhost 来源:
http://localhost:9119/http://127.0.0.1:9119(生产环境)http://localhost:3000/http://127.0.0.1:3000http://localhost:5173/http://127.0.0.1:5173(Vite 开发服务器)
如果你在自定义端口上运行服务器,该来源会自动添加。
开发
如果你正在为 Web 仪表盘前端做贡献:
## 终端 1:启动后端 API
hermes dashboard --no-open
## 终端 2:启动带有 HMR 的 Vite 开发服务器
cd web/
npm install
npm run dev位于 http://localhost:5173 的 Vite 开发服务器将 /api 请求代理到位于 http://127.0.0.1:9119 的 FastAPI 后端。
前端使用 React 19、TypeScript、Tailwind CSS v4 和 shadcn/ui 风格组件构建。生产构建输出到 hermes_cli/web_dist/,FastAPI 服务器将其作为静态 SPA 提供。
更新时自动构建
当你运行 hermes update 时,如果 npm 可用,Web 前端会自动重建。这使仪表盘与代码更新保持同步。如果未安装 npm,更新会跳过前端构建,hermes dashboard 会在首次启动时构建它。
主题和插件
仪表盘内置了六个主题,并且可以通过用户定义的主题、插件标签页和后端 API 路由进行扩展——全部即插即用,无需克隆仓库。
实时切换主题 从标题栏——点击语言切换器旁边的调色板图标。选择会持久化到 config.yaml 中的 dashboard.theme,并在页面加载时恢复。
独立更改字体 从同一个选择器——主题列表下方的字体部分会覆盖当前主题的 UI 字体。选择会跨主题切换持久化(config.yaml → dashboard.font);选择主题默认可清除它并返回当前主题的自身字体。
内置主题:
| 主题 | 特点 |
|---|---|
Hermes Teal (default) | 深青色 + 奶油色,系统字体,舒适的间距 |
Hermes Teal (Large) (default-large) | 与默认相同,但 18px 文本和更宽松的间距 |
Midnight (midnight) | 深蓝紫色,Inter + JetBrains Mono |
Ember (ember) | 暖红 + 青铜色,Spectral 衬线 + IBM Plex Mono |
Mono (mono) | 灰度,IBM Plex,紧凑 |
Cyberpunk (cyberpunk) | 黑色背景上的霓虹绿,Share Tech Mono |
Rosé (rose) | 粉色 + 象牙色,Fraunces 衬线,宽敞 |
要构建你自己的主题、添加插件标签页、注入到 shell 插槽或暴露插件特定的 REST 端点,请参阅 扩展仪表盘——完整指南涵盖:
- 主题 YAML 模式——调色板、排版、布局、资源、组件样式、颜色覆盖、自定义 CSS
- 布局变体——
standard、cockpit、tiled - 插件清单、SDK、shell 插槽、页面限定插槽(将小部件注入内置页面而不覆盖它们)、后端 FastAPI 路由
- 一个完整的主题加插件组合演练(Strike Freedom cockpit 演示)
- 发现、重新加载和故障排除