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, andendpoints.session_chat_streamfromGET /v1/capabilities. - Require the advertised session stream endpoint to be
POST, use an absolute path containing{session_id}, and send the documented JSONinputfield. - 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_iddiffers from the bound lease. A later Local/Remote re-evaluation applies only to a later request. - Append
assistant.deltatext deterministically, then replace it with the authoritativeassistant.completed.contentresponse when present. - Report HTTP, I/O, or malformed-SSE failure only against the route/session lease that owned the failed call; an upstream
errorevent fails the session without invalidating the route. - Decode and render tool lifecycle/progress only when
tool_progress_eventsis 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/*. /healthreturns a stablestatusfield./v1/responsescan 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
messageorerror.
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.
legacyCompanionAdapterexists 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:
- If a local URL is configured, probe its documented
GET /healthendpoint using a short, bounded timeout. - Use the local URL when the probe succeeds and the gateway identity/auth contract is compatible.
- Otherwise immediately use the configured remote URL; never leave chat blocked waiting for LAN discovery.
- Re-evaluate on app foreground, explicit reconnect, connectivity changes, and after a failed active request. Do not switch a request mid-stream.
- Show the active route (
LocalorRemote) 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 fromSet-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
- Auth and connection: validate local + remote URLs, store bearer tokens securely, probe
GET /health, useGET /v1/modelsas the first authenticated check, and make local-first fallback observable/retryable. - Sessions: keep repository interfaces for list/create/select/continue, but mark route mapping provisional until upstream session docs are verified.
- 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.
- 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.