Native Android companion app for your Hermes AI agent.
Hermes Mobile is a native Android app for controlling your Hermes Agent from your phone. Chat with your agent, manage cron jobs, skills, models and more.
It connects to the Hermes dashboard (REST API and WebSocket gateway), so you need a running dashboard that your phone can reach.
Chat, automation, productivity, and agent configuration — from your phone.
- Chat: Talk to your agent. History is stored locally in Room, and notifications support inline replies.
- Configuration: Manage profiles, skills, plugins, toolsets, and model/provider selections.
- Operations: Stream and filter live logs, manage cron jobs, edit environment keys, test webhooks, and monitor processes.
- Gateway status: Check the WebSocket connection, MCP servers, messaging channels, and OAuth providers.
- Productivity: Manage tasks on Kanban boards, track agent milestones, and browse session history.
- Analytics and billing: See usage analytics and manage billing or subscriptions.
- Theming: Seven built-in presets (Default, Monochrome, Gruvbox, Catppuccin, AMOLED, Nord, Garnet), plus Material You dynamic colors on supported devices.
- Material 3 design: Pull-to-refresh, a scroll-aware top bar, and customizable bottom navigation.
- Install Hermes Mobile from F-Droid, or download the APK from the latest GitHub release.
- Start your Hermes dashboard on a host your phone can reach.
- Open the app, tap Sign in, and follow Authentication.
The app detects which auth mode your dashboard uses and shows only the fields you need.
Security: Use HTTPS for remote connections. Plain HTTP/WS is for trusted local networks only, because authentication does not encrypt the transport.
On the host machine:
hermes dashboard # loopback (127.0.0.1:9119) — no auth needed
hermes dashboard --host 0.0.0.0 # LAN — requires authFor LAN access, set credentials in ~/.hermes/config.yaml:
dashboard:
basic_auth:
username: admin # pick your own
password: hermes # pick your ownTap Sign in on the landing screen and enter the dashboard host and port. The app probes the dashboard and shows the fields it needs.
Token only (dashboard on the same machine, bound to loopback)
- Enter the Token. You can find it in
~/.hermes/dashboard-token.txtor in~/.hermes/.envasHERMES_DASHBOARD_SESSION_TOKEN. - The app can also extract the token from the dashboard page for you.
Basic auth (dashboard on the LAN behind a password)
- Enter your Username and Password (default
admin/hermes). - The app logs in, stores a session cookie, and requests a WebSocket ticket automatically.
Open Settings → Connection → HTTPS mTLS client certificate configuration, or tap Connections on the first-launch landing page before signing in. Install client certificates in Android settings first; the app stores aliases only, never private keys.
Each binding belongs to an HTTPS hostname and port across all paths and connection profiles. Use Add Configuration or Edit Configuration, then Select Certificate or Change Certificate to open Android KeyChain. A valid address, port and selected certificate are required to Save. Delete Configuration clears the saved binding without removing the system certificate. Cancelling the picker keeps the draft alias; leaving an edited draft asks whether to discard unsaved changes. Host/port edits preserve the draft certificate and invalidate pending choices. An editor dismissed or destroyed while the picker is open ignores its late callback.
On the login screen, a failed initial /api/status probe offers certificate setup only
when its HTTPS error cause chain contains an SSL exception with the exact
received alert TLSV1_ALERT_CERTIFICATE_REQUIRED, SSLV3_ALERT_BAD_CERTIFICATE,
SSLV3_ALERT_UNSUPPORTED_CERTIFICATE, SSLV3_ALERT_CERTIFICATE_REVOKED,
SSLV3_ALERT_CERTIFICATE_EXPIRED, SSLV3_ALERT_CERTIFICATE_UNKNOWN, or
TLSV1_ALERT_UNKNOWN_CA. Local server-trust/hostname failures and generic handshake or
network failures do not trigger it. Missing and rejected client certificates use the same
dialog and wording. The dialog fixes the host and port; you must
explicitly select an installed certificate. Save verifies that candidate with a fresh
TLS connection and one credential-free GET /api/status to that origin. Any HTTP response
headers suffice, including 401/403; redirects are not followed. Failed verification leaves
existing bindings unchanged and allows another selection. Cancel preserves the mTLS
connection error. A successful save replaces any binding for that host and port and clears
the previous connection error. Use the connection button to retry; later errors still
appear normally. The inline notice points to Connections for changes.
Peers and providers reporting generic failures or other error formats may not trigger this prompt; use the manual configuration above. Later login steps, background requests and other screens do not trigger it. Network requests never open the system picker or wait for selection. Missing, expired, revoked or incompatible keys result in no client identity, so servers requiring mTLS fail normally until a usable binding is saved. REST, WebSocket, images, attachments and audio/video share the TLS configuration. Default Android server trust and hostname verification remain enabled; no private CA or permissive trust policy is added.
Saving (including selecting the same alias again) or deleting a configuration retires live TLS sockets and session contexts for the affected addresses. Retry failed requests or restart media playback; content already displayed or buffered may remain visible. Remote image memory/disk cache keys and gateway file cache keys include persistent identity generations and a shared cache epoch covering redirect destinations, so a late old response cannot populate the new identity's cache. While any binding exists, remote cache keys also include a per-launch epoch, because a KeyChain change made while the app was stopped cannot be observed; cached remote content is reused only within one app launch. Old cache entries age out under the existing cache policies. To isolate client identities, HTTP/2 connection coalescing across origins is disabled for the shared clients, including unbound origins; HTTP/2 within one origin remains enabled. A binding change also changes cache keys for unrelated remote resources because their redirect destinations are unknown before fetching. Redirects select the destination origin's saved identity only.
- Enter your dashboard's HTTPS URL on the login screen.
- Tap Custom headers, then Add Cloudflare headers.
- Enter your service token's
CF-Access-Client-IdandCF-Access-Client-Secretvalues. - Tap Save. The app probes the dashboard again, then shows the Hermes login fields.
Your Cloudflare Access policy must accept the service token. These headers authenticate with Cloudflare; you still need to complete Hermes authentication.
Other headers. You can add any custom header name and value in the same editor. To edit or remove saved headers, open Custom headers from login, or from the saved connection's edit dialog in Settings. Values are masked and stored in encrypted preferences. Saving an empty list removes the headers for that URL.
Where headers are sent.
- Profiles with the same server URL share headers.
- They apply to probes, login, API and media requests, and WebSocket handshakes.
- Each URL's scheme, host, port, and path prefix limit where its headers go.
- Redirects outside that scope do not receive them, and WebSocket redirects are not followed.
- The app manages
Authorization,Cookie, and transport headers itself.
Have multiple gateways? Switch between them in Settings → Connection profiles. Each profile stores its own host, port, and token.
Contributions are welcome! Read CONTRIBUTING.md for the branch workflow, code style, and PR checklist.
- Translations: Help translate the app on Hosted Weblate.
- Conventions and architecture: AGENTS.md.
- Visual and interaction requirements: DESIGN.md.
- Theme implementation: THEMES.md.
- JDK 21+ (used for Kotlin compilation and the Gradle toolchain).
- An Android SDK matching the compile SDK in
app/build.gradle.kts, from Android Studio or the Nix development environment. SetANDROID_HOMEorsdk.dirinlocal.properties.
git clone https://github.com/Hy4ri/hermes-mobile.git
cd hermes-mobile
./gradlew assembleDebug
adb install app/build/outputs/apk/debug/app-debug.apkFor release builds, set the keystore environment variables (KEYSTORE_PATH, KEYSTORE_PASSWORD, KEY_ALIAS, KEY_PASSWORD). Or let the GitHub Actions release workflow build on a v* tag push.
The Nix development shell turns on physical keyboard input in an existing hermes_dev AVD. It respects ANDROID_AVD_HOME and ANDROID_USER_HOME, defaulting to ~/.android/avd. The shell does not create the AVD, so create hermes_dev first.
Close any running emulator, then cold boot once to apply the setting:
nix develop --command emulator -avd hermes_dev -no-snapshot-loadLater launches can omit -no-snapshot-load.
app/src/main/java/com/m57/hermescontrol/
├── data/ # Local (Room, AuthManager), Remote (Retrofit, OkHttp), WS (WebSocket), Models
├── notification/ # Foreground service + inline reply for chat notifications
├── theme/ # Preset-based design system (7 themes), status colors, spacing, typography
├── ui/ # Compose feature screens + common components (HermesScaffold, StateViews)
├── util/ # CronExpressionFormatter, LocaleContextWrapper
└── Navigation*.kt # Navigation3 wiring, keys, screen registry, controller
- Language: Kotlin with KSP
- UI: Jetpack Compose, Material 3 / Material You
- Navigation: Navigation3
- Networking: Retrofit, OkHttp, Kotlinx Serialization
- Database: Room with SQLCipher encryption
- Security:
EncryptedSharedPreferences(AES256-GCM), DataStore - Images: Coil
- Testing: JUnit, MockK, Turbine, Espresso, Compose UI testing
- Formatting:
ktlint1.8.0 (checked in CI)
Dependency versions live in gradle/libs.versions.toml. Build configuration and dependency scopes live in app/build.gradle.kts.
If Hermes Mobile is useful to you, consider supporting its development on Ko-fi.
Copyright © 2026 M57 (Hy4ri).
Licensed under the Apache License, Version 2.0. See the LICENSE file for details.






