Nico ccee249618 v0.6.42: Hermes chat UI — Vue3/TS/Vite, audio STT/TTS, sidebar rail, MCP event loop
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 19:35:10 +02:00
..

Hermes Backend

WebSocket gateway between browser clients and the OpenClaw agent runtime.

Stack

  • Bun — runtime, HTTP server, WebSocket server (native, no npm deps)
  • TypeScript — all source files, run directly by Bun (no compile step)

Files

File Role
server.ts Main entry — HTTP + WebSocket via Bun.serve
gateway.ts Upstream connection to OpenClaw gateway
auth.ts Token auth, session tokens, OTP
session-sm.ts Per-connection state machine
session-watcher.ts JSONL session tail + event dispatch
session-watcher.ts JSONL session tail + prev session lookup
hud-builder.ts HUD event factory, result builders
message-filter.ts Filter sensitive values from tool output
system-access.ts System access request/approve flow
mcp/index.ts MCP Streamable HTTP server, auth, routing
mcp/dev.ts Health, subscribe, push_state, chat tools
mcp/events.ts In-memory event queue + long-poll
mcp/system.ts Start/stop/restart/deploy tools
mcp/fs.ts Remote file read/write/edit/grep/ls
mcp/deck.ts Nextcloud Deck integration
mcp/takeover.ts Browser takeover bridge

HTTP Endpoints (added in 0.6.x)

Endpoint Auth Purpose
POST /api/tts session ElevenLabs TTS generation
GET /api/session-history session Previous session messages
POST /api/dev/counter session Push counter events to MCP
POST /api/dev/broadcast session Push WS state to browsers

Dev Setup

cd projects/hermes/backend
NODE_TLS_REJECT_UNAUTHORIZED=0 PORT=3003 ~/.bun/bin/bun --watch run server.ts
  • --watch — auto-restarts on file change (rsync push → instant reload)
  • NODE_TLS_REJECT_UNAUTHORIZED=0 — required because the OpenClaw gateway uses a self-signed TLS cert
  • Default port: 3001 (prod) / 3003 (dev)
  • Requires a running OpenClaw gateway on :18789

Dev workflow (from Titan host)

# Pull remote → local mirror
rsync -avz --exclude='node_modules' --exclude='dist' --exclude='.git' \
  openclaw:~/.openclaw/workspace-titan/projects/hermes/ \
  /d/ClaudeCode/Titan/Openclaw/workspace-titan/projects/hermes/

# Edit locally (Claude Code Read/Edit tools — no escaping issues)

# Push local → remote (bun --watch restarts BE, Vite HMR updates FE)
rsync -avz --exclude='node_modules' --exclude='dist' --exclude='.git' \
  /d/ClaudeCode/Titan/Openclaw/workspace-titan/projects/hermes/backend/ \
  openclaw:~/.openclaw/workspace-titan/projects/hermes/backend/

Production

Managed by systemd:

sudo systemctl status openclaw-web-gateway.service
sudo systemctl restart openclaw-web-gateway.service
journalctl -u openclaw-web-gateway.service -f

Health check:

curl https://chat.jqxp.org/health

Architecture

Browser (WebSocket / HTTP)
    │
    ▼
server.ts ─── Bun.serve()
    │
    ├── HTTP routes
    │     /health, /agents
    │     /api/auth, /api/auth/verify, /api/auth/logout
    │     /api/viewer/token, /api/viewer/tree, /api/viewer/file
    │
    └── WebSocket (/ws)
          │
          ├── auth.ts          token validation, session tokens
          ├── gateway.ts       RPC → openclaw gateway (:18789, WSS)
          ├── session-sm.ts    state: IDLE / AGENT_RUNNING / HANDOVER_* / SWITCHING
          └── session-watcher.ts  tails .jsonl, emits events to WS client

WebSocket Protocol

Client → Server

Type Auth Description
connect No Identify by username
auth No Identify by session/static token
message Yes Send message to agent
stop Yes Request agent stop
kill Yes Kill agent turn
switch_agent Yes Switch to different agent
handover_request Yes Ask agent to write HANDOVER.md
new Yes Reset session
new_with_handover Yes Handover then reset
cancel_handover Yes Cancel pending handover
ping No Keepalive
stats_request No OpenRouter credits + model info
disco_request Yes Disconnect gateway (triggers reconnect)
disco_chat_request Yes Close WebSocket

Server → Client

Type Description
ready Auth success — session info, agents list
session_state State machine transition
sent Message accepted by gateway
delta Streaming text chunk from agent
done Agent turn complete
tool Tool call or result (action: call/result)
finance_update Per-turn cost estimate
handover_context HANDOVER.md content on connect
handover_done Handover written, content included
handover_writing Handover in progress
new_ok Session reset acknowledged
switch_ok Agent switch confirmed
viewer_file_changed Watched file changed (viewer feature)
stats OpenRouter credit + model data
pong Ping reply
killed / stopped Agent turn terminated
error Error with code and message

HTTP Endpoints

Method Path Auth Description
GET /health None Status, version, gateway state
GET /agents None Agent list with models
POST /api/auth None Login with static token
POST /api/auth/verify None Verify OTP challenge
POST /api/auth/logout Session Revoke session token
POST /api/viewer/token Session Issue fstoken for file viewer
GET /api/viewer/tree fstoken List files/dirs in viewer root
GET /api/viewer/file fstoken Serve file content or PDF
HEAD /api/viewer/file fstoken Check file existence/type

Viewer

Read-only file browser exposed over HTTP. Roots:

Key Path
shared /home/openclaw/.openclaw/shared
titan /home/openclaw/.openclaw/workspace-titan
adoree /home/openclaw/.openclaw/workspace-adoree
alfred /home/openclaw/.openclaw/workspace-alfred
ash /home/openclaw/.openclaw/workspace-ash
eras /home/openclaw/.openclaw/workspace-eras
willi /home/openclaw/.openclaw/workspace-willi

Allowed extensions: .md .txt .ts .js .json .sh .py .pdf .css .woff2 .ttf .otf Max file size: 2MB. Live reload via fs.watch pushed over WebSocket (viewer_file_changed).

Session State Machine

IDLE
  ├─ message            → AGENT_RUNNING
  ├─ handover_request   → HANDOVER_PENDING
  └─ new / new_with_handover → SWITCHING

AGENT_RUNNING
  ├─ agent_done         → IDLE
  ├─ stop / kill        → IDLE
  └─ send_error         → IDLE

HANDOVER_PENDING
  ├─ handover_written   → HANDOVER_DONE
  ├─ handover_error     → IDLE
  └─ cancel_handover    → IDLE

HANDOVER_DONE
  └─ cancel_handover    → IDLE

SWITCHING
  ├─ switch_ready       → IDLE
  └─ new_greeting       → AGENT_RUNNING

Environment Variables

Variable Default Description
PORT 3001 HTTP/WS listen port
GATEWAY_HOST 10.0.0.10 OpenClaw gateway host
GATEWAY_PORT 18789 OpenClaw gateway port
GATEWAY_TOKEN (hardcoded) Auth token for gateway connection
NODE_TLS_REJECT_UNAUTHORIZED 1 (verify) Set to 0 for self-signed certs
SSL_KEY ../web-frontend/ssl/key.pem TLS key for HTTPS (optional)
SSL_CERT ../web-frontend/ssl/cert.pem TLS cert for HTTPS (optional)