Skip to content

feat(dashboard): support serving under URL prefix via X-Forwarded-Prefix#19450

Closed
cmcgrabby-hue wants to merge 1 commit into
NousResearch:mainfrom
cmcgrabby-hue:mc-auto-13-base-path
Closed

feat(dashboard): support serving under URL prefix via X-Forwarded-Prefix#19450
cmcgrabby-hue wants to merge 1 commit into
NousResearch:mainfrom
cmcgrabby-hue:mc-auto-13-base-path

Conversation

@cmcgrabby-hue

Copy link
Copy Markdown

What

Make the Hermes dashboard work when served under a non-root URL prefix
(e.g. https://mission-control.tilos.com/hermes/) instead of only at
the host root (e.g. https://kanban.tilos.com/).

Why

The Mission Control dashboard at mission-control.tilos.com wants to
surface the Hermes Kanban dashboard as a tab in its own nav, served
inside the same domain so SSO + cookie scope is unified. CF Tunnel
routes mission-control.tilos.com/hermes/* through a small local Caddy
that strips the /hermes/ prefix and forwards to the dashboard with
X-Forwarded-Prefix: /hermes. Before this PR the dashboard 404'd because
the built SPA shipped absolute asset URLs and React Router had no basename.

How

Runtime, not rebuild — same web_dist works at root AND under any prefix.

Server (hermes_cli/web_server.py):

  • New _normalise_prefix() parses + validates X-Forwarded-Prefix
    (rejects .., control chars, oversize values).
  • _serve_index(prefix) rewrites <head> asset paths and injects
    window.__HERMES_BASE_PATH__ for the SPA to read.
  • New serve_css route intercepts CSS asset requests BEFORE the
    StaticFiles mount and rewrites url(/fonts/...) references when a
    prefix is in play.

Frontend (web/src/):

  • lib/api.ts exports HERMES_BASE_PATH (read from window) and prefixes
    it onto every /api/... fetch.
  • plugins/usePlugins.ts prefixes /dashboard-plugins/... script + css
    URLs.
  • main.tsx passes basename={HERMES_BASE_PATH || undefined} to
    <BrowserRouter>.

When X-Forwarded-Prefix is absent (the kanban.tilos.com path), the
prefix is empty and behavior is identical to before.

Verified locally

Single dashboard process at 127.0.0.1:9119, then:

  • curl http://127.0.0.1:9119/kanban → root mode, asset paths absolute
    /assets/... → still works on kanban.tilos.com.
  • curl -H 'X-Forwarded-Prefix: /hermes' http://127.0.0.1:9119/kanban
    → asset paths rewritten to /hermes/assets/...,
    window.__HERMES_BASE_PATH__="/hermes" set,
    CSS url(/fonts/...) rewritten to url(/hermes/fonts/...).
  • End-to-end via https://mission-control.tilos.com/hermes/kanban
    Pages Function → CF Tunnel → local Caddy (strips /hermes) → :9119
    with header → renders correctly.

Out of scope

  • The local Caddy + tunnel ingress + Pages Function live in their
    respective repos and are not touched here.
  • kanban.tilos.com keeps working unchanged as a fallback URL.

Refs: MC-AUTO-13

The Hermes dashboard previously assumed it was served at the root of its
host (e.g. https://kanban.tilos.com/). When mounted behind a path-prefix
reverse proxy (e.g. https://mission-control.tilos.com/hermes/), the SPA
404'd because:

- index.html shipped absolute /assets/index-*.js URLs
- React Router had no basename
- The plugin loader hit /dashboard-plugins/<name>/... at the root host
- CSS in the bundle had absolute url(/fonts/...) references

This patch makes the dashboard prefix-aware at runtime, no rebuild
required. The proxy injects 'X-Forwarded-Prefix: /hermes' on every
request and the Python server:

- Rewrites href/src in served index.html to '${prefix}/assets/...'
- Injects 'window.__HERMES_BASE_PATH__="${prefix}"' for the SPA to read
- Rewrites url() refs in CSS at serve time

The SPA reads window.__HERMES_BASE_PATH__ once at boot and:

- Prefixes all /api/... fetches via api.ts
- Prefixes all /dashboard-plugins/... script/css URLs in usePlugins
- Sets <BrowserRouter basename={...}> so client-side routing works

When no X-Forwarded-Prefix header is present, behavior is unchanged
(empty prefix => serves at root, kanban.tilos.com keeps working).

Refs: MC-AUTO-13
@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) labels May 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/cli CLI entry point, hermes_cli/, setup wizard comp/tui Terminal UI (ui-tui/ + tui_gateway/) P3 Low — cosmetic, nice to have type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants