Skip to content

fix(desktop): use served dashboard token for websocket auth - #44516

Closed
OutThisLife wants to merge 1 commit into
mainfrom
bb/desktop-served-token
Closed

fix(desktop): use served dashboard token for websocket auth#44516
OutThisLife wants to merge 1 commit into
mainfrom
bb/desktop-served-token

Conversation

@OutThisLife

Copy link
Copy Markdown
Collaborator

Salvage of #43720 (rebased onto current main, package.json conflict resolved). Closes #43720.

What does this PR do?

Fixes a desktop startup / WebSocket auth failure where the local dashboard serves a different session token than the one the Electron main process generated when spawning the backend. GET /api/status still returns 200, but the dashboard HTML injects a different window.__HERMES_SESSION_TOKEN__; the renderer then attempts the /api/ws handshake with the stale spawn-time token and the gateway rejects it, surfacing as Could not connect to Hermes gateway.

Why the fix is correct (validation)

The server side already honors the spawn-time token at import:

# hermes_cli/web_server.py
_SESSION_TOKEN = os.environ.get("HERMES_DASHBOARD_SESSION_TOKEN") or secrets.token_urlsafe(32)

…and injects _SESSION_TOKEN into the SPA HTML. So the served token only diverges from the desktop's spawn-time token when the app ends up bound to a backend it did not directly spawn with that env — e.g. an already-running / rerouted machine dashboard or a reused pooled instance, each carrying its own _SESSION_TOKEN. In those cases the renderer's source of truth is the served token, not the env token.

This fix reads the injected token from the served HTML after readiness and uses it for desktop session state + /api/ws URLs, falling back to the spawn-time token if discovery fails or the HTML has no token. It is strictly additive — the new path can only do better than the old one, never worse.

Changes Made

  • apps/desktop/electron/dashboard-token.cjs — new helper: fetch the served dashboard index, extract the JSON-encoded window.__HERMES_SESSION_TOKEN__, preserve path prefixes, fall back safely. HTTP/HTTPS only, per-request timeout.
  • apps/desktop/electron/main.cjs — both the primary (startHermes) and pooled (spawnPoolBackend) local-backend paths resolve the served token after readiness and use it for returned state + /api/ws URLs.
  • apps/desktop/electron/dashboard-token.test.cjs — unit coverage (extraction, escaping, malformed-HTML fallback, differing-token logging, fetch-error propagation, protocol rejection).
  • apps/desktop/electron/windows-child-process.test.cjs — normalize CRLF→LF when reading source so the Windows child-process audit holds on core.autocrlf=true checkouts.
  • apps/desktop/package.json — wires dashboard-token.test.cjs into test:desktop:platforms (resolved against main, which has since added other entries to that line).

Validation

cd apps/desktop && npm run test:desktop:platforms153 passed, 1 skipped, 0 failed (includes the 9 new token tests and the CRLF-normalized Windows audit).

Notes

Salvaged to preserve original authorship (commit authored by @jeffrobodie-glitch; PR opened by @lEWFkRAD) — credit to both. Reviewed/approved on the original #43720 by @austinpickett. The only change from that PR is the trivial package.json line merge against current main.

@github-actions

Copy link
Copy Markdown
Contributor

🔎 Lint report: bb/desktop-served-token vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 10764 on HEAD, 10764 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 5637 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@OutThisLife

Copy link
Copy Markdown
Collaborator Author

Superseded by #44529, which carries this exact commit (authorship preserved) plus a hardening commit: served-token mismatch from a dead child is now treated as a foreign backend squatting the port (boot fails loudly) instead of silently adopting the stranger's token. Mismatch with a live child keeps this PR's adopt-and-log behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants