Skip to content

fix(dashboard): reload loopback tabs after stale session-token closes - #78313

Merged
kshitijk4poor merged 3 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/54022-dashboard-stale-token
Aug 4, 2026
Merged

fix(dashboard): reload loopback tabs after stale session-token closes#78313
kshitijk4poor merged 3 commits into
NousResearch:mainfrom
kshitijk4poor:salvage/54022-dashboard-stale-token

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Summary

Dashboard chat tabs now auto-reload once when the PTY/events/gateway WebSocket closes with a 4401 (stale session token) after a hermes dashboard restart, instead of blanking and looping on every focus/visibility change.

Root cause

Every dashboard restart mints a new _SESSION_TOKEN in web_server.py. The browser tab still holds the old token in window.__HERMES_SESSION_TOKEN__. REST fetches already handled this (one-shot reload on 401), but WebSocket connections had no equivalent — the PTY reconnect logic fired reconnectPty() on every visibilitychange/focus/pageshow/online event, each attempt rejected with token_mismatch, leaving the terminal blank until the user manually reloaded.

Changes

  • web/src/lib/dashboard-auth-reload.ts (new): shared maybeReloadForLoopbackWsAuthFailure() — reloads the page once on a 4401 WS close in loopback mode (no-op in gated mode). Also extracts attemptDashboardTokenReloadOnce() + clearDashboardTokenReloadAttempt() so the REST 401 path in api.ts uses the same logic.
  • web/src/pages/ChatPage.tsx: call maybeReloadForLoopbackWsAuthFailure(ev.code) before setting the "Auth failed" banner on 4401 PTY close.
  • web/src/components/ChatSidebar.tsx: same guard on the /api/events WS close handler.
  • web/src/lib/gatewayClient.ts: wire onSocketClose into the JSON-RPC gateway client so the /api/ws sidecar also auto-reloads.
  • apps/shared/src/json-rpc-gateway.ts: add onSocketClose option to GatewayClientOptions so the close handler can intercept the default closed-state transition.
  • web/src/lib/api.ts: refactor the inline 401 reload logic to use the shared dashboard-auth-reload module.

Test plan

Closes #53972

izumi0uu and others added 2 commits August 4, 2026 13:22
Loopback dashboard tabs now share one one-shot stale-token recovery path across REST 401s, the PTY socket, the structured event socket, and the shared JSON-RPC gateway wrapper. The shared client exposes only an optional close-event interception hook; the dashboard remains responsible for deciding that loopback 4401 means reload.

Constraint: Current main delegates the web gateway to apps/shared JsonRpcGatewayClient, and NousResearch#54022 review requires a shared-client-compatible close-code hook plus direct ChatSidebar event-socket coverage.
Rejected: Restore the dashboard's old direct WebSocket implementation | stale against the shared JSON-RPC client and would duplicate transport behavior.
Confidence: high
Scope-risk: moderate
Directive: Keep stale-token policy dashboard-specific; the shared JSON-RPC client should expose close events without learning dashboard auth semantics.
Tested: npm --workspace web test (21 files, 106 tests); focused stale-token tests (5 files, 14 tests); npm --workspace web run typecheck; npm --workspace @hermes/shared run lint; npm --workspace @hermes/shared run typecheck; focused web eslint; git diff --check.
Not-tested: Manual browser smoke test across a real dashboard restart.
react-router v7 exports MemoryRouter from 'react-router', not
'react-router-dom'. The test was written when the repo still imported
from 'react-router-dom' (4000+ commits ago).
@alt-glitch alt-glitch added type/bug Something isn't working comp/dashboard Web dashboard / control panel UI (dashboard/, landing) area/auth Authentication, OAuth, credential pools P3 Low — cosmetic, nice to have sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data labels Aug 4, 2026
Sibling site missed by PR NousResearch#54022 — /api/console WebSocket in
HermesConsoleModal.tsx has the same buildWsUrl → stale-token → 4401
close path as the PTY and events WebSockets. Without this guard,
opening the console after a dashboard restart shows 'Console closed
(4401). auth: token_mismatch' with no recovery.
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) August 4, 2026 09:49
@kshitijk4poor
kshitijk4poor merged commit b8b17b8 into NousResearch:main Aug 4, 2026
31 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/auth Authentication, OAuth, credential pools comp/dashboard Web dashboard / control panel UI (dashboard/, landing) P3 Low — cosmetic, nice to have sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] Dashboard chat loses random chunks + token_mismatch spam after dashboard restart

3 participants