Installation & Setup
Prerequisites
- Android device or emulator (API 26+ / Android 8.0+)
- A running Hermes Agent instance (v0.8.0+ recommended) with the API server enabled
- Python 3.11+ on the server (for the pairing plugin)
Quick Start
Three commands get you from zero to connected:
1. Install the Android app
Hermes-Relay ships in two flavors built from the same codebase:
- Google Play — easy install, automatic updates, the agent can read your screen and notifications. The accessibility-service surface is conservative by design so the build stays inside Play Store policy.
- Sideload — manual install from GitHub Releases, full feature set including hands-free voice control of your phone (the agent can tap, type, swipe, and navigate apps for you).
The two builds use different application IDs, so you can install both side-by-side and try them out. Most users want the Google Play version. Read the Release tracks page for the full feature comparison and a decision guide before you pick.
Once you've decided: install from the Play Store listing, or grab the file ending in -sideload-release.apk from the latest GitHub Release and follow the Sideload APK section below for step-by-step install and integrity-verification instructions.
2. Install the server plugin
On the machine running your Hermes agent:
curl -fsSL https://raw.githubusercontent.com/Codename-11/hermes-relay/main/install.sh | bashThe installer follows Hermes's canonical skill-distribution pattern:
- Clones the repo to
~/.hermes/hermes-relay/(override with$HERMES_RELAY_HOME) pip install -e ~/.hermes/hermes-relay/into the hermes-agent venv — editable, sogit pullis all that's needed to update the plugin- Adds
~/.hermes/hermes-relay/skillstoskills.external_dirsin~/.hermes/config.yaml(idempotent YAML edit) so thehermes-relay-pairskill is picked up on every hermes-agent load - Symlinks
~/.hermes/plugins/hermes-relay→ the clone'splugin/subdir - Installs a thin
~/.local/bin/hermes-pairshim that execspython -m plugin.pairinside the hermes-agent venv - Installs a systemd user unit at
~/.config/systemd/user/hermes-relay.service(optional — skipped on macOS, WSL-without-systemd, bare chroots)
Restart hermes-agent after install.
What you get
- Full Hermes-Relay Android app features — sessions browser, conversation history on app restart, personality picker, command palette, memory management. Just install the plugin and it works.
- 18
android_*device control tools (tap, type, read screen, screenshot, open apps, send SMS, call, search contacts, etc.) — registered by the plugin /hermes-relay-pairslash command — backed by thedevops/hermes-relay-pairskill and usable from any Hermes chat surfacehermes-pairshell shim — for scripts and power-user flows- Voice mode endpoints on the WSS relay (transcribe, synthesize, voice config) wired into the Android app's voice mode UI
No separate skill install, no qrencode binary needed.
Updating
Because the installer uses pip install -e for the plugin and external_dirs for the skill, updates are a single command:
cd ~/.hermes/hermes-relay && git pull && bash install.sh
systemctl --user restart hermes-gateway hermes-relaybash install.sh is idempotent — safe to re-run as often as you like. It re-applies every step against the existing install, picks up any new files, and rebuilds the systemd unit from the latest template.
Uninstalling
A clean uninstaller ships in the same repo:
bash ~/.hermes/hermes-relay/uninstall.shOr if you don't have the clone any more:
curl -fsSL https://raw.githubusercontent.com/Codename-11/hermes-relay/main/uninstall.sh | bashThe uninstaller reverses every install step in the opposite order, is idempotent, and never touches state shared with other Hermes tools (~/.hermes/.env, the gateway's state.db, the hermes-agent venv core). Useful flags:
bash uninstall.sh --dry-run # preview without changing anything
bash uninstall.sh --keep-clone # leave ~/.hermes/hermes-relay in place
bash uninstall.sh --remove-secret # also wipe the QR signing identityBy default the QR signing secret at ~/.hermes/hermes-relay-qr-secret is preserved, so re-installing keeps the same identity and any phones still holding their session tokens stay valid.
3. Pair your phone
You have two equivalent entry points — pick whichever fits where you already are:
From an active Hermes session (shortest path if you're already chatting with the agent): type /hermes-relay-pair in any chat surface — CLI, Discord, Telegram, anywhere Hermes is listening. The hermes-relay-pair skill generates the QR and renders it inline for you. No shell required.
From a shell (power-user / scriptable): on the server, run
hermes-pairThe dashed hermes-pair is a thin shim that execs python -m plugin.pair in the hermes-agent venv. Both routes share the same implementation and produce the same QR + plain-text output.
hermes pair (with a space) is not currently exposed
A top-level hermes pair sub-command would be nice, but hermes-agent v0.8.0's top-level argparser doesn't forward to third-party plugins' register_cli_command() dict yet. Use /hermes-relay-pair or the dashed hermes-pair shim in the meantime — both work today and will keep working once the upstream gap is closed.
This prints a QR code and the plain-text connection details (server URL, API key). Scan the QR from the app's onboarding screen — or type the values in manually if your terminal can't render QR blocks. The text fallback is always shown, so this works inside Hermes's Rich TUI panel and over SSH with limited charsets.
One scan configures chat and the relay. If you've already started the Hermes-Relay WSS server on the same host (see Relay Server below), hermes-pair automatically detects it at localhost:8767, mints a fresh 6-char pairing code, pre-registers the code with the relay via its loopback-only /pairing/register endpoint, and embeds the relay URL and code in the same QR. The phone scans once and is ready for chat, terminal, and bridge.
If the relay isn't running, hermes-pair prints an [info] line pointing at hermes relay start and renders an API-only QR — chat still works, and you can pair with the relay later once it's up. You can also force API-only mode explicitly:
hermes-pair --no-relayChoosing session lifetime + channel grants
By default the phone prompts you to pick a session TTL when you scan the QR (1 day / 7 days / 30 days / 90 days / 1 year / never expire). You can also pre-set the TTL and per-channel grants on the host side so the phone's picker dialog opens with your chosen values already selected:
# Pair for 7 days
hermes-pair --ttl 7d
# Pair indefinitely, limit terminal to 30 days and bridge to 1 day
hermes-pair --ttl never --grants terminal=30d,bridge=1d
# Short-lived dev session
hermes-pair --ttl 1dSupported duration formats: 1d, 7d, 30d, 90d, 1y, never (or any <number><unit> combo where unit is s/m/h/d/w/y). Grants can be pre-set for the terminal and bridge channels and are automatically clamped to the overall session TTL — a grant cannot outlive its session.
Camera unavailable? Use manual pairing
If you can't scan a QR — for example you're SSH'd into the host from the same phone you want to pair, the host has no display attached, or there's no second camera-equipped device handy — Hermes-Relay ships a manual fallback flow. Open the app's Settings → Connection → Manual pairing code (fallback) card to read its locally-generated 6-char code, then on the host run:
hermes-pair --register-code ABCD12 # default 30d session
hermes-pair --register-code ABCD12 --ttl 7d # composes with --ttl / --grantsThe command pre-registers your code with the local relay over loopback and prints a confirmation. Tap Connect in the same card and you're paired. Same 10-minute single-use expiry as QR codes; same TTL/grant rules — --ttl and --grants flags compose with --register-code exactly the same way they compose with the default QR flow.
The phone's TTL picker dialog always opens on scan, preselected with your chosen values, so you have one final chance to confirm or override before the session is created. The selection you make is persisted as the new default for future pairs.
Never expire
Never expire is always available in the picker regardless of transport. The phone treats your intent as the trust model rather than gating on secure-transport detection — if you explicitly pick it, the session stays active until you revoke it from Paired Devices.
Transport security + insecure-mode consent
The app renders a Transport Security badge next to each Connection row:
- 🔒 Secure (TLS) — paired over
wss:// - 🔓 Insecure (LAN only / Tailscale / Local dev) — paired over plain
ws://with the reason you picked on the consent dialog - 🔓 Insecure — plain
ws://with no reason recorded
The first time you toggle insecure mode on, a consent dialog opens with a plain-language threat-model explanation and a reason picker. The reason is displayed on the badge but is not enforced — it's informational, to make the choice visible to you later.
The app also runs a Trust On First Use (TOFU) cert pinning check on wss:// connections: on the first successful handshake it records the server's certificate fingerprint, and every subsequent connect verifies against it. If the cert changes (because the relay was rebuilt, the Let's Encrypt cert rolled over, or an MITM is happening), the connection fails loudly. Re-pairing via QR is taken as explicit consent to pin a new certificate.
Paired Devices management
Settings → Connection → Paired Devices lists every device currently paired with the relay — device name, transport badge, session expiry, per-channel grant chips, and a Revoke button per row. Revoking the current device wipes local state and redirects to the pair flow. Any paired device can revoke any other; for single-operator setups this is intentional (so you can manage everything from one phone), multi-user deployments will need a role model later.
Security
The QR contains credentials — your API key if one is set, and the relay pairing code if a relay block was embedded. The pairing QR is now also signed with HMAC-SHA256 using a host-local secret (auto-created at ~/.hermes/hermes-relay-qr-secret, mode 0o600). Don't screenshot or share it. The relay code is one-shot and expires in 10 minutes, but the API key is long-lived.
Hermes Server Setup
Enable the API server in your Hermes configuration (~/.hermes/.env):
API_SERVER_ENABLED=true
API_SERVER_KEY=your-secret-key-here
API_SERVER_HOST=0.0.0.0 # Allow network access (default is localhost only)
API_SERVER_PORT=8642API key is optional for local setups
If you're running Hermes on the same machine (or connecting via localhost), you can leave API_SERVER_KEY unset. The key is only needed when exposing the API server over the network. If you do set one, hermes-pair reads it automatically.
Sideload APK
If you'd rather not use Google Play, you can install the signed APK directly from GitHub Releases. This works on any Android 8.0+ device.
1. Download the APK
Head to github.com/Codename-11/hermes-relay/releases/latest and grab the file ending in -sideload-release.apk from the assets list — for example, hermes-relay-0.4.0-sideload-release.apk. Every release is version-tagged, so the exact prefix changes each version but the -sideload-release.apk suffix stays constant.
Why "sideload" and not "googlePlay"?
Each release ships both a -sideload-release.apk (full feature set — bridge channel, voice-to-bridge intents, vision-driven navigation) and a -googlePlay-release.apk (conservative Play Store build). Most sideloaders want the -sideload- flavor. The two builds install with different application IDs, so you can have both side-by-side.
Download the .apk, not the .aab
Each release also ships -release.aab files. That's the Android App Bundle format Google Play uses internally — it won't install directly on your device. Always pick a file ending in .apk for sideloading.
2. Allow installs from your browser (first time only)
Android blocks APKs from unknown sources by default. Before the install prompt appears, you'll need to grant permission to whichever app you used to download the file (usually Chrome, Firefox, or your Files app):
- Settings → Apps → Special app access → Install unknown apps
- Pick the browser or file manager you downloaded the APK with
- Toggle Allow from this source
The exact wording varies by OEM (Samsung calls it "Install unknown apps", Pixel calls it "Install unknown apps", older versions use "Security → Unknown sources"), but the idea is the same.
3. Install it
Open the downloaded APK from your Downloads notification or the Files app, then tap Install. The first launch will walk you through onboarding and pairing.
4. Verify integrity (optional but recommended)
Every release ships a SHA256SUMS.txt file alongside the APK. Compare the checksum of your download against it before installing:
macOS / Linux / Git Bash:
sha256sum hermes-relay-*-sideload-release.apk
# Compare the output against the matching line in SHA256SUMS.txtWindows PowerShell:
Get-FileHash -Algorithm SHA256 hermes-relay-*-sideload-release.apk
# Compare the Hash column against the matching line in SHA256SUMS.txtIf the hashes don't match, don't install — redownload and try again.
5. Verify the signing certificate (advanced)
The APK is signed with the Codename-11 release keystore. If you want to confirm the signature matches the one Google Play pins to the app, check the SHA256 fingerprint of the certificate:
- Subject:
CN=Bailey Dixon, Codename-11 - SHA256 fingerprint:
A9:A4:2D:94:20:8B:94:B3:68:5B:01:93:E3:94:9B:90:50:AD:80:60:56:E7:16:3C:FC:E5:11:AF:68:0D:79:4B
You can inspect it yourself with:
keytool -printcert -jarfile hermes-relay-*-sideload-release.apkManual Install (from source)
If you prefer to build the app yourself:
git clone https://github.com/Codename-11/hermes-relay.git
cd hermes-relay
scripts/dev.bat build # Build debug APK
scripts/dev.bat run # Build + install + launch (requires connected device)Manual Pairing
If you don't want to use QR pairing, you can enter connection details by hand — either during the app's onboarding flow or later from Settings.
During onboarding:
- The app opens with an onboarding flow
- On the Connect page, tap Enter manually
- Type your API Server URL (e.g.,
http://192.168.1.100:8642) and API Key - Tap Test Connection to verify
- Optionally enter a Relay URL for Terminal/Bridge features
- Tap Get Started
After onboarding: open Settings → Connection. The top card (Pair with your server) shows a Scan Pairing QR button and a status summary for the API server, relay, and session. To enter values by hand, expand the Manual configuration card below it — API Server URL, API Key, Relay URL, and Insecure Mode live there, along with Save & Test.
The hermes-pair command always prints these same values as plain text alongside the QR code, so you can copy them directly.
Relay Server (Optional)
The relay server is only needed for Terminal (remote shell) and Bridge (agent-driven phone control). Chat works without it.
Start the relay
# If you installed the hermes-relay plugin (recommended):
hermes relay start --no-ssl
# Or directly from a repo checkout:
python -m plugin.relay --no-sslRun this on the same machine as hermes-agent. If the relay is running when you execute hermes-pair (or /hermes-relay-pair), its URL and a freshly-registered pairing code are automatically embedded in the QR — you don't need to enter anything in the app.
For persistent deployment, Docker, systemd, and TLS options, see the Relay Server docs.
If you only saw an API-only QR earlier (because the relay wasn't running), just start the relay and re-run hermes-pair — the new QR will include the relay block.
Verify Connection
Once you're connected, the chat looks like this — streaming responses, tool cards, markdown rendering, and the personality picker all live:
After onboarding, the chat header shows the agent name with an animated green pulse on the avatar when the API server is reachable. If the dot is red (no pulse), check:
- Is the Hermes agent running? (
hermes gateway) - Is
API_SERVER_ENABLED=true? - Can your phone reach the server? (same network, firewall rules)
- Is the URL correct? (include port, e.g.,
:8642)