Hermes Agent — Claude-Max-Proxy + Hermes selbst, alles auf einer Maschine
Repliziert den Proxy-Trick, den wir fuer ARIA nutzen: claude-max-api-proxy
(npm) wrappt die Claude Code CLI — angemeldet mit deiner Claude-Max-
Subscription — als OpenAI-kompatiblen /v1/chat/completions-Endpoint.
Hermes Agent (Nous Research,
CLI/Desktop-Agent) redet darueber mit Claude, ohne API-Kosten — laeuft ueber
die Subscription, nicht ueber Anthropic-API-Billing.
Entscheidung (Stefan): Hermes Agent selbst laeuft auf DERSELBEN Maschine
wie dieser Proxy-Stack — alles was zu Hermes gehoert auf eine Box, minimale
Latenz, nur ein Rechner zum Pflegen. ARIA bleibt komplett unangetastet
(eigenes Docker-Netz hermes-net, eigenes Claude-CLI-Login-Volume, kein
gemeinsamer Container, laeuft ja ohnehin auf einer anderen Maschine als
Hermes).
Architektur
hermes-agent (Nous Research, network_mode: host)
| HTTP, Authorization: Bearer <TOKEN>, http://localhost:8447/v1
v
hermes-gateway (Port 8447, NUR auf 127.0.0.1 gebunden)
| prueft Bearer-Token, sonst 401
v
hermes-proxy (Port 3456, NUR docker-intern im hermes-net erreichbar)
| claude-max-api-proxy -> Claude Code CLI (Claude-Max-Login)
v
Anthropic (ueber deine Claude-Max-Subscription)
hermes-agent-dashboard (0.0.0.0:9119, Hermes' eigenes Web-UI/Setup, per
Basic-Auth gesichert seit 19.07.2026 — Stefans
Entscheidung, siehe Sicherheitshinweis)
Warum das Gateway trotzdem noch da ist, obwohl alles auf einer Maschine
laeuft: hermes-proxy selbst hat keine Authentifizierung (genau wie ARIAs
Proxy) — jeder andere Container im selben Docker-Host koennte sonst
unbemerkt Deine Claude-Max-Subscription mitbenutzen. Das Gateway
(proxy-patches/gateway.js, pure Node, keine Extra-Deps) prueft den
Authorization: Bearer <TOKEN>-Header gegen ein Shared Secret, bevor
irgendwas an hermes-proxy weitergeleitet wird. Seit "alles auf einer
Maschine" bindet das Gateway nur noch auf 127.0.0.1, nicht mehr nach
aussen — kein anderer Rechner kommt mehr direkt ran.
Was uebernommen wurde (1:1 aus ARIAs Proxy-Setup)
- Sed-Patches an der npm-CLI-Bridge (host-binding,
--dangerously-skip-permissions, echter--system-prompt-Kanal, 24h-Timeout, Prompt via stdin) — generische Infra-Fixes, nicht ARIA-spezifisch. - Adapter-Patches (
proxy-patches/openai-to-cli.js,cli-to-openai.js) — die OpenAI-tools<-><tool_call>-Text-Bruecke. Generisch, 1:1 uebernommen. - NICHT uebernommen: ARIAs
routes.js-Patch (Live-Tool-Stream/Not-Aus fuers Diagnostic-UI, ARIA-Bridge-spezifisch). Stattdessen gibt's hier das eigenegateway.jsfuer die Auth, die ARIA in der Form nicht braucht.
Setup
-
Hermes-Agent-Sourcecode daneben klonen (das offizielle Docker-Image liegt nicht auf Docker Hub, sondern wird per
build:aus dem Repo gebaut):cd /pfad/zum/deploy-ziel git clone https://github.com/NousResearch/hermes-agent.git hermes-agent-src(dieser Clone ist in
.gitignore— bleibt lokal, wandert nicht in unser Repo) -
.envanlegen:cp .env.example .env openssl rand -hex 32 # Ausgabe in .env bei HERMES_GATEWAY_TOKEN eintragen -
Model-Config fuer Hermes seeden (einmalig, VOR dem ersten Start — danach fasst Hermes die Datei nicht mehr an):
mkdir -p hermes-data/agent-home cp hermes-agent-config/config.yaml.example hermes-data/agent-home/config.yamlDie Datei zeigt Hermes per
provider: customauf unserenhermes-gateway(http://localhost:${HERMES_GATEWAY_PORT}/v1) —${HERMES_GATEWAY_PORT}/${HERMES_GATEWAY_TOKEN}werden von Hermes selbst beim Config-Laden aus den Container-Env-Vars expandiert, die kommen aus Deiner.env. Kein manuelles Ausfuellen noetig. -
Dashboard-Auth einrichten (Pflicht seit 19.07.2026 —
hermes-agent-dashboardlaeuft mit--host 0.0.0.0und Hermes verweigert diesen Bind hart, wenn kein Auth-Provider registriert ist:Refusing to bind dashboard to 0.0.0.0 — the auth gate engages on non-loopback binds, but no auth providers are registered. Ohne diesen Schritt kommt der Dashboard- Container beim naechsten Schritt gar nicht hoch):# HERMES_DASHBOARD_USER in .env ist Klartext, HERMES_DASHBOARD_PASSWORD_HASH # ist NIEMALS das Klartext-Passwort, sondern der Hash daraus. Erzeugen kannst # Du den erst NACHDEM Schritt 5 unten (docker compose up -d) mindestens # hermes-agent hochgefahren hat -- gegen "hermes-agent", NICHT # "hermes-agent-dashboard" (der braucht den Hash schon zum Start, Henne-Ei): docker exec -it hermes-agent python -c \ "from plugins.dashboard_auth.basic import hash_password; print(hash_password('DEIN-PASSWORT'))"ACHTUNG DOLLARZEICHEN-FALLE: der Hash hat das Format
pbkdf2:sha256:<n>$<salt>$<hash>— zwei rohe$-Zeichen drin. Docker Compose interpretiert$xyzin.env-Werten selbst als Variablen- Referenz und ersetzt nicht aufloesbare Teile stillschweigend durch Leerstring (sichtbar alsWARN The "xyz" variable is not setbeimdocker-compose up). Ohne Escaping kommt im Container ein verstuemmelter Hash an und das Dashboard bleibt bei "no auth providers are registered" haengen, obwohl alles "richtig" eingetragen aussieht. Jedes$im Hash durch$$ersetzen, bevor Du ihn in.enveintraegst (z.B.260000$abc$def→260000$$abc$$def). Anfuehrungszeichen um den Wert helfen dabei NICHT — Compose expandiert$in.env-Werten unabhaengig davon ob sie gequotet sind.Am einfachsten den Hash direkt per
sedin der Zeile escapen, statt ihn von Hand nochmal abzutippen (verdoppelt NUR die$in genau dieser einen Zeile):sed -i '/^HERMES_DASHBOARD_PASSWORD_HASH=/ s/\$/$$/g' .envZur Kontrolle, ob noch unverdoppelte
$uebrig sind (sollte leer sein):grep -o '\$[^$]' .envOutput (den escapeten Hash, nicht das Passwort) in
.envbeiHERMES_DASHBOARD_PASSWORD_HASHeintragen,HERMES_DASHBOARD_USERnach Belieben setzen (Defaultadmin). Derdashboard.basic_auth-Block dazu liegt schon fertig inhermes-agent-config/config.yaml.example(Platzhalter, die Hermes selbst aus den Container-Env-Vars expandiert — kein manuelles Ausfuellen noetig, siehe Schritt 3).WICHTIG, wenn Du Schritt 3 schon VOR dem 19.07.2026 gemacht hast: Schritt 3 kopiert
config.yaml.examplenur EINMALIG nachhermes-data/agent-home/config.yaml— kein automatischer Re-Sync danach. Derdashboard:-Block wurde erst nachtraeglich in die.example-Datei ergaenzt, landet also NICHT von selbst in einer bereits existierendenconfig.yaml. Ohne ihn bleibt Hermes bei "no auth providers are registered" haengen, egal wie sauber.envescaped ist (weil Hermes den Block ja gar nicht sieht). Pruefen:grep -A3 '^dashboard:' hermes-data/agent-home/config.yamlKommt nichts zurueck → Block manuell anhaengen:
cat >> hermes-data/agent-home/config.yaml <<'EOF' dashboard: basic_auth: username: "${HERMES_DASHBOARD_USER}" password_hash: "${HERMES_DASHBOARD_PASSWORD_HASH}" EOFDanach neu hochziehen:
docker compose up -d --build hermes-agent-dashboard -
Stack starten:
docker compose up -dBaut beim ersten Mal das Hermes-Image aus
hermes-agent-src(dauert ein paar Minuten), startet dannhermes-proxy,hermes-gateway,hermes-agent(Gateway/Messaging-Prozess) undhermes-agent-dashboard. FallsHERMES_DASHBOARD_PASSWORD_HASHnoch leer ist (Schritt 4 noch nicht gemacht):hermes-agent-dashboardbricht beim Start ab, die anderen drei Services laufen trotzdem — Hash nachtragen, danndocker compose up -d hermes-agent-dashboard. -
Einmalig Claude-CLI-Login (oeffnet Browser-OAuth mit deiner Claude-Max-Subscription):
docker exec -it hermes-proxy claudeDanach liegen die Credentials persistent in
hermes-data/claude-config/— ueberlebt Container-Restarts. -
Testen, dass der Proxy laeuft:
curl -s http://127.0.0.1:${HERMES_GATEWAY_PORT:-8447}/v1/chat/completions \ -H "Content-Type: application/json" \ -H "Authorization: Bearer <HERMES_GATEWAY_TOKEN aus .env>" \ -d '{"model":"sonnet","messages":[{"role":"user","content":"Sag Hallo"}]}'Ohne oder mit falschem Bearer-Token gibt's
401 unauthorized. -
Mit Hermes chatten:
docker exec -it hermes-agent hermes chatoder das Dashboard direkt im Browser oeffnen:
http://<diese-maschine>:9119— bindet auf0.0.0.0, kein SSH-Tunnel mehr noetig, dafuer fragt es jetzt den Basic-Auth-Login aus Schritt 4 ab. Traffic ist weiterhin unverschluesseltes HTTP (siehe Sicherheitshinweis unten) — fuer mehr als "kurz testen" gehoert ein TLS-Reverse-Proxy davor.
Mobiler Client (Android)?
Zwei GRUNDVERSCHIEDENE Richtungen, nicht verwechseln:
- Unser Proxy oben (
hermes-gateway->hermes-proxy): Claude ALS Backend FUER Hermes. Kein Client redet direkt damit ausser Hermes selbst. - Hermes' EIGENER OpenAI-kompatibler API-Server (
API_SERVER_HOST/API_SERVER_KEY, auskommentiert imhermes-agent-Service): Hermes ALS Server FUER einen Chat-Client. Aktivieren +API_SERVER_KEYin.envsetzen, dann kann ein Client wie gpt_mobile oder Maid (Open Source, Android, freierbase_url+Key, kein Messenger — siehe Chat-Verlauf) sich direkt gegen Hermes verbinden. Fuer sowas wie die ARIA-App (Wake-Word, Sprachsteuerung, TTS) braeuchte man dagegen einen eigenen App-Build — kein fertiges Produkt zum Runterladen.
Sicherheitshinweis
hermes-gateway bindet nur auf 127.0.0.1 — von aussen kommt niemand mehr
ran, solange Du nicht selbst was aendertst (z.B. fuer den mobilen API-Server
oben, oder falls Hermes doch mal auf eine zweite Maschine zieht).
hermes-agent-dashboard bindet seit 19.07.2026 bewusst auf 0.0.0.0 (Stefans
Entscheidung — ein SSH-Tunnel machte mit dem SSL-Setup Probleme) und ist per
Basic-Auth gesichert (siehe Setup-Schritt 4, Hermes verweigert den 0.0.0.0-Bind
sonst ohnehin hart). Das ist Testzustand, kein Dauerbetrieb-Haerten: das
Passwort geht als HTTP Basic-Auth unverschluesselt raus (kein TLS auf Port
9119), Keys/Config/Sessions liegen dahinter offen. Fuer Dauerbetrieb gehoert
ein TLS-Reverse-Proxy (Caddy/nginx) davor, der Basic-Auth + Zertifikat
uebernimmt — Dashboard-Port dann wieder 127.0.0.1, nur der Proxy von aussen
erreichbar.
Generell gilt: wenn Du HERMES_GATEWAY_PORT, API_SERVER_HOST oder den
Dashboard-Port doch mal direkt nach aussen exposest, ist der Traffic
unverschluesseltes HTTP, kein TLS. Nicht direkt ins offene Internet haengen —
SSH-Tunnel, WireGuard/Tailscale oder einen TLS-Reverse-Proxy davorschalten.
Verzeichnisse (nicht committet, siehe .gitignore)
hermes-agent-src/— geklonter Hermes-Agent-Sourcecode (Docker-Build-Context)hermes-data/claude-config/— Claude-CLI-Login (Browser-OAuth-Session)hermes-data/agent-home/— Hermes'~/.hermes-Aequivalent: Config (config.yaml, aushermes-agent-config/config.yaml.examplegeseedet), Sessions, Memory, Skills,.env.env— Secrets (Token, ggf. API_SERVER_KEY)
Wo deployen?
Diese Compose-Datei ist ortsunabhaengig. Stefan deployt sie selbst auf der
Ziel-Maschine (nicht aria-wohnung — komplett getrennt von ARIA). Repo klonen,
die drei Setup-Schritte oben (Hermes-Source klonen, .env, Config seeden),
dann docker compose up -d.