hermes - 💡(How to fix) Fix TUI remote gateway attach (HERMES_TUI_GATEWAY_URL) fails with 404 — /api/ws missing from APIServerAdapter

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Root Cause

  1. Two separate HTTP stacks exist:

    • gateway/platforms/api_server.pyaiohttp server, REST-only, started by the gateway process. Binds to API_SERVER_PORT.
    • hermes_cli/web_server.pyFastAPI + uvicorn server, contains @app.websocket("/api/ws") (line ~3582) and delegates to tui_gateway.ws.handle_ws. This is started by hermes webui / hermes dashboard.
  2. The documented HERMES_TUI_GATEWAY_URL feature conflates these two stacks. The WebSocket handler (tui_gateway/ws.py) is designed to be mounted into a FastAPI/Starlette app, not into the aiohttp-based API server. When the gateway runs in api_server mode, there is no FastAPI instance to mount it on.

  3. The APIServerAdapter.start() method (around line 3488 in api_server.py) builds an aiohttp.web.Application and adds health, chat completion, cron job, and run endpoints — but no WebSocket route.


Fix Action

Fix / Workaround

  • User expectation gap: Official docs describe a remote attach workflow that cannot succeed with the current code.
  • Use case blocked: Users who want to run a lightweight TUI client on their local machine (e.g., Windows WSL2) while the heavy gateway (models, tools, credentials) runs on a remote Linux server are unable to do so.
  • Workaround confusion: Users may incorrectly point HERMES_TUI_GATEWAY_URL at the WebUI server (port 80/8080/8787 depending on setup), but that is a different product surface (browser dashboard) and also not the intended attach target.

Option A: Add WebSocket support to APIServerAdapter Implement an aiohttp-compatible WebSocket endpoint in gateway/platforms/api_server.py that reuses tui_gateway.ws.WSTransport and tui_gateway.server.dispatch. This keeps the architecture unified — one gateway process serves both REST API clients (OpenAI compat) and TUI attach clients.

Code Example

export HERMES_TUI_GATEWAY_URL="ws://<gateway-host>:<port>/api/ws?token=<key>"

---

API_SERVER_ENABLED=true
   API_SERVER_KEY=change-me-local-dev
   API_SERVER_PORT=8642
   API_SERVER_HOST=0.0.0.0

---

export HERMES_TUI_GATEWAY_URL="ws://<gateway-host>:8642/api/ws?token=<key>"

---

"GET /api/ws?token=*** HTTP/1.1" 404 174
RAW_BUFFERClick to expand / collapse

Bug Description

The documentation at TUI → Attaching to a running gateway instructs users to connect a local TUI to a remote Hermes gateway by setting:

export HERMES_TUI_GATEWAY_URL="ws://<gateway-host>:<port>/api/ws?token=<key>"

However, this endpoint returns HTTP 404 because the APIServerAdapter (the component that serves the gateway's HTTP API) does not register a WebSocket route at /api/ws. The route only exists in the local FastAPI-based web server (hermes_cli/web_server.py), which is a separate process meant for the browser dashboard, not for the gateway's api_server platform.

This makes remote TUI attach to a running gateway effectively impossible with the current codebase, despite being documented.


Environment

  • Hermes version: v0.14.0 (also confirmed on latest main as of 2026-05-27)
  • Python: 3.11
  • Gateway mode: hermes gateway run (systemd user service)
  • API Server config: API_SERVER_ENABLED=true, API_SERVER_KEY=<key>, API_SERVER_PORT=8642

Steps to Reproduce

  1. On the gateway host, enable the API server in ~/.hermes/.env:
    API_SERVER_ENABLED=true
    API_SERVER_KEY=change-me-local-dev
    API_SERVER_PORT=8642
    API_SERVER_HOST=0.0.0.0
  2. Start the gateway: hermes gateway run
  3. On a remote client (e.g., WSL2, another machine in the same network), install Hermes TUI dependencies and set:
    export HERMES_TUI_GATEWAY_URL="ws://<gateway-host>:8642/api/ws?token=<key>"
  4. Launch the TUI: hermes --tui
  5. Observe the gateway logs:
    "GET /api/ws?token=*** HTTP/1.1" 404 174
    The TUI reports: gateway websocket unavailable / gateway websocket connection failed.

Expected Behavior

The gateway should accept the WebSocket upgrade request at /api/ws and establish a bidirectional JSON-RPC transport that mirrors the local stdio interface used by tui_gateway.entry. This would allow a remote TUI to interact with the gateway's sessions, tools, and agent loop.


Actual Behavior

The APIServerAdapter only registers REST endpoints (/v1/chat/completions, /health, /api/jobs, /v1/runs, etc.) via aiohttp. There is no add_get("/api/ws") or WebSocket handler anywhere in gateway/platforms/api_server.py.

The TUI client (ui-tui/src/gatewayClient.ts) tries to open a WebSocket to the configured HERMES_TUI_GATEWAY_URL, gets a 404 from aiohttp, and exits with gateway websocket unavailable.


Root Cause Analysis

  1. Two separate HTTP stacks exist:

    • gateway/platforms/api_server.pyaiohttp server, REST-only, started by the gateway process. Binds to API_SERVER_PORT.
    • hermes_cli/web_server.pyFastAPI + uvicorn server, contains @app.websocket("/api/ws") (line ~3582) and delegates to tui_gateway.ws.handle_ws. This is started by hermes webui / hermes dashboard.
  2. The documented HERMES_TUI_GATEWAY_URL feature conflates these two stacks. The WebSocket handler (tui_gateway/ws.py) is designed to be mounted into a FastAPI/Starlette app, not into the aiohttp-based API server. When the gateway runs in api_server mode, there is no FastAPI instance to mount it on.

  3. The APIServerAdapter.start() method (around line 3488 in api_server.py) builds an aiohttp.web.Application and adds health, chat completion, cron job, and run endpoints — but no WebSocket route.


Impact

  • User expectation gap: Official docs describe a remote attach workflow that cannot succeed with the current code.
  • Use case blocked: Users who want to run a lightweight TUI client on their local machine (e.g., Windows WSL2) while the heavy gateway (models, tools, credentials) runs on a remote Linux server are unable to do so.
  • Workaround confusion: Users may incorrectly point HERMES_TUI_GATEWAY_URL at the WebUI server (port 80/8080/8787 depending on setup), but that is a different product surface (browser dashboard) and also not the intended attach target.

Suggested Fix Directions

Option A: Add WebSocket support to APIServerAdapter Implement an aiohttp-compatible WebSocket endpoint in gateway/platforms/api_server.py that reuses tui_gateway.ws.WSTransport and tui_gateway.server.dispatch. This keeps the architecture unified — one gateway process serves both REST API clients (OpenAI compat) and TUI attach clients.

Option B: Clarify documentation If remote TUI attach is intentionally unsupported, remove or reword the "Attaching to a running gateway" section and explain that HERMES_TUI_GATEWAY_URL only works with a locally spawned gateway or with the hermes webui FastAPI server (which requires a separate command and is browser-focused).

Option C: Separate tui_gateway listener Allow tui_gateway.ws to spin up its own minimal uvicorn/starlette listener (perhaps on a configurable sub-port or path) inside the gateway process when API_SERVER_ENABLED=true, so TUI clients can attach without polluting the main REST API surface.


Additional Context

  • The tui_gateway/ws.py module already exists and is well-abstracted (expects any WebSocket-like object with .accept(), .receive_text(), .send_text(), .close()).
  • The TUI's gatewayClient.ts (line 33) simply reads HERMES_TUI_GATEWAY_URL and passes it to the native WebSocket constructor. No additional handshake or protocol negotiation is required beyond the JSON-RPC wire format.
  • A quick grep across the repository confirms no other platform adapter besides web_server.py mounts the /api/ws route.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING