Hermes Agent selbst mit ins Compose: alles auf einer Maschine
Stefan-Entscheidung: Hermes Agent (Nous Research) laeuft auf derselben
Maschine wie der Claude-Max-Proxy, nicht mehr remote. Ergaenzt:
- hermes-agent + hermes-agent-dashboard Services (offizielle Definition,
gebaut aus separat geklontem hermes-agent-src/), network_mode: host
- hermes-agent-config/config.yaml.example: seedet Hermes' model:-Block
automatisch auf provider: custom -> unser hermes-gateway, per ${VAR}-
Expansion aus .env (kein manuelles Config-Editieren mehr)
- hermes-gateway bindet jetzt nur noch auf 127.0.0.1 statt 0.0.0.0, da
Hermes lokal mitlaeuft und kein Remote-Zugriff mehr noetig ist
- .gitignore fuer hermes-agent-src/, hermes-data/, .env (Secrets/Sourcecode
gehoeren nicht ins Repo)
- README auf Single-Machine-Architektur aktualisiert, inkl. Setup-Schritten
und Einordnung des optionalen API_SERVER (fuer spaeteren mobilen Client,
andere Richtung als unser Proxy)
This commit is contained in:
@@ -1,42 +1,45 @@
|
||||
# Hermes Agent — Claude-Max-Proxy Setup
|
||||
# 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](https://github.com/NousResearch/hermes-agent) (Nous Research,
|
||||
CLI/Desktop-Agent, unterstuetzt laut Doku "your own endpoint" als
|
||||
Model-Provider) kann darueber mit Claude reden, ohne API-Kosten — laeuft
|
||||
ueber die Subscription, nicht ueber Anthropic-API-Billing.
|
||||
CLI/Desktop-Agent) redet darueber mit Claude, ohne API-Kosten — laeuft ueber
|
||||
die Subscription, nicht ueber Anthropic-API-Billing.
|
||||
|
||||
**Wichtig:** Hermes Agent selbst (das CLI/Desktop-Tool von Nous Research)
|
||||
ist hier NICHT enthalten — Stefan deployt das selbst auf einer anderen
|
||||
Maschine. Dieser Stack liefert nur die Proxy-Seite, die Hermes von dort
|
||||
ansprechen kann. ARIA bleibt komplett unangetastet (eigenes Docker-Netz
|
||||
`hermes-net`, eigenes Claude-CLI-Login-Volume, kein gemeinsamer Container).
|
||||
**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 (andere Maschine)
|
||||
| HTTP(S), Authorization: Bearer <TOKEN>
|
||||
hermes-agent (Nous Research, network_mode: host)
|
||||
| HTTP, Authorization: Bearer <TOKEN>, http://localhost:8447/v1
|
||||
v
|
||||
hermes-gateway (Port ${HERMES_GATEWAY_PORT:-8447}, nach aussen exposed)
|
||||
hermes-gateway (Port 8447, NUR auf 127.0.0.1 gebunden)
|
||||
| prueft Bearer-Token, sonst 401
|
||||
v
|
||||
hermes-proxy (Port 3456, NUR docker-intern erreichbar)
|
||||
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 (127.0.0.1:9119, Hermes' eigenes Web-UI/Setup)
|
||||
```
|
||||
|
||||
Warum das Gateway davor: `hermes-proxy` selbst hat keine Authentifizierung
|
||||
(genau wie ARIAs Proxy) — das ist bei ARIA ok, weil der Proxy nie einen
|
||||
Netzwerk-Port bekommt. Hermes laeuft aber auf einer anderen Maschine, der
|
||||
Proxy muss also ueber Netzwerk erreichbar sein. Ohne Auth waere das ein frei
|
||||
nutzbarer Claude-Max-Zugang fuer jeden der den Port erreicht. Das Gateway
|
||||
(`proxy-patches/gateway.js`, pure Node, keine Extra-Deps) prueft dafuer den
|
||||
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.
|
||||
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)
|
||||
|
||||
@@ -51,66 +54,104 @@ irgendwas an `hermes-proxy` weitergeleitet wird.
|
||||
|
||||
## Setup
|
||||
|
||||
1. **`.env` anlegen:**
|
||||
1. **Hermes-Agent-Sourcecode daneben klonen** (das offizielle Docker-Image
|
||||
liegt nicht auf Docker Hub, sondern wird per `build:` aus dem Repo
|
||||
gebaut):
|
||||
```bash
|
||||
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:**
|
||||
```bash
|
||||
cd /shared/projects/hermes_agent
|
||||
cp .env.example .env
|
||||
openssl rand -hex 32 # Ausgabe in .env bei HERMES_GATEWAY_TOKEN eintragen
|
||||
```
|
||||
|
||||
2. **Stack starten:**
|
||||
3. **Model-Config fuer Hermes seeden** (einmalig, VOR dem ersten Start —
|
||||
danach fasst Hermes die Datei nicht mehr an):
|
||||
```bash
|
||||
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. **Stack starten:**
|
||||
```bash
|
||||
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`.
|
||||
|
||||
3. **Einmalig Claude-CLI-Login** (oeffnet Browser-OAuth mit deiner Claude-Max-Subscription):
|
||||
5. **Einmalig Claude-CLI-Login** (oeffnet Browser-OAuth mit deiner Claude-Max-Subscription):
|
||||
```bash
|
||||
docker exec -it hermes-proxy claude
|
||||
```
|
||||
Danach liegen die Credentials persistent in `hermes-data/claude-config/.credentials.json`
|
||||
Danach liegen die Credentials persistent in `hermes-data/claude-config/`
|
||||
— ueberlebt Container-Restarts.
|
||||
|
||||
4. **Von der Hermes-Maschine aus testen:**
|
||||
6. **Testen, dass der Proxy laeuft:**
|
||||
```bash
|
||||
curl -s http://<diese-maschine>:${HERMES_GATEWAY_PORT:-8447}/v1/chat/completions \
|
||||
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":"claude-sonnet-4","messages":[{"role":"user","content":"Sag Hallo"}]}'
|
||||
-d '{"model":"sonnet","messages":[{"role":"user","content":"Sag Hallo"}]}'
|
||||
```
|
||||
Ohne oder mit falschem Bearer-Token gibt's `401 unauthorized` — Gateway
|
||||
arbeitet.
|
||||
Ohne oder mit falschem Bearer-Token gibt's `401 unauthorized`.
|
||||
|
||||
5. **Hermes Agent konfigurieren** (auf der Ziel-Maschine, `~/.hermes/config.yaml`):
|
||||
```yaml
|
||||
model:
|
||||
provider: custom
|
||||
model: "sonnet"
|
||||
base_url: "http://<diese-maschine>:8447/v1"
|
||||
api_key: "<derselbe HERMES_GATEWAY_TOKEN>"
|
||||
7. **Mit Hermes chatten:**
|
||||
```bash
|
||||
docker exec -it hermes-agent hermes chat
|
||||
```
|
||||
`base_url` hat laut Hermes-Doku Vorrang vor `provider` — funktioniert wie
|
||||
bei selbst gehosteten/lokalen Endpoints (vLLM, Ollama etc.), die
|
||||
Hermes-Doku beschreibt genau diesen Ansatz. Modellnamen `sonnet` / `opus`
|
||||
/ `haiku` werden vom Adapter (`openai-to-cli.js`, `MODEL_MAP`) auf die
|
||||
`--model`-CLI-Flags gemappt.
|
||||
oder das Dashboard per SSH-Tunnel oeffnen:
|
||||
```bash
|
||||
ssh -L 9119:localhost:9119 <diese-maschine>
|
||||
```
|
||||
dann im Browser `http://localhost:9119`.
|
||||
|
||||
## 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
|
||||
|
||||
Das Gateway schuetzt vor Fremdnutzung deiner Subscription (Bearer-Token-
|
||||
Pflicht), **aber der Traffic selbst ist unverschluesseltes HTTP** — kein TLS
|
||||
zwischen Hermes und dem Gateway. Solange beide Maschinen im selben
|
||||
vertrauenswuerdigen LAN oder per VPN verbunden sind: unkritisch. Wenn die
|
||||
Hermes-Maschine ueber offenes Internet zugreifen soll: Port NICHT direkt
|
||||
exposen, sondern per SSH-Tunnel, WireGuard/Tailscale oder einen TLS-
|
||||
Reverse-Proxy davorschalten. Sag Bescheid, dann bauen wir das nach, sobald
|
||||
klar ist wie die beiden Maschinen tatsaechlich verbunden sind.
|
||||
`hermes-gateway` bindet jetzt nur noch 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). Wenn Du `HERMES_GATEWAY_PORT` oder `API_SERVER_HOST` doch mal nach
|
||||
aussen exposest: der Traffic ist 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 — lief bisher nirgends deployed,
|
||||
liegt nur unter `/shared/projects/hermes_agent/` (Code-Editor-Sync). Du
|
||||
kannst sie 1:1 dahin kopieren, wo der Proxy tatsaechlich laufen soll (z.B.
|
||||
aria-wohnung als eigener isolierter Stack neben ARIA, oder eine ganz andere
|
||||
Maschine mit Zugriff auf deine Claude-Max-Subscription). Wichtig ist nur:
|
||||
egal wo, `HERMES_GATEWAY_PORT` muss von der Hermes-Maschine aus erreichbar
|
||||
sein.
|
||||
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`.
|
||||
|
||||
Reference in New Issue
Block a user