Summary
The Vite dev proxy in vite.config.ts does not forward (or inject) the Authorization header on /api/* requests to the Hermes Agent gateway. Result: every dashboard data fetch (/api/sessions, /api/history, /api/connection-status, /api/skills, /api/files) returns 401 when API_SERVER_KEY / HERMES_API_TOKEN is set on the gateway. This affects any Hermes Workspace deployment where the agent is auth-gated — including every production and Docker setup.
The token cannot be injected client-side (Vite only ships VITE_* env vars to the browser bundle, and shipping HERMES_API_TOKEN to the browser would leak the gateway bearer token to every visitor). It must be injected server-side by the Vite middleware.
Affected setups
- Workspace running
pnpm dev (vite) against a Hermes Agent with API_SERVER_KEY set → broken, no /api/* data loads
- Docker deployments with
hermes-agent exposing API_SERVER_KEY → broken, dashboard renders empty/error
- Any setup where
HERMES_DASHBOARD_URL / HERMES_API_TOKEN is set in workspace .env → broken
Dev-only setups (no API_SERVER_KEY on the agent) are unaffected, which is why this never shows up in the workspace's own development loop.
Root cause
vite.config.ts defines the dev proxy like this:
// line ~525
'/api/claude-proxy': {
target: proxyTarget,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/claude-proxy/, ''),
},
The browser sends GET /api/sessions. Vite proxies it to http://127.0.0.1:8642/sessions. The browser cannot include Authorization because the token lives in HERMES_API_TOKEN (server-side env), not VITE_HERMES_API_TOKEN (client-side). The proxy forwards headers as-is. The gateway responds 401.
For comparison, the same file's connection-status probe (the one that drives the green/red "API connected" indicator) does inject auth correctly — it was added in commit ce4fab30 ("fix(vite): point /api/sessions probe at dashboard URL, add auth header & ignore noise", May 4 2026). The fix only landed for the probe, not the runtime proxy.
Reproduction
# Prereqs: Hermes Agent running on :8642 with API_SERVER_KEY set.
# Hermes Workspace running via `pnpm dev` on :3000.
# HERMES_API_TOKEN set in workspace .env.
# 1. Hit the proxy from the browser's perspective (same as the UI does):
$ curl -s http://127.0.0.1:3000/api/sessions
{"ok":false,"error":"Unauthorized"}
# 2. Confirm the upstream actually rejects unauthenticated requests:
$ curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1:8642/api/sessions
401
# 3. Confirm the upstream accepts auth:
$ curl -s -o /dev/null -w "%{http_code}\n" -H "Authorization: Bearer $HERMES_API_TOKEN" http://127.0.0.1:8642/api/sessions
200
In the browser, this surfaces as a banner: Failed to load sessions. Hermes Agent dashboard /api/sessions?limit=50&offset=0: 401 {"error":{"message":"Invalid API key",...}}
Suggested fix
Server-side token injection in the Vite proxy. Roughly:
// vite.config.ts
const claudeApiToken =
env.HERMES_API_TOKEN?.trim() || env.CLAUDE_API_TOKEN?.trim() || ''
const apiProxyOptions: ProxyOptions = {
target: proxyTarget,
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api\/claude-proxy/, ''),
configure: (proxy) => {
if (claudeApiToken) {
proxy.on('proxyReq', (proxyReq) => {
proxyReq.setHeader('Authorization', `Bearer ${claudeApiToken}`)
})
}
},
}
// Then use { ...apiProxyOptions } for both /api/claude-proxy and the
// /api fallback block.
HERMES_API_TOKEN is already loaded into env via loadEnv(mode, process.cwd(), '') at the top of the config file (line ~85) and is used by the existing probe. Same source, same precedence rules, no new env vars.
Related
Environment
outsourc-e/hermes-workspace v2.3.0 (tag)
- Hermes Agent latest
- Linux native,
pnpm dev
Summary
The Vite dev proxy in
vite.config.tsdoes not forward (or inject) theAuthorizationheader on/api/*requests to the Hermes Agent gateway. Result: every dashboard data fetch (/api/sessions,/api/history,/api/connection-status,/api/skills,/api/files) returns 401 whenAPI_SERVER_KEY/HERMES_API_TOKENis set on the gateway. This affects any Hermes Workspace deployment where the agent is auth-gated — including every production and Docker setup.The token cannot be injected client-side (Vite only ships
VITE_*env vars to the browser bundle, and shippingHERMES_API_TOKENto the browser would leak the gateway bearer token to every visitor). It must be injected server-side by the Vite middleware.Affected setups
pnpm dev(vite) against a Hermes Agent withAPI_SERVER_KEYset → broken, no/api/*data loadshermes-agentexposingAPI_SERVER_KEY→ broken, dashboard renders empty/errorHERMES_DASHBOARD_URL/HERMES_API_TOKENis set in workspace.env→ brokenDev-only setups (no
API_SERVER_KEYon the agent) are unaffected, which is why this never shows up in the workspace's own development loop.Root cause
vite.config.tsdefines the dev proxy like this:The browser sends
GET /api/sessions. Vite proxies it tohttp://127.0.0.1:8642/sessions. The browser cannot includeAuthorizationbecause the token lives inHERMES_API_TOKEN(server-side env), notVITE_HERMES_API_TOKEN(client-side). The proxy forwards headers as-is. The gateway responds 401.For comparison, the same file's connection-status probe (the one that drives the green/red "API connected" indicator) does inject auth correctly — it was added in commit
ce4fab30("fix(vite): point /api/sessions probe at dashboard URL, add auth header & ignore noise", May 4 2026). The fix only landed for the probe, not the runtime proxy.Reproduction
In the browser, this surfaces as a banner:
Failed to load sessions. Hermes Agent dashboard /api/sessions?limit=50&offset=0: 401 {"error":{"message":"Invalid API key",...}}Suggested fix
Server-side token injection in the Vite proxy. Roughly:
HERMES_API_TOKENis already loaded intoenvvialoadEnv(mode, process.cwd(), '')at the top of the config file (line ~85) and is used by the existing probe. Same source, same precedence rules, no new env vars.Related
Cannot read properties of undefined (reading 'map')on the dashboard). Same repro on my setup. Fix in fix(server): accept OpenAI-style list shape for gateway sessions/messages #577 handles the response-shape case; this issue handles the auth case. Both need to land for the dashboard to render on an auth-gated backend.ce4fab30only addressed the probe, not the runtime proxy. This issue is the runtime-proxy half of that fix.Environment
outsourc-e/hermes-workspacev2.3.0 (tag)pnpm dev