From 3c73d1852e372d1fe03dc5931d2f95be059caa67 Mon Sep 17 00:00:00 2001 From: Teknium <127238744+teknium1@users.noreply.github.com> Date: Wed, 3 Jun 2026 09:30:20 -0700 Subject: [PATCH] docs: remote desktop connect needs --tui on the backend (#38350) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Desktop App and Web Dashboard remote-connect instructions told users to start the backend with `hermes dashboard --no-open --insecure --host 0.0.0.0`, omitting --tui. Without --tui the embedded-chat WebSockets (/api/ws, /api/pty) are refused, so the desktop passes the /api/status health check and reports the backend "ready" — but chat never works because the socket is closed on connect. - Add --tui to both backend command blocks (with an inline why-comment). - Explain that the desktop chat runs over /api/ws + /api/pty and needs the embedded-chat surface enabled; a plain dashboard/gateway is not enough. - Add a troubleshooting entry for the exact symptom (connects, says ready, chat dead) on both pages. --- website/docs/user-guide/desktop.md | 7 ++++++- website/docs/user-guide/features/web-dashboard.md | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/website/docs/user-guide/desktop.md b/website/docs/user-guide/desktop.md index 2c6cb7f91..af417853f 100644 --- a/website/docs/user-guide/desktop.md +++ b/website/docs/user-guide/desktop.md @@ -113,6 +113,8 @@ By default the app starts and manages its own **local** backend. You can instead The session token is the part that trips people up. **Hermes does not print it for you to copy** — by default the backend mints a fresh random token on every boot and injects it straight into the served HTML, so there is nothing in `config.yaml`, in `/gateway`, or in the logs to grab. For a remote connection you pin the token yourself on the backend, then paste that same value into the app. +The backend also has to be started with **`--tui`** (or `HERMES_DASHBOARD_TUI=1`). The desktop's chat runs over the dashboard's `/api/ws` + `/api/pty` WebSockets, and those endpoints are refused unless the embedded-chat surface is enabled. Without `--tui` the connection still passes the `/api/status` health check and the app reports "Remote Hermes backend is ready" — but chat never works because the WebSocket is closed immediately. A plain `hermes dashboard` or `hermes gateway` is not enough. + ### On the backend (the remote machine) ```bash @@ -125,10 +127,12 @@ chmod 600 ~/.hermes/.env echo "$TOKEN" # copy this value into the desktop app # 2. Run the dashboard bound to a reachable address. +# --tui enables the embedded chat (the /api/ws + /api/pty WebSockets the +# desktop drives) — without it the app connects but chat stays dead. # --insecure is required for any non-loopback bind and keeps the legacy # session-token auth path (a non-loopback bind WITHOUT --insecure engages # the OAuth gate, which ignores the session token). -hermes dashboard --no-open --insecure --host 0.0.0.0 --port 9119 +hermes dashboard --tui --no-open --insecure --host 0.0.0.0 --port 9119 ``` Running the dashboard as a systemd service? Give the unit `EnvironmentFile=%h/.hermes/.env` so the token is in the environment at boot. @@ -151,6 +155,7 @@ The token is stored encrypted in the app's local config; leave the field blank o ### Troubleshooting - **Test fails with 401** — the token doesn't match the backend's `HERMES_DASHBOARD_SESSION_TOKEN`, or the backend is bound non-loopback *without* `--insecure` (OAuth gate is on, ignoring the token). Verify with `curl -s -H "X-Hermes-Session-Token: $TOKEN" http://:9119/api/status` — that should return JSON, not a 401. +- **App says "Remote Hermes backend is ready" but chat does nothing** — the backend was started without `--tui` (or `HERMES_DASHBOARD_TUI=1`). The status probe passes, but the chat WebSocket (`/api/ws` / `/api/pty`) is refused. Restart the backend with `--tui`. - **Connection refused / times out** — the backend bound to `127.0.0.1` (the default) or a firewall/VPN is blocking the port. Bind to `0.0.0.0` or the tailscale IP and open the port to your trusted network. - **No token to copy** — expected. You mint it yourself; Hermes never surfaces the default ephemeral one. diff --git a/website/docs/user-guide/features/web-dashboard.md b/website/docs/user-guide/features/web-dashboard.md index 51ed32fba..3df19d688 100644 --- a/website/docs/user-guide/features/web-dashboard.md +++ b/website/docs/user-guide/features/web-dashboard.md @@ -627,6 +627,8 @@ The "session token" is the dashboard's session token — the same secret the loc The desktop app sends the token as an `X-Hermes-Session-Token` header. The backend accepts it only in legacy session-token mode — i.e. when bound non-loopback **with `--insecure`**. A non-loopback bind *without* `--insecure` engages the [OAuth gate](#oauth-authentication-gated-mode) instead, which ignores the session token. So a remote desktop connection means: `--insecure` + a token you control. +The backend must also be started with **`--tui`** (or `HERMES_DASHBOARD_TUI=1`). The desktop's chat runs over the `/api/ws` + `/api/pty` WebSockets, and those are refused unless the embedded-chat surface is enabled. Without `--tui` the desktop still passes the `/api/status` health check (so the app reports the backend "ready") but the chat WebSocket is closed on connect — connects, looks ready, chat stays dead. A plain `hermes dashboard` or `hermes gateway` is not enough. + ### On the backend (the remote machine) ```bash @@ -640,9 +642,11 @@ chmod 600 ~/.hermes/.env echo "$TOKEN" # copy this value into the desktop app # 2. Run the dashboard bound to a reachable address. +# --tui enables the embedded chat (the /api/ws + /api/pty WebSockets the +# desktop drives) — without it the app connects but chat stays dead. # --insecure is required for any non-loopback bind and keeps the # legacy session-token auth path (instead of the OAuth gate). -hermes dashboard --no-open --insecure --host 0.0.0.0 --port 9119 +hermes dashboard --tui --no-open --insecure --host 0.0.0.0 --port 9119 ``` If you run the dashboard as a systemd service, `~/.hermes/.env` is picked up automatically when the unit has `EnvironmentFile=%h/.hermes/.env`, so the token is in the environment at boot. @@ -677,6 +681,7 @@ Both must be set together — setting only the URL is an error. - **"Remote gateway incomplete"** — you haven't entered both a URL and a token. The token only needs re-entering if `remoteTokenSet` is false (no saved token yet). - **Test remote fails with 401** — the token doesn't match the backend's `HERMES_DASHBOARD_SESSION_TOKEN`, or the backend is running *without* `--insecure` on a non-loopback bind (the OAuth gate is on and ignores the session token). Confirm `--insecure` and that the env var is actually loaded (`curl -s -H "X-Hermes-Session-Token: $TOKEN" http://:9119/api/status` should return JSON, not 401). +- **Backend reports "ready" but chat does nothing** — the backend was started without `--tui` (or `HERMES_DASHBOARD_TUI=1`), so `/api/status` answers but the chat WebSocket (`/api/ws` / `/api/pty`) is refused. Restart the backend with `--tui`. - **Connection refused / times out** — the backend bound to `127.0.0.1` (the default) instead of a reachable address, or a firewall/VPN is blocking the port. Bind to `0.0.0.0` or the tailscale IP and open the port to your trusted network. - **No token anywhere to copy** — expected. You mint it (`HERMES_DASHBOARD_SESSION_TOKEN`); Hermes never auto-surfaces the default ephemeral one.