docs: remote desktop connect needs --tui on the backend (#38350)

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.
This commit is contained in:
Teknium
2026-06-03 09:30:20 -07:00
committed by GitHub
parent df848bd2da
commit 3c73d1852e
2 changed files with 12 additions and 2 deletions

View File

@ -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://<host>: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.

View File

@ -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://<host>: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.