hermes - 💡(How to fix) Fix WebSocket connections rejected when dashboard bound to 0.0.0.0 with --insecure

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…

Error Message

  1. Server logs show no explicit error — the WS handshake is simply not accepted

Code Example

def _ws_client_is_allowed(ws):
    if getattr(app.state, "auth_required", False):
        return True
    bound_host = getattr(app.state, "bound_host", None)
    if bound_host in {"0.0.0.0", "::"}:
        return True  # operator opted into all-interfaces via --insecure
    client_host = ws.client.host if ws.client else ""
    ...
RAW_BUFFERClick to expand / collapse

Bug Description

When running hermes dashboard --host 0.0.0.0 --insecure, the HTTP middleware correctly accepts all hosts (line 214 in hermes_cli/web_server.py_is_accepted_host returns True for 0.0.0.0 binds). However, WebSocket upgrade requests are rejected for any non-loopback client IP.

The _ws_client_is_allowed function (line 3371) only checks app.state.auth_required before falling through to a loopback-only client IP check. When --insecure is passed, auth_required is False, so the early return does not trigger, and non-loopback clients are rejected at client_host in _LOOPBACK_HOSTS (line 3391).

This means the dashboard web UI loads fine over LAN/VPN/Twingate (HTTP passes), but the chat tab WebSocket connection is silently dropped — it appears to connect but never receives data.

Steps to Reproduce

  1. Run hermes dashboard --host 0.0.0.0 --insecure
  2. Open the dashboard from a non-loopback client (e.g. LAN IP, VPN, or Twingate)
  3. The web UI loads, but the chat tab shows a disconnected state
  4. Server logs show no explicit error — the WS handshake is simply not accepted

Expected Behavior

_ws_client_is_allowed should mirror _is_accepted_host logic: when bound_host is 0.0.0.0 or :: (the all-interfaces bind), skip the loopback client IP check, since the operator has explicitly opted in via --insecure.

Proposed Fix

In _ws_client_is_allowed, add a bound_host check alongside the existing auth_required check:

def _ws_client_is_allowed(ws):
    if getattr(app.state, "auth_required", False):
        return True
    bound_host = getattr(app.state, "bound_host", None)
    if bound_host in {"0.0.0.0", "::"}:
        return True  # operator opted into all-interfaces via --insecure
    client_host = ws.client.host if ws.client else ""
    ...

Additional Note

HERMES_DASHBOARD_INSECURE=1 is referenced in test files but does not appear to be consumed anywhere in the application code. If it is intended as a supported config path, it may need wiring up as well.

Environment

  • Hermes Agent v0.15.1
  • Debian Linux, systemd service with --host 0.0.0.0 --insecure

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

hermes - 💡(How to fix) Fix WebSocket connections rejected when dashboard bound to 0.0.0.0 with --insecure