Root Cause fuer "Claude kennt Hermes-Tools nicht" gefunden: unser
openai-to-cli.js baut systemPrompt korrekt, manager.js liest options.systemPrompt
korrekt fuer --system-prompt -- aber der stock npm-Code in server/routes.js
kopiert beim subprocess.start(cliInput.prompt, {model, sessionId}) nur model+
sessionId, nie systemPrompt. Also war options.systemPrompt immer undefined,
--system-prompt kam nie mit echtem Inhalt an, Claude lief die ganze Zeit mit
seiner eigenen Default-Identitaet statt der injizierten Hermes-Tools.
Neuer sed patcht jetzt auch routes.js und reicht systemPrompt durch (trifft
beide Call-Sites: Streaming + Non-Streaming). Verifiziert lokal gegen das
echte npm-Paket claude-max-api-proxy@1.0.0.
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 (EIN Container, s6 supervisiert Gateway + Dashboard als
Geschwister-Prozesse via HERMES_DASHBOARD=1):
- Gateway-Prozess: Messaging-Bruecke, redet mit Claude wie oben
- Dashboard-Prozess: 127.0.0.1:9119, Hermes' eigenes Web-UI, nur intern
erreichbar
^
| reverse_proxy (Dashboard-Port)
v
caddy (0.0.0.0:443, TLS + Basic-Auth — einzige Auth-/TLS-Schicht,
siehe Sicherheitshinweis)
Wichtig, Kurskorrektur 20.07.2026: Gateway und Dashboard liefen anfangs
in ZWEI getrennten Containern (hermes-agent + hermes-agent-dashboard,
jeweils dasselbe Image mit anderem command:). Das verursachte zwei reale
Bugs: Dashboard zeigte den Gateway-Status immer als "Stopped" (die Status-
Pruefung ist eine LOKALE Prozess-Inspektion im eigenen Container, nicht per
Netzwerk-Call — im getrennten Container sah sie den Gateway-Prozess nie),
und der "Restart Gateway"-Button im Dashboard scheiterte mit no such gateway 'default' (der Button loest s6-Services im EIGENEN Container auf,
im Dashboard-Container gab's aber keinen). Der Startup-Log hatte den
Hinweis die ganze Zeit schon dabei: "dashboard supervised alongside if
HERMES_DASHBOARD is set. This is the recommended setup for the s6 container
image." Jetzt EIN hermes-agent-Container mit HERMES_DASHBOARD=1 +
HERMES_DASHBOARD_HOST=127.0.0.1 — kein separater Dashboard-Service mehr.
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. -
TLS-Reverse-Proxy (Caddy) einrichten — macht Port 443 von aussen erreichbar, Dashboard bleibt intern auf
127.0.0.1:Hermes' eigenes Dashboard-Auth-Gate (
dashboard.basic_authinconfig.yaml+HERMES_DASHBOARD_PASSWORD_HASH) ist bewusst NICHT mehr Teil des Setups (aufgeraeumt am 19.07.2026, spaeter Abend/20.07.2026) — das Gate engagiert sich laut eigener Fehlermeldung ohnehin nur bei einem0.0.0.0-Bind, und das Dashboard bindet fest auf127.0.0.1(HERMES_DASHBOARD_HOSTimhermes-agent-Service, siehedocker-compose.yml). Die komplette Absicherung nach aussen laeuft ausschliesslich ueber Caddy unten — ein Auth-Layer statt zwei, weniger Setup-Schritte, weniger Verwirrung.a. Selbstsigniertes Zertifikat, 100 Jahre gueltig (bewusst kein Let's Encrypt — kein Domain-/Port-80-Zwang, kein Renewal je wieder noetig). Erzeugt sich automatisch — kein manueller Schritt mehr noetig: der
caddy-Container prueft beim Start, ob inhermes-data/caddy-certs/schon ein Zertifikat liegt, und wenn nicht, installiert er sich kurzopenssl(perapk) und erzeugt es selbst (siehecommand:-Block imcaddy-Service indocker-compose.yml). Einmalig generiert, danach persistent im Volume — ueberlebtdocker compose up/Neustarts, wird nicht bei jedem Start neu gebaut.Falls Du's doch manuell/mit eigenem
/CN(z.B. Server-IP) erzeugen willst, geht das weiterhin von Hand — dann einfach VOR dem ersten Hochziehen selbst nachhermes-data/caddy-certs/dashboard.{crt,key}legen, der Auto-Generate-Check ueberspringt die Erzeugung dann:mkdir -p hermes-data/caddy-certs openssl req -x509 -nodes -newkey rsa:4096 \ -keyout hermes-data/caddy-certs/dashboard.key \ -out hermes-data/caddy-certs/dashboard.crt \ -days 36500 \ -subj "/CN=hermes-dashboard"b. Caddy-Basic-Auth-Hash erzeugen (bcrypt, das einzige Auth-Secret das es jetzt noch braucht):
docker run --rm caddy:2-alpine caddy hash-password --plaintext 'DEIN-PASSWORT'Output in
.envbeiCADDY_BASIC_AUTH_HASHeintragen, danach — Dollarzeichen-Falle (bcrypt-Hashes haben rohe$-Zeichen drin, Docker Compose interpretiert$xyzin.env-Werten sonst als eigene Variablen-Referenz und ersetzt still durch Leerstring) — escapen:sed -i '/^CADDY_BASIC_AUTH_HASH=/ s/\$/$$/g' .envHERMES_DASHBOARD_USERaus Schritt 2 (.env) wird als Login-Name wiederverwendet. -
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(EIN Container, s6 supervisiert darin Gateway UND Dashboard zusammen, siehe Architektur oben) undcaddy. FallsCADDY_BASIC_AUTH_HASHnoch leer ist:caddybricht beim Start ab, die anderen laufen trotzdem — Wert nachtragen (Schritt 4b), danndocker compose up -d caddy. -
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 im Browser oeffnen:
https://<diese-maschine>(Port 443, kein:9119mehr — der ist jetzt nur noch intern). Browser meldet beim ersten Aufruf "Zertifikat nicht vertrauenswuerdig" (weil selbstsigniert, nicht von einer oeffentlichen CA) — einmalig als Ausnahme akzeptieren ("Trotzdem fortfahren" o.ae.), danach fragt Caddy den Basic-Auth-Login aus Schritt 4b ab. Traffic ist ab hier durchgehend TLS-verschluesselt, kein SSH-Tunnel mehr noetig.
Toolsets aktivieren (Spotify & Co.)
Neu, sonst fragt Hermes nie nach Zugangsdaten und antwortet stattdessen mit
"ich hab keinen Zugriff auf Spotify/dein Handy/etc." — kein Bug, nur
unkonfiguriert: Hermes' Toolsets (Spotify, Kalender, Browser, …) sind bei
einer frischen Installation ALLE deaktiviert, komplett Opt-in pro Plugin.
Solange nichts eingeschaltet ist, schickt Hermes gar kein tools-Feld an
unseren Proxy — Claude sieht ehrlich keine Tools und antwortet entsprechend
ehrlich "kann ich nicht", obwohl der Proxy selbst (openai-to-cli.js)
technisch laengst faehig ist, <tool_call>-Tools zu injizieren (genau
derselbe Mechanismus wie bei ARIA).
Toolset einschalten (interaktives TUI im Container):
docker exec -it hermes-agent hermes tools
Zum gewuenschten Plugin runterscrollen (z.B. 🎵 Spotify), mit Leertaste an,
mit s speichern.
Login/OAuth fuers Toolset:
docker exec -it hermes-agent hermes auth spotify
Bei Spotify z.B. keine geteilte Drittanbieter-App — Hermes fuehrt Dich durch
das Anlegen einer eigenen minimalen Spotify-Dev-App (nur Client-ID, kein
Secret, PKCE-Flow) auf developer.spotify.com/dashboard inkl. der
einzutragenden Redirect-URI (http://127.0.0.1:43827/spotify/callback).
Der Login-Callback-Server laeuft dabei IM Container/auf der Remote-Maschine — fuer den Abschluss im lokalen Browser einmalig ein SSH-Port-Forward aufmachen:
ssh -N -L 43827:127.0.0.1:43827 <user>@<hermes-maschine>
Kein SSH-Port-Forward moeglich? Hermes validiert redirect_uri hart auf
127.0.0.1/localhost (kein oeffentlicher Callback moeglich), aber der
lokale Listener laesst sich umgehen: scripts/spotify_manual_auth.py baut
dieselbe PKCE-Autorisierungs-URL, wartet aber nicht auf den Callback,
sondern fragt Dich nach dem code-Parameter, den Du von Hand aus der
(fehlschlagenden) Redirect-URL in der Adressleiste kopierst. Das Ergebnis
landet 1:1 im selben Format in ~/.hermes/auth.json wie beim normalen
Login.
docker cp scripts/spotify_manual_auth.py hermes-agent:/tmp/spotify_manual_auth.py
docker exec -it hermes-agent python3 /tmp/spotify_manual_auth.py
Details/Ablauf stehen im Docstring des Skripts. Du kannst dabei dieselbe
Spotify-Dev-App wie fuer ARIA wiederverwenden — dafuer im
Spotify-Dashboard bei der
bestehenden App unter Settings → Redirect URIs zusaetzlich
http://127.0.0.1:43827/spotify/callback eintragen (kein neues
Client-Secret noetig, PKCE braucht nur die Client-ID).
Hinweis: Free-Spotify-Account reicht fuer Suche/Playlists/Library, fuer echte Playback-Steuerung (play/pause/skip auf einem Geraet) braucht's Spotify Premium.
Gilt analog fuer jedes andere Toolset (Kalender, Browser, etc.) — immer
zuerst hermes tools (einschalten), dann hermes auth <name> falls das
Plugin OAuth/Login braucht.
Toolset aktiviert, Claude sagt trotzdem "kenn ich nicht" / nennt eigene Skills/MCP-Server
Root Cause (20.07.2026, im echten Code + per Live-Test verifiziert, nicht
geraten): hermes-proxy laeuft mit einer echten Claude-Code-CLI-Session
(npm-Paket claude-max-api-proxy wrappt @anthropic-ai/claude-code
tatsaechlich als Subprocess). Diese Session hat ein eigenes Volume
(./hermes-data/claude-config), kann aber auf demselben Anthropic-Account
eingeloggt sein wie ARIA. Ein Account hat aber account-weite claude.ai-
Connectors (z.B. Gmail/Calendar/Drive) und Claude Codes eigenes natives
Skill-System — die werden in JEDER CLI-Session auf diesem Account automatisch
mitgeladen, auch hier. Wenn man Claude dann explizit nach einem "Skill" fragt
(z.B. "nutze Skill spotify_albums"), checkt Claude zuerst sein EIGENES,
natives Skill-/MCP-System (findet dort nichts zu Spotify, dafuer Gmail/
Calendar/Drive) und ignoriert dabei die per Prompt injizierten
<tool_call>-Tools (Hermes' eigentliche Plugins wie Spotify) komplett — es
sind zwei parallele, konkurrierende Tool-Konzepte in derselben Session.
Fix: docker-compose.yml startet die CLI seit 367ff64+ (Sed-Patch auf
manager.js) zusaetzlich mit --safe-mode — das deaktiviert fuer diese
Session ALLE Customizations (Skills, Plugins, MCP-Server, CLAUDE.md, ...),
eingebaute Tools (Bash/Read/...) bleiben zwar technisch an, werden aber nicht
gebraucht. Danach sieht Claude in der Hermes-Proxy-Session ausschliesslich die
per System-Prompt injizierten externen Tools und sollte sie auch fuer
"Skill"-artige Anfragen (Spotify etc.) nutzen.
git pull
docker-compose up -d --build hermes-proxy
Test: nach dem Neustart erneut nach einem aktivierten Toolset fragen (z.B.
"spiel meine Playlist Fliegen auf dem Handy ab") — Claude sollte jetzt einen
<tool_call>-Block ausgeben statt "kenn ich nicht" bzw. eigene Connectors zu
nennen. Falls es weiter hakt: docker logs hermes-proxy --tail 100 -f
waehrend der Reproduktion pruefen ob --safe-mode tatsaechlich im
gestarteten Prozess ankam (docker exec -it hermes-proxy ps aux | grep claude).
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).
Das Dashboard (s6-supervisierter Geschwister-Prozess im hermes-agent-
Container, HERMES_DASHBOARD_HOST=127.0.0.1) bindet fest auf 127.0.0.1 —
von aussen nicht mehr direkt erreichbar. Davor steht caddy (Service in
docker-compose.yml, Config in Caddyfile): terminiert TLS auf Port 443 mit
einem selbstsignierten 100-Jahre-Zertifikat (Setup-Schritt 4a) und erzwingt
eine Basic-Auth-Schicht (Setup-Schritt 4b, bcrypt-Hash in
CADDY_BASIC_AUTH_HASH). Caddy ist die EINZIGE Auth-/TLS-Schicht — Hermes'
eigenes Dashboard-Auth-Gate (dashboard.basic_auth/
HERMES_DASHBOARD_PASSWORD_HASH) wurde bewusst komplett rausgenommen
(aufgeraeumt 20.07.2026): es engagiert sich laut eigener Fehlermeldung
ohnehin nur bei einem 0.0.0.0-Bind, das Dashboard bindet aber fest auf
127.0.0.1 — ein zweiter, nie greifender Auth-Layer haette nur Setup-Schritte
und Verwirrung gekostet, ohne echten Sicherheitsgewinn.
Das war vorher (kurzzeitig, testweise) 0.0.0.0 OHNE TLS — Passwort ging als
Basic-Auth im Klartext raus. Ist jetzt behoben: einziger von aussen offene
Port ist 443, durchgehend verschluesselt.
Das Zertifikat ist selbstsigniert (keine oeffentliche CA dahinter) — Browser zeigen beim ersten Aufruf eine Warnung, die Du einmalig als Ausnahme akzeptierst (Details siehe Setup-Schritt 8). Wer eine "echte", warnungsfreie CA-Kette will, braucht dafuer eine oeffentliche Domain + Let's Encrypt — war hier bewusst nicht der Plan.
Generell gilt: wenn Du HERMES_GATEWAY_PORT, API_SERVER_HOST oder den
Dashboard-Port (9119) doch mal direkt nach aussen exposest (statt ueber
Caddy), ist der Traffic unverschluesseltes HTTP, kein TLS. Nicht direkt ins
offene Internet haengen — SSH-Tunnel, WireGuard/Tailscale oder eben den
Caddy-Reverse-Proxy davorschalten.
Troubleshooting
{"detail":"Invalid Host header. Dashboard requests must use the hostname the server was bound to."} beim Aufruf ueber Caddy: Hermes' Dashboard-
Backend prueft den eingehenden Host-Header gegen den Host, an den es
gebunden ist (127.0.0.1:9119) — Schutz gegen DNS-Rebinding. Caddy reicht
per Default den ORIGINALEN Host-Header vom Browser durch (Deine
Server-IP/Domain), den lehnt Hermes dann ab. Fix ist im Caddyfile bereits
drin: der reverse_proxy-Block ueberschreibt den Host-Header explizit
(header_up Host 127.0.0.1:9119), bevor die Anfrage ans Dashboard geht.
Falls der Fehler trotzdem auftritt: pruefen ob Dein Caddyfile den
header_up-Block im reverse_proxy 127.0.0.1:9119 { ... } wirklich enthaelt
(git pull + docker-compose up -d --build caddy, kein Neubuild noetig,
Caddy liest die Config beim Start neu ein).
"Restart Gateway"-Button im Dashboard zeigt
✗ no such gateway 'default': register it with 'hermes profile create default' first, or pass an existing profile name via '-p <name>', und/oder das
Dashboard zeigt "Gateway Status: Stopped" obwohl der Gateway laeuft:
Root Cause war eine Architektur-Inkompatibilitaet: Gateway und Dashboard
liefen anfangs in ZWEI GETRENNTEN Containern (hermes-agent mit
command: ["gateway", "run"] + ein separater hermes-agent-dashboard mit
command: ["dashboard", ...], beide aus demselben Image). Zwei Symptome,
eine Ursache:
- Der Gateway-Status im Dashboard kommt aus einer lokalen Prozess- Inspektion (PID-Check) im eigenen Container, NICHT aus einem Netzwerk- Call — im getrennten Dashboard-Container sah dieser Check den Gateway-Prozess nie, daher immer "Stopped".
- Der Restart-Button ruft
hermes gateway restartauf, das s6-Services im eigenen Container aufloest. Im Dashboard-Container gab's dafuer keinen registrierten Service — daherno such gateway 'default'. Der echte Gateway-Prozess lief davon unbeeindruckt weiter.
Fix (seit 20.07.2026, docker-compose.yml): Gateway und Dashboard laufen
jetzt als s6-supervisierte Geschwister-Prozesse in EINEM hermes-agent-
Container (HERMES_DASHBOARD=1 + HERMES_DASHBOARD_HOST=127.0.0.1 im
environment:-Block) — genau der vom Image selbst empfohlene Weg, siehe
Startup-Log: "dashboard supervised alongside if HERMES_DASHBOARD is set.
This is the recommended setup for the s6 container image." Kein separater
hermes-agent-dashboard-Service mehr, kein hermes profile create default
noetig. Nach git pull + docker-compose up -d --build sollten Gateway-
Status UND Restart-Button im Dashboard den echten Zustand zeigen — bitte
nach dem Redeploy einmal ausprobieren.
Sollte der Restart-Button trotzdem noch spinnen: als Fallback von aussen neu starten (bei s6-Auto-Restart selten noetig):
docker-compose restart hermes-agent
Chat im Dashboard bricht dauerhaft mit "Chat connection interrupted (code
1006). Reconnecting..." + "events feed disconnected — tool calls may not
appear" ab, obwohl die Seite selbst (Login, Session-History) normal laedt
und docker logs hermes-agent waehrend des Reconnects RUHIG bleibt (kein
Guard-Token, kein Fehler, gar nichts):
Root Cause im echten Hermes-Sourcecode gefunden (hermes_cli/web_server.py,
Funktion _ws_host_origin_reason), nicht geraten: der Host-Header-Fix von
weiter oben loest nur die HTTP-Seite. WebSocket-Upgrades
(/api/ws, /api/pub, /api/events — genau die Endpunkte hinter Chat +
Events-Feed) haben einen ZWEITEN, unabhaengigen Guard, der zusaetzlich den
Origin-Header prueft. Der Browser schickt als Origin immer die echte
aufgerufene Adresse (Server-IP/Domain), Hermes ist aber auf 127.0.0.1
gebunden und lehnt jeden Origin ab, der nicht 127.0.0.1/localhost/::1
ist -> origin_mismatch -> WS wird sofort mit Code 4403 geschlossen
(Browser zeigt das oft generisch als 1006).
Der Grund warum die Log-Suche vorher ins Leere lief: anders als
/api/console und /api/pty loggen /api/ws, /api/pub und /api/events
diese Ablehnung nicht — sie schliessen still. Gleicher Bug-Mechanismus
wie der Host-Header-Fehler, nur eine Ebene tiefer und ohne jede
Fehlermeldung.
Fix (bereits im Caddyfile): Origin-Header komplett entfernen statt ihn
umzuschreiben — fehlt der Header, ueberspringt Hermes den Origin-Check
komplett:
reverse_proxy 127.0.0.1:9119 {
header_up Host 127.0.0.1:9119
header_up -Origin
}
Nach git pull: docker-compose up -d --build caddy (Config-Reload reicht,
kein Neubuild von hermes-agent noetig).
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,.envhermes-data/caddy-certs/— selbstsigniertes 100-Jahre-Zertifikat (dashboard.crt/dashboard.key, Setup-Schritt 4a) — Private Key, niemals committenhermes-data/caddy-data/,hermes-data/caddy-config/— Caddys eigener Runtime-State (nicht von Dir angefasst).env— Secrets (Token, ggf. API_SERVER_KEY, Basic-Auth-Hashes)
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.