Pairing Experimental
Pairing exchanges a one-time 6-character code for a long-lived session token, stored at ~/.hermes/remote-sessions.json (mode 0600). This is the same file the Android client uses — pair once from either, both work. The token survives reboots, picks up automatic reconnects with TOFU cert pinning on wss://, and is revocable from any other paired client (see hermes-relay devices).
The Hermes host needs the Relay plugin
The desktop CLI runs on Windows, macOS, and Linux, but the Hermes machine you pair with must have the Relay plugin installed, enabled, and running. On the Hermes host, complete this once before minting a pairing code:
bash
hermes plugins install Codename-11/hermes-relay/plugin --enable
hermes relay doctor
hermes relay start --no-ssl--no-ssl is for a trusted LAN or VPN path. For access outside that network, use the recommended Tailscale or TLS setup.
Recommended — paste the complete invite
In the Web Dashboard, open Relay → Pair new device, choose Hermes-Relay CLI+UI under Pairing client, then click Copy invite. This includes the existing direct Relay compatibility routes; it does not publish a listener. Verify a direct or Secure Link route is reachable from the computer. Default Android invites use authenticated Dashboard Relay ingress, which CLI+UI does not yet support.
On Windows, open CLI+UI → Hosts → Pair host → Paste invite, paste the full snippet using Paste (or Ctrl+V), then click Import and pair. The management UI invokes the installed CLI to validate and pair, preserving alternative routes and certificate trust. It keeps the invite only in the form while it is open. URL + code remains available for direct Relay pairing.
Alternatively, on the host run hermes pair --legacy-direct-relay and copy the printed hermes-relay://pair?... invite URL. An official Hermes Desktop invite works only when it includes a direct Relay or pinned Secure Link route.
Host terminal output lists advertised internal/LAN, Tailscale, and configured public addresses with their HTTP(S) or WebSocket protocol and effective port. Use the Relay WebSocket address for CLI+UI, rather than the Dashboard or optional API address. A loopback address such as 127.0.0.1 is host-only. The /relay pair chat command also shows an address receipt.
Direct Relay routes must be deliberately reachable. A public Dashboard address does not publish Relay port 8767. Pinned Secure Link invites require the full invite flow; use Paste invite in the management UI or --pair-qr in the CLI. The management UI's URL + code form does not import pins.
On the computer you are pairing:
bash
hermes-relay pair --pair-qr "hermes-relay://pair?payload=…" --grant-toolsDespite the flag name, --pair-qr accepts the pasted invite URL or the raw QR payload; the desktop client does not need a camera. The full invite is preferred because it carries the operator-reviewed certificate pin and ordered endpoint candidates. The client probes secure routes first, stores the selected route, and retains the remaining candidates for reconnect.
The invite and six-character code are single-use. Generate a fresh invite when one expires or has already been consumed.
Manual fallback — Relay URL + code
Use this only when the full invite cannot be copied.
1. Mint a code on the server
SSH into your Hermes host (or use any terminal already on it):
bash
hermes pair --ttl 600 --legacy-direct-relayOutput:
Direct Relay / LAN
Relay WebSocket: ws://192.168.1.50:8767 (WS, port 8767)The code is valid for 10 minutes (the default) and single-use. After first successful pair it's consumed. Adjust TTL (how long the minted session token stays valid) with --ttl 86400 (1 day), --ttl 2592000 (30 days), etc. — 0 means never expire (not recommended outside LAN).
If you don't have shell access to the host, run this from a Hermes chat session (any client, including Android): /hermes-relay-pair.
2. Pair on the client
On your laptop/workstation:
bash
hermes-relay pair --remote ws://<host>:8767Replace <host> with:
- A LAN IP (
192.168.1.100) - A Tailscale tailnet hostname (
hermes.tail1234.ts.net) — usewss://if tailscale serve is on - A public URL (
wss://hermes.example.com) — Cloudflare Tunnel, Caddy, nginx, etc.
The CLI prompts:
Relay: ws://<host>:8767
Need a pairing code — run `hermes pair` (or `/hermes-relay-pair`) on the relay host.
(Paste works; cleaned code shown before submit.)
Pairing code (6 chars): _Type or paste F3W7EY. On success:
→ using code: F3W7EY
Pairing with ws://<host>:8767…
✓ Paired. Token stored in ~/.hermes/remote-sessions.json
server: 1.2.0
relay: ws://<host>:8767
route: lan
tip: add --grant-tools to also enable desktop tools (needed for `daemon`).Subsequent hermes-relay commands reuse the stored token. When that token nears its expiry, the CLI warns you (on a TTY) before the next command would fail and prints the exact re-pair command — so an expired session never just silently breaks.
Port default
A bare ws://<host> with no port defaults to :8767 (the relay's default), and the CLI tells you it did. A wss://<host> is left untouched — it's usually a reverse-proxy / Tailscale Serve front on :443 — so include the port explicitly if your secure relay listens elsewhere.
First use
Pairing is complete when the CLI reports that its token was stored. Pick the first result you want:
bash
# Open the paired Hermes TUI now
hermes-relay
# Confirm the selected host and connection state
hermes-relay status
# Windows: open the management UI from the same installation
hermes-relay uiTo keep approved desktop tools available in the background, include --grant-tools when pairing and then start the daemon:
bash
hermes-relay daemon startThe grant is host-scoped and remains subject to the access policy you select locally. Start with the TUI if you only want to confirm that pairing works.
Paste safety — what if the code comes out garbled?
Some terminals (Windows Terminal, WezTerm, older iTerm2) wrap pasted content in bracketed paste escape markers (\x1b[200~...\x1b[201~). The CLI disables bracketed paste before the prompt and defensively strips ANSI + control chars, but a few terminals ignore the disable flag. The → using code: F3W7EY confirmation line is your sanity check — if the echoed code doesn't match what you pasted, type it manually instead.
You can also skip the prompt entirely by passing the code positionally:
bash
hermes-relay pair F3W7EY --remote ws://<host>:8767Multi-endpoint pairing (ADR 24)
If your Hermes server is reachable from multiple routes — optional Hermes Secure Link, Tailscale, a public TLS URL, and LAN — the host can mint a single QR payload containing all of them. Generated defaults put Secure Link first when it is enabled, then other secure candidates, with plain LAN retained as the last fallback. The CLI probes endpoints in strict priority order, printing each result and latency, picks the first reachable route, and records what it used so reconnect banners remain clear. Network changes trigger another probe, allowing a move between trusted LAN and remote routes without re-pairing.
Hermes Secure Link is one QR-pinned TLS origin for Relay, API, and authenticated Dashboard namespaces. Their credentials remain separate, and Secure Link does not make the host reachable; LAN, VPN/Tailscale, or public routing must already reach port 9443. A certificate, hostname, or port change requires re-pairing.
Hermes Reach is an experimental outbound-only broker route. It lets the host and CLI connect outward when the host cannot accept an inbound connection. The Reach broker forwards opaque records; QR-pinned Secure Link TLS remains end-to-end inside that route. The broker sees routing/source metadata, timing, and byte counts, not inner Hermes credentials or plaintext. It is disabled by default and attempted only after Tailscale and supported direct TLS routes. Broker failure never enables plain transport.
Probing 3 endpoint(s)…
✓ [1/3] lan ws://192.168.1.50:8767 145ms
· [2/3] tailscale ws://hermes.tail1234.ts.net:8767 — timeout
→ picked lan endpoint ws://192.168.1.50:8767On the server:
bash
# Advertised routes plus the explicit direct Relay compatibility listener
hermes pair --mode auto --legacy-direct-relay
# Trusted LAN fallback
hermes pair --mode lan --legacy-direct-relayFor public direct Relay access, configure a deliberate TLS reverse-proxy route first. A Dashboard origin alone is insufficient. Secure Link invites carry their own paired certificate trust and can use the CLI invite flow.
Copy the printed invite URL and paste it to the CLI:
bash
hermes-relay pair --pair-qr 'hermes-relay://pair?payload=…'Or via env:
bash
HERMES_RELAY_PAIR_QR='<payload>' hermes-relay shellThe CLI races candidates within the same priority tier (Promise.any with 4s per-candidate timeout, 60s reachability cache) and picks the winner. Priority is strict — reachability only breaks ties within a tier, never promotes a lower-priority candidate.
HMAC signature. The QR payload carries an optional HMAC-SHA256 signature (
sigfield). The current CLI parses it but doesn't verify — the server's HMAC secret isn't client-accessible yet. Matches the Android app's current behavior. Verification lands with v1.0.
Re-pair (reset)
If your stored token expires, was revoked, or you want a fresh start:
bash
# purge stored session for this URL
rm ~/.hermes/remote-sessions.json # or delete just this URL's entry
# mint a fresh code on the server, then:
hermes-relay pair --remote ws://<host>:8767Inspect stored sessions
bash
hermes-relay statusShows per-URL: server version, pair age, token prefix, TTL expiry, grants (per-channel access), endpoint role, cert pin (wss only), tool consent state. Pass --json for a machine-readable redacted dump, or --json --reveal-tokens to include full tokens (for scripted re-auth — never paste into a shared terminal).
Paired devices on the server (revoke remotely)
See what the server thinks is paired — and revoke / extend:
bash
hermes-relay devices # list all paired devices on this server
hermes-relay devices revoke abc12345 # revoke by token prefix
hermes-relay devices extend abc12345 --ttl 604800 # extend to 7 daysTalks to the relay's GET /sessions HTTP endpoint using your stored bearer. The current device is marked with ●.
Related
- Installation — get the binary on your machine first.
- Subcommands — full reference for
pair,status,devices,shell,chat,tools. - Troubleshooting —
auth timed out,relay rejected,disconnected before auth, etc.