ARIA 8b9aa73f98 Doku: Dollarzeichen-Falle beim Passwort-Hash in .env dokumentieren
docker-compose interpretiert rohe $-Zeichen im pbkdf2-Hash (.env) selbst
als Variablen-Referenzen und ersetzt sie stillschweigend durch Leerstring
(sichtbar als "WARN variable is not set"). Ergebnis: verstümmelter Hash,
Dashboard bleibt trotz "richtig" eingetragener Werte bei "no auth
providers are registered" haengen. Fix: jedes $ im Hash durch $$ ersetzen.
2026-07-19 21:52:37 +00:00

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 eigene gateway.js fuer die Auth, die ARIA in der Form nicht braucht.

Setup

  1. 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)

  2. .env anlegen:

    cp .env.example .env
    openssl rand -hex 32   # Ausgabe in .env bei HERMES_GATEWAY_TOKEN eintragen
    
  3. 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.yaml
    

    Die Datei zeigt Hermes per provider: custom auf unseren hermes-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.

  4. Dashboard-Auth einrichten (Pflicht seit 19.07.2026 — hermes-agent-dashboard laeuft mit --host 0.0.0.0 und 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 $xyz in .env-Werten selbst als Variablen- Referenz und ersetzt nicht aufloesbare Teile stillschweigend durch Leerstring (sichtbar als WARN The "xyz" variable is not set beim docker-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 .env eintraegst (z.B. 260000$abc$def260000$$abc$$def).

    Output (den escapeten Hash, nicht das Passwort) in .env bei HERMES_DASHBOARD_PASSWORD_HASH eintragen, HERMES_DASHBOARD_USER nach Belieben setzen (Default admin). Der dashboard.basic_auth-Block dazu liegt schon fertig in hermes-agent-config/config.yaml.example (Platzhalter, die Hermes selbst aus den Container-Env-Vars expandiert — kein manuelles Ausfuellen noetig, siehe Schritt 3).

  5. Stack starten:

    docker compose up -d
    

    Baut beim ersten Mal das Hermes-Image aus hermes-agent-src (dauert ein paar Minuten), startet dann hermes-proxy, hermes-gateway, hermes-agent (Gateway/Messaging-Prozess) und hermes-agent-dashboard. Falls HERMES_DASHBOARD_PASSWORD_HASH noch leer ist (Schritt 4 noch nicht gemacht): hermes-agent-dashboard bricht beim Start ab, die anderen drei Services laufen trotzdem — Hash nachtragen, dann docker compose up -d hermes-agent-dashboard.

  6. Einmalig Claude-CLI-Login (oeffnet Browser-OAuth mit deiner Claude-Max-Subscription):

    docker exec -it hermes-proxy claude
    

    Danach liegen die Credentials persistent in hermes-data/claude-config/ — ueberlebt Container-Restarts.

  7. 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.

  8. Mit Hermes chatten:

    docker exec -it hermes-agent hermes chat
    

    oder das Dashboard direkt im Browser oeffnen: http://<diese-maschine>:9119 — bindet auf 0.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 im hermes-agent-Service): Hermes ALS Server FUER einen Chat-Client. Aktivieren + API_SERVER_KEY in .env setzen, dann kann ein Client wie gpt_mobile oder Maid (Open Source, Android, freier base_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, aus hermes-agent-config/config.yaml.example geseedet), 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.

S
Description
No description provided
Readme
130 KiB
Languages
JavaScript 54.7%
Python 45.3%