Skip to content

Architecture

Select the diagram to inspect it at full size.
How Hermes-Relay connects: Vanilla Hermes runs chat, Manage and voice with no plugin; the optional Relay plugin adds terminal, bridge, relay voice and desktop tools to the app and CLI; Device Control needs the sideload build.

Connection Model

The app maintains independent connection paths — chat over the vanilla Hermes surfaces (preferring the dashboard gateway, falling back to API-server SSE), and persistent WSS for the optional relay channels.

For a compact shareable reference covering connection paths, transport boundaries, pairing/session lifecycle, and operator controls, see the Relay Architecture Spec.

PathProtocolServerPurpose
Chat (preferred)WSDashboard :9119Gateway chat via /api/ws (tui_gateway) — live thinking/reasoning
Chat (fallback)HTTP/SSEAPI Server :8642Streaming conversations via the Sessions / runs / completions APIs
TerminalWSSRelay Server :8767Remote shell via tmux (Phase 2)
BridgeWSSRelay Server :8767Device control via AccessibilityService + MediaProjection (Phase 3)
NotificationsWSSRelay Server :8767NotificationListenerService forwards posted notifications over a bounded channel

The bridge channel was consolidated onto the unified relay port :8767 in v0.3 — the legacy standalone android_relay.py service on port 8766 is retired.

Key Components

ComponentPurpose
HermesApiClientDirect HTTP/SSE client for Hermes API Server
ChatHandlerMessage state management and streaming event processing
ChatViewModelSession CRUD, message sending, personality selection
ConnectionViewModelDual connection model, API client lifecycle, settings
ConnectionManagerWebSocket connection for relay (bridge/terminal)
ChannelMultiplexerEnvelope routing for relay channels
AuthManagerAPI key and session token storage (encrypted)
ConnectivityObserverNetwork connectivity monitoring

Chat Message Flow

When the dashboard gateway is available, the turn rides the /api/ws WebSocket (GatewayChatClient) and the same lifecycle events arrive over JSON-RPC, with live reasoning. The flow below is the API-server SSE fallback, used when there's no dashboard auth yet or the server is older:

  1. User types a message in ChatScreen
  2. ChatViewModel creates a session (if needed) via POST /api/sessions
  3. Message sent via POST /api/sessions/{id}/chat/stream
  4. HermesApiClient receives SSE events on OkHttp thread pool
  5. Events dispatched to main thread via Handler
  6. ChatHandler updates StateFlows (messages, streaming, tools)
  7. Compose UI recomposes from StateFlow changes

SSE Event Pipeline

The Hermes API Server streams events using Server-Sent Events. Each event type maps to a specific UI update.

EventHandler Action
session.createdInitialize session context (session_id, run_id, title)
run.startedRecord run start, capture user_message object
message.startedCreate assistant message placeholder from message object (id, role)
assistant.deltaAppend text delta to streaming message
tool.progressAppend reasoning/thinking delta to message
tool.pendingCreate tool progress card (queued state)
tool.startedUpdate card with start time, preview, args
tool.completedMark card as done with result_preview
tool.failedMark card as failed with error
assistant.completedFinalize message (content, completed, partial, interrupted flags)
run.completedEnd streaming state (completed, partial, interrupted, api_calls)
errorDisplay error banner (message, error)
doneClose SSE connection (state: "final")

Relay Auth Flow

The relay connection (bridge/terminal) uses a pairing code for initial setup, then session tokens for persistence.

Pairing codes use the full A-Z / 0-9 alphabet (36 chars). The pair command (hermes pair, /hermes-relay-pair, or the compatibility hermes-pair shell shim) on the Hermes host mints the code and pre-registers it with the relay via a loopback-only /pairing/register endpoint before embedding it in the QR — so the phone never types a code by hand. Session tokens are stored in EncryptedSharedPreferences backed by Android Keystore.

Vanilla Hermes chat vs Relay

Chat uses vanilla upstream Hermes either way (gateway preferred, API-server SSE as fallback); the relay is a separate, optional surface for bridge/terminal/notifications.

AspectVanilla Hermes Chat (gateway / API fallback)Relay (Bridge/Terminal/Notifications)
ProtocolWS (/api/ws) preferred · HTTP/SSE fallbackWSS
ConnectionPersistent gateway socket · per-request on SSE fallbackPersistent
AuthDashboard ws-ticket (gateway) · API bearer token (SSE fallback)Pairing code + session token. Voice endpoints may also accept the API bearer token.
ServerHermes dashboard :9119 · Hermes API :8642Unified Relay :8767
Live reasoningYes on gateway · post-hoc only on SSE fallback