For a Linux machine with systemd that already runs Hermes Agent, with Python 3.11 or newer and sudo. The
bridge goes on the same machine as Hermes; one script installs it and pairs your phone. The examples use
voice.example.com for the bridge’s public address; use your own.
The bridge talks to Hermes through Hermes’ own API server. How to install Hermes Agent itself is in the Hermes documentation.
Per the Hermes API server documentation,
add this to ~/.hermes/.env of the Hermes user, with a long random key of your own:
API_SERVER_ENABLED=true
API_SERVER_KEY=<a long random key>
Then start (or restart) the gateway:
hermes gateway
It reports API server listening on http://127.0.0.1:8642. Keep it on 127.0.0.1: the key gives full
access to Hermes and its tools, including terminal commands, and only the bridge needs it. Port, host and the
other options are in the Hermes documentation linked above.
You can also leave this to the installer: when the API server is off or has no key, it shows these two lines
and offers to add them with a newly generated key. Restart hermes gateway afterwards.
As the user that runs Hermes:
git clone https://github.com/Vantix-Wealth-Labs/hermes-car-voice.git
sudo python3 hermes-car-voice/backend/install.py
The installer needs nothing but Python. It:
API_SERVER_ENABLED, API_SERVER_KEY, API_SERVER_HOST and API_SERVER_PORT
from ~/.hermes/.env of the user who ran sudo (else /root/.hermes/.env), and asks before it changes
anything there. If your Hermes keeps these settings elsewhere, add --hermes-env PATH, or
--hermes-url http://127.0.0.1:8642 and type the key when asked.carvoice, the code in /opt/hermes-car-voice/backend, the pinned
Python packages (requirements.lock) in /opt/hermes-car-voice/.venv, the settings in
/etc/hermes-car-voice/bridge.env (readable only by root) and the systemd service
hermes-car-voice-bridge, listening on 127.0.0.1:8787.small); its first start downloads the model, a few
hundred MB. On a small CPU an OpenAI-compatible transcription service is much faster: add
--stt openai --stt-base https://api.groq.com/openai/v1 --stt-model whisper-large-v3-turbo (or your
service’s URL and model) and type its key when asked. Your recorded questions then go to that service.The installer never shows the Hermes key or a transcription key; they only go into bridge.env. To see
everything it would do (paths, the systemd unit, the names of the settings) without changing anything, run it
with --dry-run. Every bridge setting is described in Install the bridge by hand.
The app only connects over HTTPS (plain http only to a bridge on the phone itself), and the address has to work from the phone, also on mobile data. Pick one:
Tailscale (recommended). When Tailscale is running and connected on the machine, the installer offers to run
tailscale serve --bg --https=443 http://127.0.0.1:8787
Tailscale Serve then publishes the bridge with HTTPS inside your tailnet, at the machine’s name
(https://<machine>.<tailnet>.ts.net), and --bg keeps it running after a reboot. Two things are needed:
tailscale serve says
so and offers a link to switch them on; the installer shows its message unchanged. Tailscale points out that
machine names then appear in a public certificate log.Nothing becomes reachable from the internet; that would be Tailscale Funnel, which the bridge does not need.
voice.example.com whose service is http://127.0.0.1:8787. Cloudflare provides the certificate. The
bridge sends a keepalive every 10 seconds while Hermes works, so long answers stay within Cloudflare’s idle
limit.A reverse proxy with TLS, for example Caddy, which gets a certificate by itself:
voice.example.com {
reverse_proxy 127.0.0.1:8787
}
Answers stream as server-sent events. The bridge sends X-Accel-Buffering: no, which nginx honours; with
another proxy, make sure responses are not buffered and the read timeout is above 60 seconds.
Check from the phone’s browser, on mobile data: https://voice.example.com/healthz shows {"status":"ok"}.
With Tailscale the installer pairs the phone right away (step 4). For an address you set up after the installer
ran, pair the phone with:
sudo python3 hermes-car-voice/backend/install.py --pair --origin https://voice.example.com
Once it has the address, the installer pairs the phone: it creates a random access token, saves it for the bridge (the running bridge picks it up by itself) and shows a QR code plus the address and token as text. Treat the token like a password.
Then install Car Voice for Hermes from Google Play and open it:
The app stores the token encrypted and never shows it again; Settings shows only its last four characters.
To pair again, or another phone:
sudo python3 hermes-car-voice/backend/install.py --pair # this phone again, or the address changed
sudo python3 hermes-car-voice/backend/install.py --pair --name tablet # another phone
A name that is already paired needs --replace (or a yes when asked); its old token then stops working.
cd hermes-car-voice && git pull
sudo python3 backend/install.py
Running the installer again is the update: it installs the new code (the previous code stays in
/opt/hermes-car-voice/backups/), updates the Python packages and restarts the bridge. Your bridge.env, the
systemd unit and the paired phones stay as they are.
A bridge installed by hand with this guide’s earlier instructions (a clone in /opt/hermes-car-voice) uses the
same paths: cd /opt/hermes-car-voice && sudo git pull && sudo python3 backend/install.py updates it in place.
Hosts without a git checkout can update from a bundle with backend/deploy/upgrade_bridge.sh, which also checks
the result; see the
operator runbook.
Something not working? See Troubleshooting. Prefer to do every step yourself? See Install the bridge by hand.