Files
hermes-mobile/docs/DIRECT_GATEWAY_ARCHITECTURE.md
2026-07-24 18:15:44 +00:00

11 KiB

Hermes Mobile Direct Gateway Architecture

Hermes Mobile is pivoting to a native Android client that talks directly to a remote upstream Hermes Agent gateway/API server. The apps/companion TypeScript server is legacy compatibility code and must not be required by Android or future releases.

Architecture

Android app
  Kotlin/Compose UI
  connection/auth storage
  transport DTOs
  domain repositories
        |
        | HTTPS, bearer token when configured
        v
Upstream Hermes Agent gateway/API server
  auth/session authority
  chat/completion authority
  streaming/tool/artifact authority
  provider credentials and Hermes runtime

The Android app remains a thin client. It may store server profiles, non-provider UI preferences, selected session IDs, and Android-secured bearer tokens. It must not embed provider credentials, clone Hermes backend behavior, or make the legacy companion server part of the release path.

Documented Upstream Surface

The current repository/context documents these upstream Hermes API-server endpoints:

Endpoint Status Mobile contract
GET /health Documented Primary reachability and compatibility probe. Response shape beyond a successful JSON response is treated as server-specific.
GET /v1/models Documented Authenticated compatibility probe for bearer-token connections and future model picker source.
GET /v1/capabilities Documented Capability negotiation source. Current upstream behavior advertises endpoints as {method, path} objects and feature support as booleans.
POST /v1/chat/completions Documented Baseline OpenAI-compatible chat path. Mobile must assume this may not expose Hermes session IDs or structured tool events.
POST /v1/responses Documented Preferred future request/streaming path if upstream exposes richer response events. Exact Hermes event mapping is not yet verified.
/api/sessions/* Documented and capability-advertised List/create/get/update/delete/messages/fork/chat/chat-stream routes exist, but mobile must use only the exact advertised method/path object and must not infer search or other routes.
POST /api/sessions/{session_id}/chat/stream Documented and capability-advertised SSE session turn. Core mobile support begins with message.started, assistant.delta, authoritative assistant.completed, error, and done; tool events remain gated by tool_progress_events.

Any route not listed here must be labeled provisional until checked against official Hermes Agent docs or a running gateway. Session search remains explicitly unverified and unmapped.

Direct stream lease contract

  • Parse session_chat_streaming, tool_progress_events, and endpoints.session_chat_stream from GET /v1/capabilities.
  • Require the advertised session stream endpoint to be POST, use an absolute path containing {session_id}, and send the documented JSON input field.
  • Bind the request URL, route generation, session ID, endpoint path, and structured-event gate into one immutable lease before the HTTP call starts.
  • Execute that prebuilt request through OkHttp without consulting route selection again until the call ends.
  • Never apply an SSE event whose session_id differs from the bound lease. A later Local/Remote re-evaluation applies only to a later request.
  • Append assistant.delta text deterministically, then replace it with the authoritative assistant.completed.content response when present.
  • Report HTTP, I/O, or malformed-SSE failure only against the route/session lease that owned the failed call; an upstream error event fails the session without invalidating the route.
  • Decode and render tool lifecycle/progress only when tool_progress_events is advertised. Treat event payload fields defensively, retain large output outside bounded display windows, and persist only client presentation state such as expansion.
  • Do not expose artifact share/save actions until an upstream artifact handoff capability and URI/download contract are advertised; the current direct gateway contract has neither.

Unverified Assumptions

The following are useful design assumptions, not confirmed contracts:

  • Bearer authentication is accepted through an Authorization: Bearer <token> header on /v1/*.
  • /health returns a stable status field.
  • /v1/responses can provide enough streaming metadata to preserve Hermes sessions, artifacts, cancellation, and reconnect state.
  • Hermes session search has a stable direct HTTP endpoint.
  • Tool result and artifact listing/download have stable direct HTTP endpoints.
  • Cancellation has a stable direct HTTP endpoint.
  • Error payloads consistently include message or error.

Android contracts may model these needs, but implementation must keep them capability-gated until verified.

Compatibility Policy

  • Direct gateway routes are the default target for all new Android transport work.
  • Legacy companion routes such as /api/health, /api/auth/validate, /api/chat, /api/files/*, and /api/terminal/* are temporary compatibility only.
  • UI and domain terminology should say "gateway", "server", or "upstream Hermes" unless explicitly describing legacy companion behavior.
  • The transport/domain model exposes direct gateway capabilities first. legacyCompanionAdapter exists only to identify old adapters during migration.
  • Backend behavior belongs upstream in Hermes Agent. Hermes Mobile may add client adapters and mappers, not server-side replacements.

Local-first gateway routing

A server profile can contain two URLs for the same Hermes gateway identity:

  • Local URL — preferred when the phone can safely reach the gateway on the home/LAN (for example, https://hermes.home.arpa).
  • Remote URL — fallback when local reachability is unavailable (for example, a configured public HTTPS hostname).

The client must choose automatically per connection attempt:

  1. If a local URL is configured, probe its documented GET /health endpoint using a short, bounded timeout.
  2. Use the local URL when the probe succeeds and the gateway identity/auth contract is compatible.
  3. Otherwise immediately use the configured remote URL; never leave chat blocked waiting for LAN discovery.
  4. Re-evaluate on app foreground, explicit reconnect, connectivity changes, and after a failed active request. Do not switch a request mid-stream.
  5. Show the active route (Local or Remote) in connection diagnostics, but do not expose bearer tokens or internal host details in message content/logs.

Security and reliability rules:

  • Local and remote URLs are explicit user configuration, not guessed IP ranges or silently discovered devices in the MVP.
  • Both routes must use HTTPS by default and must be treated as the same intended gateway only after an authenticated compatibility check. The app must not silently send a bearer token to an arbitrary captive portal or unrelated LAN host.
  • Remote is a fallback, not a duplicate profile. User preferences, selected session state, and credentials stay associated with one logical gateway profile.
  • A later milestone may add opt-in mDNS/QR setup, certificate pinning/identity binding, and richer connectivity observation only after direct-gateway basics are verified.
  • The current upstream identity-binding limitation and the stronger contract needed are tracked in docs/BLOCKERS.md.

First-run setup and authentication

The Android app owns one versioned setup profile. First launch shows welcome/privacy guidance and requires an explicit Remote HTTPS URL; a Local HTTPS URL is optional and represents the same logical gateway for local-first routing. A valid existing profile bypasses mandatory setup, while legacy/current valid profiles are marked migrated once without requiring credential re-entry. Settings reopens the same wizard state for later edits.

Supported client authentication modes are:

  • None — probes and compatible requests carry no authorization credential.
  • Bearer — the token is encrypted at rest and sent only to the selected configured gateway through the existing authorization-header path.
  • Username/password — the client posts directly to the configured Remote gateway's /auth/password-login, accepts only allowlisted Hermes session-cookie names from Set-Cookie, encrypts the password and resulting cookie at rest, and sends only the cookie pair on subsequent authenticated checks.

Raw bearer tokens, passwords, and session cookies are excluded from Compose wizard state string output, connection forms, diagnostics, and logs. Changing the remote URL or username invalidates credentials bound to the previous gateway/account; editing may retain an existing encrypted credential only while its binding remains unchanged. Login redirects are disabled, cookie values are validated against header injection, and unsuccessful login clears the prior session cookie.

Setup completion is gated on the same local-first compatibility test used by connection Settings: Local is attempted first when configured, then Remote is used as fallback. This source contract has unit/build verification only; no real gateway credentials, attached Android target, clean install, or release flow has been verified.

Android Contract Milestones

  1. Auth and connection: validate local + remote URLs, store bearer tokens securely, probe GET /health, use GET /v1/models as the first authenticated check, and make local-first fallback observable/retryable.
  2. Sessions: keep repository interfaces for list/create/select/continue, but mark route mapping provisional until upstream session docs are verified.
  3. Streaming: support final response and token delta reduction now; gate structured tool/artifact/session events behind server capabilities. Keep each stream bound to its selected local or remote route.
  4. Companion removal: after direct gateway chat/session/streaming works end to end, remove legacy companion UI routes and then deprecate install/docs around apps/companion.

Verified Session Capability Boundary — 2026-07-24

Official upstream API server documentation defines authenticated GET /v1/capabilities discovery and advertises session endpoint entries including session_list, session_create, session_get, and session_delete. The Android B3 foundation therefore builds requests only from those returned endpoint strings; a missing entry leaves that operation unavailable rather than falling back to an assumed route.

Source checked: https://hermes-agent.nousresearch.com/docs/user-guide/features/api-server/ on 2026-07-24.

The same official endpoint table does not document session search or a search query schema. Hermes Mobile keeps search in the repository contract and fake implementation for UI/state verification, but does not map search to HTTP until upstream publishes that contract. The exact blocker is tracked in docs/BLOCKERS.md.