hermes-agent/web/src
Ben a6e47314f9 fix(dashboard): sanction plugin WS/upload auth via SDK helpers (gated mode)
Dashboard plugins (kanban, hermes-achievements) read
window.__HERMES_SESSION_TOKEN__ directly and hand-assembled WebSocket
URLs with ?token=. That works in loopback/--insecure mode but is
rejected on OAuth-gated deployments, where the session token is absent
and _ws_auth_ok only accepts single-use ?ticket= auth. The result was
401s on plugin REST calls and 1008/403 on the kanban live-events WS
whenever the dashboard ran behind OAuth (e.g. hosted Fly agents).

Make the plugin SDK the single sanctioned auth surface:

- web/src/lib/api.ts: add authedFetch() (raw Response for FormData
  uploads / blob downloads, token-or-cookie auth, no throw / no 401
  redirect) and buildWsUrl() (assembles a ws(s):// URL with the correct
  auth param for the active mode — fresh single-use ticket in gated
  mode, token in loopback).
- web/src/plugins/registry.ts: expose authedFetch, buildWsUrl,
  buildWsAuthParam, and sdkVersion on window.__HERMES_PLUGIN_SDK__;
  add SDK_CONTRACT_VERSION.
- web/src/plugins/sdk.d.ts: hand-authored typed contract for the
  plugin SDK + registry globals (single source of truth for the
  Window declarations).
- plugins/kanban + hermes-achievements dist bundles: stop reading the
  session token directly; route uploads/downloads through
  SDK.authedFetch and the live-events WS through SDK.buildWsUrl.
- plugins/kanban plugin_api.py: _ws_upgrade_authorized() delegates the
  /events WS upgrade to the canonical web_server._ws_auth_ok gate, so
  it transparently accepts loopback token / gated ticket / internal
  credential and can never drift from core auth again.
- tests: guard test asserting no plugin dist reads
  __HERMES_SESSION_TOKEN__ directly; kanban gated-ticket WS test.

Verified live on a gated staging Fly agent: kanban /events upgrades
101 with a minted ticket (ticket_len=43, ws_auth_ok=True) where the
old code got 403.
2026-06-03 16:59:36 -07:00
..
components feat(dashboard): nous-blue theme, bulk sessions, schedule picker (#37383) 2026-06-02 12:37:40 -04:00
contexts fix(dashboard): surface Docker update guidance instead of generic failure (#34347) (#37085) 2026-06-02 10:36:10 +10:00
hooks Merge remote-tracking branch 'origin/main' into refactor/use-ds-primitives 2026-05-28 14:20:49 -04:00
i18n feat(dashboard): enrich profiles dashboard and de-dupe channel env vars (#37872) 2026-06-03 10:37:36 -04:00
lib fix(dashboard): sanction plugin WS/upload auth via SDK helpers (gated mode) 2026-06-03 16:59:36 -07:00
pages feat(cli): make hermes portal the human-readable Portal onboarding alias 2026-06-04 01:19:28 +05:30
plugins fix(dashboard): sanction plugin WS/upload auth via SDK helpers (gated mode) 2026-06-03 16:59:36 -07:00
themes feat(dashboard): nous-blue theme, bulk sessions, schedule picker (#37383) 2026-06-02 12:37:40 -04:00
App.tsx feat(dashboard): Channels page — set up every gateway messaging channel from the browser (#37211) 2026-06-01 23:41:35 -07:00
index.css feat(dashboard): nous-blue theme, bulk sessions, schedule picker (#37383) 2026-06-02 12:37:40 -04:00
main.tsx fix(dashboard): remove country flags from language picker (#29997) 2026-05-21 13:10:52 -07:00