docs(dashboard-auth): remove legacy session-token references

Sweeps user-facing docs (English + zh-Hans mirrors) to the new auth model
now that the legacy dashboard session token is gone:
- loopback bind: no identity gate (the bind is the boundary) + a
  Sec-Fetch-Site CSRF guard on mutating requests + localhost-only CORS
- gated (non-loopback) bind: pluggable OAuth/basic-auth provider; REST via
  session cookie, WS via single-use ?ticket=

Files:
- configuring-models.md: drop the X-Hermes-Session-Token header from the
  /api/model/* curl examples; replace the window.__HERMES_SESSION_TOKEN__
  'grab it from devtools' note with the no-loopback-auth / gated-cookie model
- features/kanban.md: kanban routes + WS no longer described as token-gated
  (loopback none; gated cookie + ?ticket=)
- features/web-dashboard.md: /api/pty WS auth reworded; the Security warning
  now names the loopback-bind boundary + CSRF guard + CORS instead of 'no
  authentication of its own', linking the gated auth section
- features/extending-the-dashboard.md: plugin routes 'require no identity
  auth on a loopback bind' (kept the --host 0.0.0.0 / untrusted-plugin warning)
- zh-Hans mirrors of all four

Left untouched (verified NOT the legacy token): HERMES_DASHBOARD_BASIC_AUTH_SECRET
(basic provider cookies), the basic-auth 'asks for a session token' login hint,
desktop i18n remote-gateway token strings (remote 'token' mode kept), faq /usage,
homeassistant session tokens.

Co-authored-by: Hermes subagent <noreply@nousresearch.com>
This commit is contained in:
Ben 2026-06-16 16:30:01 +10:00
parent 4ad1655211
commit 1443be72f7
8 changed files with 24 additions and 24 deletions

View File

@ -236,30 +236,30 @@ The dashboard uses three endpoints. Useful for scripting:
```bash
# List authenticated providers + curated model lists
curl -H "X-Hermes-Session-Token: $TOKEN" http://localhost:PORT/api/model/options
curl http://localhost:PORT/api/model/options
# Read current main + auxiliary assignments
curl -H "X-Hermes-Session-Token: $TOKEN" http://localhost:PORT/api/model/auxiliary
curl http://localhost:PORT/api/model/auxiliary
# Set the main model
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
curl -X POST -H "Content-Type: application/json" \
-d '{"scope":"main","provider":"openrouter","model":"anthropic/claude-opus-4.7"}' \
http://localhost:PORT/api/model/set
# Override a single auxiliary task
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
curl -X POST -H "Content-Type: application/json" \
-d '{"scope":"auxiliary","task":"vision","provider":"openrouter","model":"google/gemini-2.5-flash"}' \
http://localhost:PORT/api/model/set
# Assign one model to every auxiliary task
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
curl -X POST -H "Content-Type: application/json" \
-d '{"scope":"auxiliary","task":"","provider":"openrouter","model":"google/gemini-2.5-flash"}' \
http://localhost:PORT/api/model/set
# Reset all auxiliary tasks to auto
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
curl -X POST -H "Content-Type: application/json" \
-d '{"scope":"auxiliary","task":"__reset__","provider":"","model":""}' \
http://localhost:PORT/api/model/set
```
The session token is injected into the dashboard HTML at startup and rotates on every server restart. Grab it from the browser devtools (`window.__HERMES_SESSION_TOKEN__`) if you're scripting against a running dashboard.
A local (loopback) dashboard needs no auth for scripting — the curl calls above work as-is against `127.0.0.1`. If you're scripting against a gated (remote / non-loopback) dashboard, authenticate with the session cookie the browser already uses (the gate sets it on login); there is no static token to grab anymore.

View File

@ -743,7 +743,7 @@ Routes are mounted under `/api/plugins/<name>/`, so the above becomes:
- `GET /api/plugins/my-plugin/data`
- `POST /api/plugins/my-plugin/action`
Plugin API routes bypass session-token authentication since the dashboard server binds to localhost by default. **Don't expose the dashboard on a public interface with `--host 0.0.0.0` if you run untrusted plugins** — their routes become reachable too.
Plugin API routes require no identity authentication on a loopback bind — the loopback bind is the security boundary. **Don't expose the dashboard on a public interface with `--host 0.0.0.0` if you run untrusted plugins** — their routes become reachable too.
#### Accessing Hermes internals

View File

@ -571,7 +571,7 @@ The GUI is strictly a **read-through-the-DB + write-through-kanban_db** layer wi
### REST surface
All routes are mounted under `/api/plugins/kanban/` and protected by the dashboard's ephemeral session token:
All routes are mounted under `/api/plugins/kanban/`. On a loopback dashboard they require no credential — the loopback bind is the security boundary. On a gated (non-loopback) dashboard they're authenticated by the session cookie, like every other `/api/` route:
| Method | Path | Purpose |
|---|---|---|
@ -615,7 +615,7 @@ Each key is optional and falls back to the shown default.
The dashboard's HTTP auth middleware [explicitly skips `/api/plugins/`](./extending-the-dashboard#backend-api-routes) — plugin routes are unauthenticated by design because the dashboard binds to localhost by default. That means the kanban REST surface is reachable from any process on the host.
The WebSocket takes one additional step: it requires the dashboard's ephemeral session token as a `?token=…` query parameter (browsers can't set `Authorization` on an upgrade request), matching the pattern used by the in-browser PTY bridge.
The WebSocket follows the same model: on a loopback dashboard it needs no credential, and on a gated dashboard it uses a single-use `?ticket=…` query parameter (minted via `/api/auth/ws-ticket`) because browsers can't set `Authorization` on an upgrade request — matching the pattern used by the in-browser PTY bridge.
If you run `hermes dashboard --host 0.0.0.0`, every plugin route — kanban included — becomes reachable from the network. **Don't do that on a shared host.** The board contains task bodies, comments, and workspace paths; an attacker reaching these routes gets read access to your entire collaboration surface and can also create / reassign / archive tasks.

View File

@ -111,7 +111,7 @@ The **Chat** tab embeds the full Hermes TUI (the same interface you get from `he
**How it works:**
- `/api/pty` opens a WebSocket authenticated with the dashboard's session token
- `/api/pty` opens a WebSocket — on a loopback dashboard it needs no credential; on a gated dashboard it authenticates with a single-use ticket
- The server spawns `hermes --tui` behind a POSIX pseudo-terminal
- Keystrokes travel to the PTY; ANSI output streams back to the browser
- xterm.js's WebGL renderer paints each cell to an integer-pixel grid; mouse tracking (SGR 1006), wide characters (Unicode 11), and box-drawing glyphs all render natively
@ -380,7 +380,7 @@ Creating a shell hook (note the consent checkbox and the run-arbitrary-commands
![New shell hook modal](/img/dashboard/admin-hook-create.png)
:::warning Security
The web dashboard reads and writes your `.env` file, which contains API keys and secrets. It binds to `127.0.0.1` by default — only accessible from your local machine. If you bind to `0.0.0.0`, anyone on your network can view and modify your credentials. The dashboard has no authentication of its own.
The web dashboard reads and writes your `.env` file, which contains API keys and secrets. It binds to `127.0.0.1` by default — only accessible from your local machine. If you bind to `0.0.0.0`, anyone on your network can view and modify your credentials. On a loopback bind there is no identity gate: the loopback bind itself is the security boundary, backed by a `Sec-Fetch-Site` CSRF guard that blocks cross-origin mutating requests and a localhost-only CORS policy that blocks cross-origin reads. To expose the dashboard beyond your machine, bind to a non-loopback address (which engages the [auth gate](#authentication-gated-mode)) rather than relying on loopback.
:::
## `/reload` Slash Command

View File

@ -208,30 +208,30 @@ hermes model # 交互式提供商 + 模型选择器(切换默认值
```bash
# 列出已认证的提供商及精选模型列表
curl -H "X-Hermes-Session-Token: $TOKEN" http://localhost:PORT/api/model/options
curl http://localhost:PORT/api/model/options
# 读取当前主模型及辅助任务分配
curl -H "X-Hermes-Session-Token: $TOKEN" http://localhost:PORT/api/model/auxiliary
curl http://localhost:PORT/api/model/auxiliary
# 设置主模型
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
curl -X POST -H "Content-Type: application/json" \
-d '{"scope":"main","provider":"openrouter","model":"anthropic/claude-opus-4.7"}' \
http://localhost:PORT/api/model/set
# 覆盖单个辅助任务
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
curl -X POST -H "Content-Type: application/json" \
-d '{"scope":"auxiliary","task":"vision","provider":"openrouter","model":"google/gemini-2.5-flash"}' \
http://localhost:PORT/api/model/set
# 将一个模型分配给所有辅助任务
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
curl -X POST -H "Content-Type: application/json" \
-d '{"scope":"auxiliary","task":"","provider":"openrouter","model":"google/gemini-2.5-flash"}' \
http://localhost:PORT/api/model/set
# 将所有辅助任务重置为 auto
curl -X POST -H "Content-Type: application/json" -H "X-Hermes-Session-Token: $TOKEN" \
curl -X POST -H "Content-Type: application/json" \
-d '{"scope":"auxiliary","task":"__reset__","provider":"","model":""}' \
http://localhost:PORT/api/model/set
```
session token 在启动时注入仪表板 HTML每次服务器重启后轮换。如需对运行中的仪表板编写脚本可从浏览器开发者工具中获取`window.__HERMES_SESSION_TOKEN__`
本地(回环)仪表板无需任何认证即可编写脚本——上面的 curl 调用直接针对 `127.0.0.1` 即可工作。如需对受保护的(远程 / 非回环)仪表板编写脚本,请使用浏览器登录时获得的会话 cookie与浏览器使用的同一个进行认证不再有可供获取的静态 token

View File

@ -727,7 +727,7 @@ async def do_action(body: dict):
- `GET /api/plugins/my-plugin/data`
- `POST /api/plugins/my-plugin/action`
插件 API 路由绕过会话 token 认证,因为 dashboard 服务器默认绑定到 localhost。**如果运行不受信任的插件,请勿使用 `--host 0.0.0.0` 将 dashboard 暴露在公共接口上**——其路由也会变得可访问。
插件 API 路由在回环绑定上无需任何身份验证——回环绑定就是安全边界。**如果运行不受信任的插件,请勿使用 `--host 0.0.0.0` 将 dashboard 暴露在公共接口上**——其路由也会变得可访问。
#### 访问 Hermes 内部模块

View File

@ -467,7 +467,7 @@ GUI 严格是一个**通过 DB 读取 + 通过 kanban_db 写入**的层,没有
### REST 接口
所有路由挂载在 `/api/plugins/kanban/`,并受仪表盘的临时会话 token 保护
所有路由挂载在 `/api/plugins/kanban/`。在回环仪表盘上,它们无需任何凭据——回环绑定就是安全边界。在受保护的(非回环)仪表盘上,它们与其他所有 `/api/` 路由一样,由会话 cookie 进行认证
| 方法 | 路径 | 用途 |
|---|---|---|
@ -511,7 +511,7 @@ dashboard:
仪表盘的 HTTP 认证中间件[显式跳过 `/api/plugins/`](./extending-the-dashboard#backend-api-routes) —— 插件路由在设计上是未认证的,因为仪表盘默认绑定到 localhost。这意味着 kanban REST 接口可以从主机上的任何进程访问。
WebSocket 额外增加了一步:它要求仪表盘的临时会话 token 作为 `?token=…` 查询参数(浏览器无法在升级请求上设置 `Authorization`与浏览器内 PTY 桥使用的模式一致。
WebSocket 遵循同样的模型:在回环仪表盘上无需任何凭据;在受保护的仪表盘上,它使用一次性的 `?ticket=…` 查询参数(通过 `/api/auth/ws-ticket` 签发),因为浏览器无法在升级请求上设置 `Authorization`——这与浏览器内 PTY 桥使用的模式一致。
如果你运行 `hermes dashboard --host 0.0.0.0`,每个插件路由 —— 包括 kanban —— 都可以从网络访问。**不要在共享主机上这样做。** 看板包含任务正文、评论和工作区路径;攻击者访问这些路由可以读取你整个协作界面,还可以创建 / 重新分配 / 归档任务。

View File

@ -69,7 +69,7 @@ Chat 标签页是每次 `hermes dashboard` 启动的一部分——内嵌的浏
**工作原理:**
- `/api/pty` 打开一个经 Dashboard 会话 token 认证的 WebSocket
- `/api/pty` 打开一个 WebSocket——在回环仪表盘上无需任何凭据在受保护的仪表盘上它使用一次性 ticket 进行认证
- 服务器在 POSIX 伪终端后面启动 `hermes --tui`
- 按键传输到 PTYANSI 输出流式返回浏览器
- xterm.js 的 WebGL 渲染器将每个单元格绘制到整数像素网格鼠标追踪SGR 1006、宽字符Unicode 11和方框绘制字形均原生渲染
@ -178,7 +178,7 @@ Chat 标签页是每次 `hermes dashboard` 启动的一部分——内嵌的浏
- **Toolsets** — 单独的部分显示内置工具集文件操作、Web 浏览等),包含其活跃/非活跃状态、设置要求和包含的工具列表
:::warning 安全提示
Web Dashboard 会读写包含 API 密钥和机密的 `.env` 文件。它默认绑定到 `127.0.0.1`——只能从本机访问。如果绑定到 `0.0.0.0`,网络上的任何人都可以查看和修改你的凭据。Dashboard 本身没有任何认证机制
Web Dashboard 会读写包含 API 密钥和机密的 `.env` 文件。它默认绑定到 `127.0.0.1`——只能从本机访问。如果绑定到 `0.0.0.0`,网络上的任何人都可以查看和修改你的凭据。在回环绑定上没有身份验证门:回环绑定本身就是安全边界,并由一个 `Sec-Fetch-Site` CSRF 防护(阻止跨源的变更请求)和一个仅限 localhost 的 CORS 策略(阻止跨源读取)作为后盾。要将仪表板暴露到本机之外,请绑定到非回环地址(这会启用[认证门](#authentication-gated-mode)),而不要依赖回环
:::
## `/reload` 斜杠命令