From f4bbdad311334cf90c3c732d31c9dc386f111edc Mon Sep 17 00:00:00 2001 From: Hitonabi Date: Thu, 24 Sep 2026 17:37:09 +0200 Subject: [PATCH] doku: Betriebsdoku neu (ARCHITEKTUR, BETRIEB, UPDATES, RADAR, WIEDERAUFBAU) - ARCHITEKTUR.md (neu): Prozesse und Units, Rollen box/homelab und Partner-Instanz aus Phase 2a (kern/einstellungen.py, kern/zeit.py, kern/partner.py, /api/partner), Modell-Rollen, Bild-Weiche, wer welche Datei schreibt (inkl. mc2-quittiert.json), Meldeweg mit Telegram-Zweitweg, alle 62 /api-Routen, Herkunftspruefung, kurzer Ausblick Homelab-Teil. - BETRIEB.md (neu, loest RUNBOOK.md und BACKUP.md ab): Handgriffe, Meldungen und Betreffzeilen, Waechter-Regeln (Partner nach 5 Takten, Spracherkennung nur wenn wach, Ausblenden-Knopf), Dienste schlafen/wecken, zweistufiger Deploy mit Prueftor, Sicherung und Zurueckspielen, Notfall, Pfade auf der Box. - UPDATES.md (neu): Sonntags-Kette per Timer, Postchecks, Festhalten und Freigeben, Handbetrieb, Update-Verlauf, Fallen. - RADAR.md (neu): Modell-Radar (Leitplanken, Nachtablauf, Pruefstand, Uebernehmen/Verwerfen, Merkliste) und Stack-Radar. - WIEDERAUFBAU.md (neu, loest DISASTER_RECOVERY.md ab): entschlackte Schrittfolge, Anhang A (llama-swap-Unit und Drop-ins) behalten. Stand der Aussagen: Code in main 75611be. Co-Authored-By: Claude Opus 5.5 --- docs/ARCHITEKTUR.md | 177 +++++++++++++++++++++ docs/BACKUP.md | 62 -------- docs/BETRIEB.md | 234 +++++++++++++++++++++++++++ docs/DISASTER_RECOVERY.md | 325 -------------------------------------- docs/RADAR.md | 104 ++++++++++++ docs/RUNBOOK.md | 85 ---------- docs/UPDATES.md | 109 +++++++++++++ docs/WIEDERAUFBAU.md | 108 +++++++++++++ 8 files changed, 732 insertions(+), 472 deletions(-) create mode 100644 docs/ARCHITEKTUR.md delete mode 100644 docs/BACKUP.md create mode 100644 docs/BETRIEB.md delete mode 100644 docs/DISASTER_RECOVERY.md create mode 100644 docs/RADAR.md delete mode 100644 docs/RUNBOOK.md create mode 100644 docs/UPDATES.md create mode 100644 docs/WIEDERAUFBAU.md diff --git a/docs/ARCHITEKTUR.md b/docs/ARCHITEKTUR.md new file mode 100644 index 0000000..80bc5ab --- /dev/null +++ b/docs/ARCHITEKTUR.md @@ -0,0 +1,177 @@ +# Architektur des Homelab Orchestrators (Teil Box-Wart) + +_Stand 24.09.2026, Code in `main` = `75611be`. Für Agenten und Techniker. Versionen, Ports und Modelle mit +Messwerten: [wissen/STACK.md](wissen/STACK.md)._ + +Der Homelab Orchestrator (vormals Mission Control 2, kurz „MC2") hat zwei Bereiche: „Box-Wart" für die KI-Box und +„Homelab" für den Proxmox-PC. Zwei Instanzen, eine Oberfläche: Dieselbe Codebasis läuft auf der KI-Box (Rolle `box`) +und später als Container auf dem Proxmox-PC (Rolle `homelab`). Die Homelab-Instanz ist noch nicht eingerichtet +(Phase 3, braucht User-OK); dieses Dokument beschreibt deshalb vor allem die Rolle `box`. + +Der Box-Wart hält die KI-Box aktuell, passt auf sie auf und sucht bessere Modelle. Er besteht aus vier eigenen +Python-Prozessen, einer Bash-Schicht für Updates, Sicherung und Meldungen und aus Zustandsdateien unter +`/srv/models`. Er steuert zwei fremde Programme: llama-swap mit llama.cpp (die Modelle) und Hermes (der Agent „Lucy" +mit Telegram). + +## Überblick + +```mermaid +flowchart LR + subgraph Netz["Heimnetz"] + BR["Browser (PC, Handy)"] + OC["OpenChamber (PC)"] + LD["Lucy-Desktop (PC)"] + NQ["NerdQuiz (Arcane)"] + end + subgraph Box["KI-Box"] + MC2["MC2 :9001
Oberfläche, /api"] + GW["mc2-gateway :9010
/v1, Bild-Weiche"] + ST["mc2-steward
Wächter, Re-Warm"] + RD["mc2-radar
00:30"] + HE["Hermes :8642
Lucy, Telegram"] + LS["llama-swap :8080"] + end + BR --> MC2 + OC -->|/v1| MC2 + LD -->|/api, /v1| MC2 + MC2 -->|/v1 roh| GW + HE -->|/v1| GW + GW --> LS + NQ -->|/v1, Alias fast| LS + RD -->|Baseline| LS + ST -.->|prüft| MC2 +``` + +Alle Modell-Anfragen enden bei llama-swap, das je Modell einen `llama-server` startet. NerdQuiz geht als einziger +Client an MC2 vorbei direkt auf `:8080`. Nur den Radar-Kandidaten startet der Radar-Lauf nachts als eigenen +`llama-server` auf `:5899`, neben llama-swap. + +## Prozesse + +| Prozess (Unit) | Einstieg | Aufgabe | Eigener Zustand | +|---|---|---|---| +| `mission-control-2` (`:9001`) | `backend/app.py` | Oberfläche aus `frontend/dist`; 62 `/api`-Routen; reicht `/v1` roh an den Gateway weiter (`MC_V1_UPSTREAM`); reicht das Hermes-Dashboard unter `/hermes-ui/` durch (HTTP und WebSocket); Erinnerungen; Update- und Download-Jobs als Kindprozesse (`services/jobengine.py`) | Briefkasten, Erinnerungen, Routing-Policy, Hugging-Face-Zugang, ausgeblendete Hinweise | +| `mc2-gateway` (`127.0.0.1:9010`) | `backend/gateway_app.py` | `/v1` mit `model: auto` und Bild-Weiche, Kontext-Warnung, `/gw/health` | Token-Zähler (`~/.hermes/token_stats.json`) | +| `mc2-steward` | `backend/steward.py` | Re-Warm (alle 90 s, lädt das Hirn nach, wenn nichts geladen ist), Config-Watch (5 s), Wächter (jede Minute) | `mc2-waechter.json` (einziger Schreiber) | +| `mc2-radar` (Timer 00:30) | `backend/radar_lauf.py` | Suche und Nachttest neuer Modelle | `mc2-radar.json`, Baseline, `/srv/models/radar/` | +| Bash-Schicht | `deploy/*.sh` | Updates (`autoupdate.sh` mit `update-swap.sh`, `update-engine.sh`, Postchecks, `self-repair.sh`), Sicherung (`backup.sh`, `restore.sh`), Meldungen (`notify.sh`, `morgenmeldung.sh`), Vorwärmen (`warmup.sh`), `projekte-sync.sh`, Deploy (`deploy.sh`, `pruefen.sh`) | Pins, Meldeprotokoll, Nacht-Warteschlange, Deploy-Log | + +Alle vier Python-Prozesse teilen die flachen Pakete `backend/services/` und `backend/kern/` (Import über +`PYTHONPATH` und `WorkingDirectory` der Units). Router bleiben dünn, die Logik liegt in `backend/services/` +(siehe `AGENTS.md`). + +**Warum getrennte Prozesse** (Umbau v3, 15.07.): Der Gateway ist klein, wird kaum angefasst und startet sich +selbst neu (`Restart=always`, `TimeoutStopSec=5`). MC2 darf deshalb neu starten, ohne dass Anfragen von Lucy und +OpenChamber abreißen. Der Steward überlebt MC2-Neustarts und kann MC2 selbst überwachen. Rückweg ist jeweils eine +Zeile in `deploy/mission-control-2.service` (`MC_V1_UPSTREAM` bzw. `MC_REWARM_ENABLED=0`/`MC_SENTRY_ENABLED=0` +entfernen). + +Fremde Dienste, die der Box-Wart nur steuert oder überwacht: `llama-swap` (System-Unit, `--watch-config`), +`hermes-gateway`, `hermes-builtin-ui`, `lucy-stimme`, `voice-service` und `box-console` (beide schlafen seit 24.09.). + +## Rollen und Partner-Instanz (seit 24.09., Phase 2a) + +- **`backend/kern/einstellungen.py`:** `MC_ROLLE` (`box` oder `homelab`, Standard `box`), `MC_INSTANZ` (Anzeigename, + Standard „Box-Wart" bzw. „Homelab"), `MC_DATEN_DIR` (Zustandsdateien; Standard `/srv/models` auf der Box, + `/var/lib/mc2` im Homelab), `MC_PARTNER_URL` (Basis-URL der anderen Instanz, leer = keine) und `MC_PARTNER_NAME`. +- **`backend/kern/zeit.py`:** die eine Zeitzone `MC_LOCAL_TZ` (Standard `Europe/Berlin`) statt einer Kopie je Modul. +- **`backend/kern/partner.py`:** Lebenszeichen der anderen Instanz über deren `/api/health` (5 s Zeitlimit, 15 s + zwischengespeichert). +- **Rolle `box`** hängt alle Box-Router ein. **Rolle `homelab`** lädt nur `/api/health` und `/api/partner`; die + Homelab-Router folgen in Phase 3. `steward.py` betreibt Re-Warm und Config-Watch nur in der Rolle `box`; der + Wächter prüft im Homelab nur Platte und Partner. +- **`/api/health`** nennt `rolle` und `instanz`; `engine_reachable`, `gateway_reachable` und `brain` gibt es nur auf + der Box. +- **`GET /api/partner`** liefert das Lebenszeichen der anderen Instanz. **`/api/partner/`** reicht an + `/api/` weiter (alle Methoden, nach der Herkunftsprüfung). `partner/…` und `stream` werden nicht + weitergereicht: Zwei Instanzen sollen sich keine Anfrage endlos zureichen, und der Live-Strom würde offen bleiben. +- **Wächter:** Antwortet die andere Instanz nicht, entsteht der Hinweis „ antwortet nicht", rot erst nach + `MC_WAECHTER_PARTNER_TAKTE` = 5 Takten, damit ein Neustart kein Alarm ist. +- Die Units setzen weder `MC_ROLLE` noch `MC_PARTNER_URL`: Die Box läuft in der Rolle `box` und ohne Partner, bis die + zweite Instanz eingerichtet ist. + +## Modelle und Modell-Rollen + +- **Rollen-Aliase statt Namen:** Clients fragen `hermes`/`fast` (Hirn), `coder`/`heavy` (Coder), `vision` und + `coder-bild` (Bild-Zwillinge), `embed`, `reranker`. Die Eintragsnamen ändern sich beim Tausch, die Aliase nicht. +- **Gruppen:** `brains` (`swap: false`, `persistent: true`, `exclusive: false`) enthält nur das Hirn, es bleibt + geladen. `bild` (`swap: true`, `exclusive: false`) enthält die zwei Zwillinge, höchstens einer ist geladen. + Alles andere lädt bei Bedarf und geht nach `ttl` wieder raus. +- **Bild-Weiche** (`routers/gateway_proxy.py`, `services/router_logic.py`): Hat der aktuelle Schritt ein Bild, geht + die Anfrage an den Zwilling der Rolle (Coder-Ziele an `coder-bild`, alles andere an `vision`). Ältere Bilder + beschreibt `vision` einmal als Text (Cache je Bild), damit der Rest beim schnellen Modell mit Draft bleibt. +- **`model: auto`** ist die Chat-Spur: normal `fast`, bei langen oder schweren Anfragen `heavy` (Schwellen in der + Routing-Policy `mc2-routing.json`, ohne Datei gelten die Env-Defaults). +- **Warm halten:** `warmup.sh` (Root-Kopie in `/usr/local/bin/llama-swap-warmup.sh`) läuft nach jedem Start von + llama-swap. Danach übernimmt der Re-Warm im Steward; welches Modell warm bleibt, sagt `MC_WARMSET` im Drop-in + `mc2-steward.service.d/warmset.conf` (heute nur das Hirn). +- **Speicherregel:** Warm-Set plus größtes Bedarfsmodell ≤ ~115 GB, sonst droht ein Kernel-OOM. + +## Wer schreibt was + +| Datei | Schreiber | Regel | +|---|---|---| +| `/etc/llama-swap/config.yaml` | MC2 (Modelle, Rollen, Aufräumen, Hirn-Umstellung), Radar („Übernehmen"), `deploy.sh`, `restore.sh` | Die lebende Datei ist die Wahrheit. MC2 schreibt nur unter `llamaswap.config_sperre()` (Thread-Sperre plus `flock` auf `.mc2-config.lock`) und atomar. `deploy.sh` überschreibt nur, wenn sich `deploy/llama-swap.config.yaml` im selben Deploy geändert hat. Jede Änderung lädt llama-swap neu und entlädt alle Modelle. | +| `~/.hermes/config.yaml` | Hermes; MC2 nur bei der Hirn-Umstellung; `autoupdate.sh` und `restore.sh` beim Rückweg; `self-repair.sh` | MC2 ändert nur `model.default`/`model.model`, atomar, vorher `config.yaml.bak-hirn-`; bei einem Lesefehler schreibt es nichts. | +| `mc2-steward.service.d/warmset.conf` | `deploy.sh`; das Radar beim Übernehmen eines neuen Hirns | Das Radar tauscht den Namen in `MC_WARMSET` und startet den Steward neu. | +| `/srv/models/mc2-waechter.json` | nur der Steward | MC2 liest und führt Knöpfe aus; der nächste Takt sieht das Ergebnis. Der Ordner folgt `MC_DATEN_DIR`. | +| `/srv/models/mc2-quittiert.json` | nur MC2 (Knopf „Ausblenden bis zum nächsten Lauf") | Der Steward liest; ein ausgeblendeter Werkzeugfehler-Hinweis kommt wieder, wenn der nächste Lauf des Jobs erneut Fehler hat. | +| `/srv/models/mc2-radar.json` (+ `mc2-radar-baseline.json`) | Radar-Lauf und MC2 | jede Änderung unter `flock`, atomar | +| `/srv/models/mc2-pins.json` | `autoupdate.sh` hält fest; MC2 gibt frei | Format: Baustein → `pinned`, `version`, `grund`, `datum` | +| `/srv/models/mc2-announce.json` (Briefkasten, 200 Einträge) | nur MC2 | Steward und Gateway liefern per HTTP (`MC_ANNOUNCE_HTTP`), `notify.sh` per `POST /api/voice/announce` | +| `/srv/models/mc2-reminders.json` | nur MC2 | Erinnerungs-Schleife und `/api/reminders` im selben Prozess | +| `/srv/models/mc2-geheimnisse.json` | MC2 (Einstellungen) | Hugging-Face-Zugang, Rechte 0600 | +| `/srv/models/mc2-discover.json` | MC2, Radar | Cache der Hugging-Face-Entdeckung (12 h) | +| `/srv/models/mc2-deploy.log` | `deploy.sh` | eine Zeile je Deploy, auch gescheiterte | +| `~/mc2-notify.log` | `notify.sh` (anhängen), `morgenmeldung.sh` (über 3 MB auf 2 MB kürzen) | Quelle des Update-Verlaufs; der Wortlaut „QUEUED für Morgen-Digest" muss bleiben | +| `~/.hermes/night-queue.txt` | `notify.sh` (anhängen), `morgenmeldung.sh` (übernehmen, senden) | nicht gesendeter Stapel bleibt als `.senden` liegen | + +## Meldeweg + +`deploy/notify.sh` ist der eine Meldeweg: Die Meldung geht in Lucys Briefkasten (`POST /api/voice/announce`) und an +Telegram. Zwischen 00:00 und 06:59 sammelt `notify.sh` alles Nicht-Dringende in der Nacht-Warteschlange; +`morgenmeldung.sh` (Timer 07:00) schickt es als eine Nachricht. Dringend ist `-d`, ein Betreff mit „Alarm" oder +„Notfall" oder ein Text, der mit „KRITISCH" beginnt. Lucy-Desktop fragt den Briefkasten ab +(`/api/voice/announcements`) und spricht neue Einträge. + +Telegram hat zwei Wege: zuerst `hermes send --to telegram`; scheitert das oder fehlt Hermes (die Homelab-Instanz hat +keins), geht die Meldung direkt an die Telegram-Bot-API (seit 24.09., live bewiesen 15:41). Die Zugangsdaten liest +`notify.sh` aus der Datei in `MC_TELEGRAM_ENV` (Standard `~/.hermes/.env`: `TELEGRAM_BOT_TOKEN`, +`TELEGRAM_HOME_CHANNEL`, optional `TELEGRAM_HOME_CHANNEL_THREAD_ID`) und gibt das Token über stdin an `curl`, damit +es nicht in der Prozessliste steht. Erst wenn beide Wege scheitern, bleiben Protokoll und `wall` +(`MC_NOTIFY_OHNE_WALL=1` schaltet `wall` ab). Einzelheiten und Betreffzeilen: [BETRIEB.md](BETRIEB.md). + +## Schnittstellen + +In der Rolle `box` 62 Routen unter `/api`: 60 des Box-Warts (seit 24.09., vorher 95) und 2 für die +Partner-Instanz. In der Rolle `homelab` gibt es nur `/api/health` und die Partner-Routen. + +| Gruppe | Routen | +|---|---| +| Cockpit (`routers/boxwart.py`) | `GET /start`, `GET /hinweise`, `POST /hinweise/{id}/aktion/{aktion}`, `GET /modelle/nutzung`, `GET /updates/verlauf`, `POST /updates/festgehalten/{baustein}/freigeben`, `GET /modelle/aufraeumen`, `POST /modelle/aufraeumen/loeschen` | +| Radar (`routers/radar.py`) | `GET /radar`, `POST /radar/suche`, `POST /radar/{id}/uebernehmen`, `POST /radar/{id}/verwerfen` | +| Modelle (`routers/models.py`) | `GET /models`, `GET /discover`, `POST /models/register`, `GET /hf/search`, `GET /hf/quants`, `POST /models/install`, `GET /jobs`, `POST /jobs/{id}/cancel`, `POST /models/{id}/role`, `POST /models/unload`, `POST /models/{id}/unload`, `POST /models/{id}/load`, `DELETE /models/{id}` | +| Wartung (`routers/maintenance.py`) | `GET /maintenance/updates`, `GET /maintenance/update-details`, `POST` `check-updates`, `os-update`, `engine-update`, `swap-update`, `hermes-update`, `update-all`, `restart`; `GET /maintenance/logs`; `GET`/`POST /maintenance/geheimnisse` | +| System (`routers/system.py`) | `GET /system/status`, `GET /system/services`, `POST /system/backup`, `GET /system/backups`, `POST /system/restart`, `GET /system/token-stats` | +| Lucy und Sprache (`routers/voice.py`) | `POST /alarm`, `POST /voice/announce`, `GET /voice/announcements`, `POST /voice/stt`, `POST /voice/turn`, `POST /voice/chat`, `GET /lucy/stimme/health`, `POST /lucy/stimme/tts`, `POST /lucy/stimme/tts/stream` | +| Sonstiges | `GET /health`, `GET /stream` (Live-Strom, SSE), `GET /routing`, `GET`/`POST /reminders`, `DELETE /reminders/{id}`, `GET /zeitmaschine`, `POST /zeitmaschine/restore` | +| Partner (`routers/partner.py`) | `GET /partner` (Lebenszeichen), `/partner/{pfad}` (Durchreiche, alle Methoden) | + +Dazu `/v1/*` (Weiterleitung an den Gateway), `/hermes-ui/` und die Auslieferung der Oberfläche. Unbekannte +`/api`-Pfade liefern einen Fehler (bei `GET` 404) statt der Startseite. Die MCP-Server in `mcp/` sprechen dieselben +Routen. + +**Herkunftsprüfung** (`backend/services/herkunft.py`, statt Anmeldung): Schreibende Aufrufe (`POST`, `PUT`, `PATCH`, +`DELETE`) auf `/api/` mit dem `Origin` einer fremden Webseite lehnt MC2 mit 403 ab. Erlaubt bleiben Aufrufe ohne +`Origin` (Skripte, `curl`), `Origin: null` bzw. `file://` (Lucy-Desktop), dieselbe Adresse wie die Oberfläche und die +Entwicklungs-Ports 5173, 5180, 5181. `/v1` ist nicht betroffen. + +## Ausblick: der Homelab-Teil (Phasen 3 und 4) + +Stand der Recherche vom 24.09.2026: Community-Script-Container aktualisiert man im Container mit `update`, ohne +Rückfragen mit `PHS_SILENT=1`; die installierte Version steht in `/root/.`. Die Proxmox-API kann keine Befehle +in Containern ausführen und gibt die Liste der Host-Updates nur mit Schreibrecht heraus. Deshalb: ein Lese-Schlüssel, +ein Proxmox-Webhook für Host-Pakete und ein kleiner Ausführer auf dem Host mit festen Aktionen. Container mit +Bind-Mount (z. B. PBS) lassen sich nicht snapshotten, dort gilt „Backup statt Snapshot". Arcane meldet Image-Updates +über eine eigene API mit Schlüssel. Ein Exit-Code 0 beweist kein gelungenes Update; geprüft wird unabhängig +(HTTP-Probe, Dienststatus, Version). Fahrplan: [wissen/OFFENE-FAEDEN.md](wissen/OFFENE-FAEDEN.md). diff --git a/docs/BACKUP.md b/docs/BACKUP.md deleted file mode 100644 index d8ccfb4..0000000 --- a/docs/BACKUP.md +++ /dev/null @@ -1,62 +0,0 @@ -# Backup & Restore - -Sichert den **nicht wiederherstellbaren Zustand** der AI-Box. Code kommt aus Git, -Modelle sind neu ladbar — gesichert wird nur, was sonst weg wäre. - -## Was im Backup ist -- **Gedächtnis:** `/srv/models/mem0/` (Chroma-Vektoren + `history.db`) -- **Hermes:** `~/.hermes/config.yaml`, `~/.hermes/.env` (**Secrets!**), `~/.hermes/plugins/` -- **Engine:** `/etc/llama-swap/config.yaml` - -Nicht enthalten (bewusst): GGUF-Modelle, MC2-Code (Git), venvs, systemd-Units (aus `deploy.sh`). - -Jedes Backup ist ein Tarball `mc2-state-.tar.gz` unter `/srv/models/mc2-backups/`, -`chmod 600` (enthält `.env`). Es werden die letzten **14** behalten. - -## Off-Box-Spiegel (C12) — zweite Kopie WEG von der Box -Nach jedem lokalen Backup spiegelt `backup.sh` die Tarballs per `rsync` auf ein **zweites Gerät** -(Default: **Proxmox-Host** `root@192.168.178.108:/var/lib/vz/mc2-backups`). Stirbt die NVMe der -Box, liegen die Backups noch auf dem Proxmox. Die Retention (14) wird per `--delete` mitgezogen, -`chmod 600` bleibt erhalten. Der Off-Box-Sync ist **best-effort**: schlägt er fehl, gilt das -lokale Backup trotzdem als erfolgreich (Warnung im Log). - -- **Auth:** eigener SSH-Key `~/.ssh/mc2_offsite` (nur für dieses Backup), im Proxmox in - `root/.ssh/authorized_keys` **gehärtet** eingetragen: `from=""`, kein Port-/Agent-/ - X11-Forwarding, kein PTY → der Key kann nur rsync-Backup, keinen Voll-Root-Fernzugang. -- **Ziel/Key überschreibbar:** `MC_BACKUP_OFFSITE` (leer = Off-Box aus) und `MC_BACKUP_OFFSITE_KEY`. - -## Backup erstellen -- **Automatisch:** systemd-Timer `mc2-backup.timer`, täglich ~03:30. Status: - `systemctl --user list-timers mc2-backup.timer` -- **Manuell (Box):** `bash ~/mission-control-v2/deploy/backup.sh` -- **UI:** Wartungs-Drawer → „Snapshot erstellen" - -## Wiederherstellen (Restore) -Restore läuft **nur per CLI auf der Box** (bewusst — er stoppt Dienste und überschreibt Configs). -Vor dem Zurückspielen macht das Skript automatisch ein Sicherheits-Backup des aktuellen Zustands. - -```bash -cd ~/mission-control-v2 -bash deploy/restore.sh --list # vorhandene Backups anzeigen -bash deploy/restore.sh --dry-run latest # zeigen, was passieren würde -bash deploy/restore.sh latest # neuestes wiederherstellen (mit Rückfrage) -bash deploy/restore.sh mc2-state-YYYYMMDD-HHMMSS.tar.gz # bestimmtes Backup -``` - -Ablauf: Sicherheits-Backup → Dienste stoppen (`mem0-service`, `mission-control-2`, -`hermes-gateway`) → Dateien zurückspielen (mem0 wird **ersetzt**, Configs überschrieben) → -Dienste starten → Health-Check. - -### Wenn die Box-Platte tot ist (Off-Box-Restore) -`restore.sh` kennt den Off-Box-Spiegel: fehlt ein Backup lokal, holt es sich das Skript -automatisch vom Proxmox. Kein manuelles Kopieren nötig. - -```bash -bash deploy/restore.sh --list # zeigt lokal UND Off-Box (Proxmox) -bash deploy/restore.sh --pull-offsite # ganzen Off-Box-Bestand nach lokal spiegeln -bash deploy/restore.sh latest # zieht das jüngste — vom Proxmox, falls lokal leer -``` - -## Erledigt -- **Off-Box-Spiegel (C12):** rsync auf den Proxmox-Host, live E2E verifiziert (2026-07-03). - Box ist Bare Metal (kein Proxmox-Gast → kein vzdump), daher aktiver Push statt vzdump-Pull. diff --git a/docs/BETRIEB.md b/docs/BETRIEB.md new file mode 100644 index 0000000..0529d65 --- /dev/null +++ b/docs/BETRIEB.md @@ -0,0 +1,234 @@ +# Betrieb — Handgriffe, Meldungen, Wächter, Deploy, Sicherung, Notfall + +_Stand 24.09.2026, Code in `main` = `75611be`. Für Techniker und Agenten. Gilt für die Instanz auf der KI-Box +(Rolle `box`); die Homelab-Instanz ist noch nicht eingerichtet. Die Bedienung für den User steht in +[BEDIENUNG.md](BEDIENUNG.md), die Update-Kette in [UPDATES.md](UPDATES.md), das Modell-Radar in [RADAR.md](RADAR.md)._ + +## Handgriffe (SSH auf die Box) + +```bash +ssh hitonabi@192.168.178.151 +export XDG_RUNTIME_DIR=/run/user/$(id -u) # sonst sieht systemctl --user nichts +systemctl --user status mission-control-2 mc2-gateway mc2-steward +systemctl --user list-timers # Radar, Sicherung, Updates, Morgenmeldung, projekte-sync +journalctl --user -u mc2-steward -n 100 # Wächter; ebenso mc2-radar, mc2-autoupdate, mc2-backup +curl -s http://127.0.0.1:9001/api/health # Gesamtzustand, Rolle und Instanz +curl -s http://127.0.0.1:9001/api/partner # Lebenszeichen der anderen Instanz (falls eingerichtet) +curl -s http://127.0.0.1:9010/gw/health # Gateway und Motor +bash -lc 'hermes cron list' # Hermes-Jobs (hermes ist über SSH nicht im PATH) +tail -n 50 ~/mc2-notify.log # was zuletzt gemeldet wurde +tail -n 5 /srv/models/mc2-deploy.log # welcher Stand seit wann live ist +sudo journalctl -u llama-swap -n 100 # Motor (System-Dienst) +``` + +## Meldungen + +- **Ein Weg:** `bash ~/mission-control-v2/deploy/notify.sh [-d] [-s "[Betreff]"] "Text"` legt die Meldung in Lucys + Briefkasten und schickt sie an Telegram. Protokoll: `~/mc2-notify.log`, je Meldung eine Zeile mit `OK telegram`, + `OK telegram direkt`, `QUEUED für Morgen-Digest` oder `FALLBACK (…)`. Den Wortlaut nicht ändern: + `backend/services/update_verlauf.py` liest ihn. +- **Zwei Wege zu Telegram (seit 24.09.):** zuerst `hermes send`. Scheitert das oder fehlt Hermes, geht die Meldung + direkt an die Telegram-Bot-API; die Zugangsdaten (`TELEGRAM_BOT_TOKEN`, `TELEGRAM_HOME_CHANNEL`, optional + `TELEGRAM_HOME_CHANNEL_THREAD_ID`) liest `notify.sh` aus der Datei in `MC_TELEGRAM_ENV` (Standard `~/.hermes/.env`). + Das Token steht dabei nicht in der Prozessliste. Live bewiesen am 24.09. um 15:41. +- **Nachtruhe:** 00:00–06:59 landet alles Nicht-Dringende in `~/.hermes/night-queue.txt`. Dringend ist `-d`, ein + Betreff mit „Alarm" oder „Notfall" oder ein Text, der mit „KRITISCH" beginnt. +- **Morgenmeldung:** `mc2-morgenmeldung.timer` (07:00) startet `deploy/morgenmeldung.sh`. Es schickt eine Nachricht + „Guten Morgen, Commander. Heute Nacht gab es N Meldungen" mit höchstens 3500 Zeichen. Drei Versuche im Abstand von + 60 s; scheitert Telegram, bleibt der Stapel als `night-queue.txt.senden` liegen und kommt mit der nächsten + Morgenmeldung. Zuletzt Gesendetes: `night-queue.txt.zuletzt-gesendet`. Das Skript kürzt außerdem + `~/mc2-notify.log` ab 3 MB auf die letzten 2 MB. +- **Scheitern beide Wege,** steht die Meldung mit `FALLBACK` im Protokoll und geht per `wall` an die + Box-Konsolen (`MC_NOTIFY_OHNE_WALL=1` schaltet `wall` ab). +- **Testen:** `bash ~/mission-control-v2/deploy/notify.sh -s "[Test]" "Testmeldung"`; nachts landet sie in der + Warteschlange, mit `-d` kommt sie sofort. + +| Betreff | Absender | Inhalt | +|---|---|---| +| `[Box-Update]` | `autoupdate.sh`, `jobs/sonntags-update.sh` | Ergebnis je Baustein, Wochenbericht, Neustart-Ankündigung; beginnt der Text mit „KRITISCH", ist es dringend | +| `[Box-Problem]` | Wächter | neuer roter Hinweis; nach 6 h erneut mit „Immer noch:" | +| `[Box wieder ok]` | Wächter | ein gemeldeter roter Hinweis ist erledigt | +| `[Homelab-Problem]` / `[Homelab wieder ok]` | Wächter der Homelab-Instanz | wie oben, sobald diese Instanz läuft | +| `[Modell-Radar]` | `backend/radar_lauf.py` | ein Kandidat hat den Nachttest bestanden | +| `[Stack-Radar]` | `jobs/stack-radar.sh` | Samstagsbericht | +| `[Morgenmeldung]` | `morgenmeldung.sh` | Sammelmeldung der Nacht, 07:00 | +| `[Alarm] Deploy` | `deploy.sh` | Deploy gescheitert, der alte Stand läuft wieder (dringend) | +| ohne Betreff | `jobs/news-melden.sh` | Daily News Report, danach die Sprachnachricht | + +## Wächter (`backend/services/waechter.py`, im `mc2-steward`) + +Takt jede Minute, der erste 60 s nach dem Start. Ein Befund wird nach 2 Takten zum Hinweis; Timer-, Job-, +Platten- und Pin-Befunde sofort, die Partner-Instanz erst nach 5 Takten (`MC_WAECHTER_PARTNER_TAKTE`, ein Neustart +soll kein Alarm sein). + +| Prüfung | Rot | Gelb | +|---|---|---| +| Dienste (systemd) | `llama-swap`, `hermes-gateway`, `mc2-gateway`, `mission-control-2` | `voice-service`, `lucy-stimme`, `hermes-builtin-ui` | +| Timer-Läufe | `mc2-backup`, `mc2-morgenmeldung`, `mc2-autoupdate` | `projekte-sync`, `mc2-radar` | +| Hermes-Jobs | fehlgeschlagener Job mit „update" im Namen | andere fehlgeschlagene Jobs; Werkzeugfehler im letzten Lauf | +| Kern (HTTP) | Motor, Hirn, Hermes, MC2, Gateway antworten nicht; die Spracherkennung nur, wenn sie nicht schläft | – | +| Platte (`MC_DATEN_DIR`, auf der Box `/srv/models`) | ab 90 % | ab 80 % | +| Festgehaltene Updates | – | jeder Pin | +| Partner-Instanz (nur mit `MC_PARTNER_URL`) | „ antwortet nicht" | – | +| Abgestürzte Prüfung | – | „Eine Prüfung des Wächters lief nicht" | + +In der Rolle `homelab` prüft der Wächter bisher nur Platte und Partner und meldet mit „[Homelab-Problem]". + +- **Selbstreparatur:** Nur ein abgestürzter Dienst (`ActiveState=failed`) wird neu gestartet, höchstens 2× je Stunde + und Hinweis. Ein gestoppter Dienst (`inactive`) wird nur gemeldet. Ein schlafender Dienst (`inactive` und + `disabled`) ist kein Befund. +- **Während eines Updates** (ein Prozess `autoupdate.sh`, `update-engine.sh`, `update-swap.sh` oder `hermes … update` + läuft) prüft er Dienste und Kern nicht und repariert nichts. Die Oberfläche zeigt dann „Update läuft". +- **Meldungen:** Rote Hinweise gehen an Telegram und in den Briefkasten, bei Fortbestand alle 6 h erneut; nachts gilt + die Nachtruhe. Gelbe Hinweise stehen nur in der Oberfläche. +- **Knöpfe am Hinweis:** „Neu starten" bzw. „Starten", „Protokoll", „Jetzt erneut laufen lassen" (Timer), + „Erneut ausführen" (`hermes cron run`), „Freigeben" (Pin), „Ausblenden bis zum nächsten Lauf" (Werkzeugfehler + eines Jobs; MC2 merkt sich den Lauf in `/srv/models/mc2-quittiert.json`, hat der nächste Lauf wieder Fehler, + erscheint der Hinweis erneut). +- Der Wächter ist der einzige Schreiber von `/srv/models/mc2-waechter.json`. Ist sein letzter Takt älter als + 5 Minuten, zeigt die Startseite „Wächter schweigt". + +## Dienste schlafen legen und wecken + +- Schlafen legen: `systemctl --user disable --now ` (seit 24.09.: `box-console`, `voice-service`). +- Wecken bis zum nächsten Neustart der Box: Knopf „Wecken" unter „Dienste und Protokolle" oder + `systemctl --user start `. +- Dauerhaft wecken: `systemctl --user enable --now `; für einen Neuaufbau die Unit zusätzlich in `AKTIV` in + `deploy/deploy.sh` eintragen. + +## Deploy + +Voraussetzung: Der Stand liegt in `main` auf Gitea (Branch, Prüftor grün, Merge). Dann auf der Box: + +```bash +bash ~/mission-control-v2/deploy/deploy.sh +``` + +**Stufe 1** (die bisherige Fassung des Skripts): Sperre gegen einen zweiten Deploy (`flock` auf +`$XDG_RUNTIME_DIR/mc2-deploy.lock`). Laufen Update- oder Download-Jobs (`/api/jobs`), bricht der Deploy ab. Dann den +alten Stand merken, `git fetch` und `git merge --ff-only origin/main` und die neue Fassung als Stufe 2 starten. + +**Stufe 2** (die neue Fassung): + +1. `pip install` aus `backend/requirements.txt` und `backend/requirements-dev.txt`. +2. Prüftor `deploy/pruefen.sh`. +3. llama-swap-Config nur, wenn sich `deploy/llama-swap.config.yaml` in diesem Deploy geändert hat: Sicherung + `/etc/llama-swap/config.yaml.bak-` (die letzten 5 bleiben), dann ersetzen. llama-swap lädt dank + `--watch-config` selbst neu. Weicht die lebende Datei nur ab, gibt es einen Hinweis und sonst nichts. +4. Alle Repo-Units nach `~/.config/systemd/user/`, dazu die Drop-ins `mc2-steward.service.d/warmset.conf` und + `mission-control-2.service.d/override.conf`; `daemon-reload`; `enable` für die Units in `AKTIV`; Timer starten + (den Update-Timer nur, wenn er noch nicht läuft). +5. Cron-Skripte `deploy/jobs/*.sh` und `*.py` nach `~/.hermes/scripts/`. +6. Skills `deploy/skills/*` nach `~/.hermes/skills//`, abgelöste Skills nach + `~/.hermes/skills-archiv/`. Plugins `deploy/hermes-plugins/*` nach `~/.hermes/plugins/`; aktiviert werden sie + einmalig von Hand, Hermes startet der Deploy nicht neu. +7. Neustart von `mc2-gateway`, `mission-control-2` und `mc2-steward`, dann bis zu 60 s Nachprüfung: `/api/health`, + `/gw/health`, Steward aktiv. + +Erfolg: eine Zeile in `/srv/models/mc2-deploy.log` und „Deploy ist live". Scheitert ein Schritt: +`git reset --hard` auf den alten Stand, llama-swap-Config zurück (falls ersetzt), Dienste neu, Zeile `GESCHEITERT` im +Log und die dringende Meldung „[Alarm] Deploy". + +Schalter: `MC_DEPLOY_TROTZDEM=1` (trotz laufender Jobs), `MC_DEPLOY_OHNE_TESTS=1` (Prüftor überspringen, nur im +Notfall), `MC_DEPLOY_SKIP_SWAP_CONFIG=1` (llama-swap-Config nie anfassen). + +Nicht Teil des Deploys: das Frontend bauen (`frontend/dist` kommt fertig aus Git), die Root-Kopie von `warmup.sh`, +Neustarts von llama-swap und Hermes. + +**Prüftor** `deploy/pruefen.sh` (am PC vor dem Push, auf der Box im Deploy): Shell-Syntax (`bash -n` für +`deploy/*.sh` und `deploy/jobs/*.sh`), Python-Syntax aller versionierten `.py` außerhalb von `frontend/`, +`ruff check .` (nur wo ruff installiert ist; `requirements-dev.txt` bringt nur pytest mit), Import von `app`, +`gateway_app`, `steward` und `radar_lauf`, dann `pytest backend/tests`. Die Frontend-Prüfungen (`npm run lint`, +`npm test`, `npm run build`) laufen nur am PC. + +## Sicherung + +- **Wann:** `mc2-backup.timer` täglich 03:30 (+≤5 min); außerdem vor jedem Hermes-Update, vor jedem Zurückspielen und + per Knopf „Jetzt sichern". Von Hand: `bash ~/mission-control-v2/deploy/backup.sh`. +- **Wohin:** `/srv/models/mc2-backups/mc2-state-.tar.gz`, Rechte 600 (enthält `~/.hermes/.env`), die letzten 14 + bleiben. Danach Spiegel per `rsync --delete` auf den Proxmox-Host (`/var/lib/vz/mc2-backups`, eigener Schlüssel + `~/.ssh/mc2_offsite`; Ziel und Schlüssel über `MC_BACKUP_OFFSITE` und `MC_BACKUP_OFFSITE_KEY`). Scheitert der + Spiegel, gilt die lokale Sicherung trotzdem. +- **Inhalt:** + - aus `~/.hermes`: `config.yaml`, `.env`, `plugins/`, `cron/`, `state/`; seit 24.09. auch `memories/` (Lucys + Gedächtnis), `SOUL.md`, `skills/` und `scripts/`; dazu Reste der früheren Desktop-Anbindung (`agent-hooks/`, + `shell-hooks-allowlist.json`, `desktop-gateway-token`, `pc-paths.yaml`, Drop-in `session-token.conf`); + - `/etc/llama-swap/config.yaml`; + - der Box-Wart-Zustand `/srv/models/mc2-*.json`; + - die Units aus `~/.config/systemd/user/` und `/etc/systemd/system/llama-swap.service.d/` (nur zum Nachschlagen, + `restore.sh` spielt sie nicht zurück); + - Lucys Stimmreferenz `~/.lucy-stimme/app/ref.wav`; + - `known-good/`: `pip freeze` je venv, Versionen (OS, Kernel, MC2- und Hermes-Commit, llama.cpp, llama-swap) und + die Liste der Modelldateien. +- Eine Sicherung ist seit 24.09. rund 12 MB groß. +- **Nicht enthalten:** Modelle (neu ladbar), Code (Git), venvs, `~/.ssh` (auch nicht der Spiegel-Schlüssel). +- **Weitere Schichten:** Die Sicherung der Box nach PBS (`pbs-backup.timer`) ist seit 21.08. aus. PBS sichert die + Proxmox-Container, das QNAP macht Snapshots; beides läuft auf den anderen Geräten und ist hier nicht geprüft. + +## Zurückspielen + +- **Per Oberfläche:** Updates → Sicherungen → „Zurückspielen". MC2 startet `restore.sh --yes ` als eigene + systemd-Unit (`mc2-restore-`), weil `restore.sh` MC2 selbst stoppt. +- **Per SSH:** + +```bash +cd ~/mission-control-v2 +bash deploy/restore.sh --list # lokal und auf dem Proxmox-Host +bash deploy/restore.sh --dry-run latest # zeigt, was passieren würde +bash deploy/restore.sh latest # mit Rückfrage; --yes ohne +bash deploy/restore.sh --mit-gedaechtnis latest # Gedächtnis und Zustand auch überschreiben +bash deploy/restore.sh --pull-offsite # alle Sicherungen vom Proxmox-Host holen +``` + +Ablauf: Sicherheits-Sicherung des jetzigen Stands → Dienste `mission-control-2`, `mc2-gateway`, `mc2-steward`, +`hermes-gateway` stoppen → Hermes-Config, `.env`, Plugins, `cron/`, `state/`, llama-swap-Config und die Desktop-Reste +zurück → Gedächtnis, `SOUL.md`, Skills, Cron-Skripte und `mc2-*.json` nur, wenn sie fehlen oder mit +`--mit-gedaechtnis` → Dienste starten → `/api/health`. Fehlt eine Sicherung lokal, holt `restore.sh` sie vom +Proxmox-Host. Sicherungen von vor dem 27.08. enthalten noch das Verzeichnis des abgelösten Gedächtnis-Dienstes; es wird +nicht zurückgespielt. + +## Notfall + +**Box tot oder Oberfläche weg:** + +1. Strom und Netz prüfen, die Box einmal per Knopf neu starten. Alles startet von selbst (systemd, Linger). Nach 2–3 + Minuten `http://192.168.178.151:9001` öffnen. +2. Oberfläche immer noch weg: per SSH `systemctl --user status mission-control-2` und + `journalctl --user -u mission-control-2 -n 100`. Hilft ein Neustart der Dienste nicht: + `bash ~/mission-control-v2/deploy/restore.sh latest`. +3. Totalschaden (neue Platte, neue Hardware): [WIEDERAUFBAU.md](WIEDERAUFBAU.md). + +**Deploy kaputt:** `deploy.sh` rollt selbst zurück. Von Hand: den vorigen Commit aus `/srv/models/mc2-deploy.log` +nehmen, `git -C ~/mission-control-v2 reset --hard ` und +`systemctl --user restart mc2-gateway mission-control-2 mc2-steward`. + +**llama-swap-Config kaputt:** `ls -t /etc/llama-swap/config.yaml.bak-*`, die passende Sicherung nach +`/etc/llama-swap/config.yaml` kopieren; llama-swap lädt selbst neu. Ältere Stände stecken in jeder Sicherung. + +**Motor startet keine Modelle:** `sudo journalctl -u llama-swap -n 100`. Nach einem Engine-Sprung ist die typische +Ursache ein gestrichenes Flag ([wissen/FALLEN.md](wissen/FALLEN.md)). Den vorigen Build hebt `update-engine.sh` in +`/opt/llamacpp-vulkan.bak` auf, die vorige llama-swap-Version `update-swap.sh` in `/usr/local/bin/llama-swap.bak`. + +**Hermes kaputt:** `journalctl --user -u hermes-gateway -n 100`, dann `bash ~/mission-control-v2/deploy/hermes-postcheck.sh`. +Rückweg wie in [UPDATES.md](UPDATES.md): alter Commit in `~/.hermes/hermes-agent`, Config aus der letzten Sicherung, +`systemctl --user restart hermes-gateway`. + +**Festgehaltener Baustein:** Knopf „Freigeben" (Startseite oder Updates-Seite); der nächste Sonntagslauf versucht das +Update erneut. + +## Wo was liegt (Box) + +| Pfad | Inhalt | +|---|---| +| `~/mission-control-v2` | Checkout von `main` (nur über `deploy.sh` ändern) | +| `~/mission-control-v2/backend/.venv` | Python-Umgebung des Box-Warts (Python 3.14) | +| `~/.config/systemd/user/` | User-Units und Drop-ins | +| `/srv/models/` | Modelle, Box-Wart-Zustand (`mc2-*.json`), Sicherungen (`mc2-backups/`), Radar-Downloads (`radar/`) | +| `/etc/llama-swap/config.yaml` | lebende llama-swap-Config (+ `.bak-*`) | +| `/opt/llamacpp-vulkan/`, `/usr/local/bin/llama-server` | Motor (llama.cpp Vulkan) | +| `/usr/local/bin/llama-swap`, `/usr/local/bin/llama-swap-warmup.sh` | Router und Vorwärm-Skript (Root-Kopie) | +| `~/.hermes/` | Hermes: Code (`hermes-agent/`), `config.yaml`, `.env`, `SOUL.md`, `memories/`, `skills/`, `scripts/`, `cron/`, `logs/` | +| `~/.lucy-stimme/` | Lucys Stimme (pocket-tts), Referenz `app/ref.wav` | +| `~/.voice/` | Umgebung der Spracherkennung (`voice_service/install.sh`) | +| `~/projekte/` | Spiegel der Gitea-Repos (`projekte-sync`) | +| `~/mc2-notify.log`, `~/.hermes/night-queue.txt` | Meldeprotokoll, Nacht-Warteschlange | diff --git a/docs/DISASTER_RECOVERY.md b/docs/DISASTER_RECOVERY.md deleted file mode 100644 index de56a06..0000000 --- a/docs/DISASTER_RECOVERY.md +++ /dev/null @@ -1,325 +0,0 @@ -# Konzept: Bare-Metal-Wiederaufbau + First-Run-Wizard (VOLLSTÄNDIG) - -> **Zweck:** Plan für den „hard crash"-Fall — die Box (`tobisniceaiarbeitstier`, Ubuntu 26.04, -> AMD Ryzen AI MAX+ 395 / gfx1151, 122 GB RAM) muss von **null** wiederherstellbar sein. -> **Anspruch:** *ALLES* ist erfasst — Engine, Hermes-Konfig 1:1, Voice/Klonstimme, -> Memory, Browser, MCP, Skills, Secrets. Pro Komponente entscheidet der Nutzer im Wizard: -> **1:1 zurück** · **neu/Default** · **weglassen**. -> -> Dieses Dokument ist die **Spezifikation für eine künftige Implementierungs-Session** — es baut -> noch nichts. Stand: 2026-06-28. - ---- - -## 1. Leitprinzip: 1:1 ODER modular — der Nutzer wählt - -Der Wizard behandelt jede Komponente als **eigene Kachel mit drei Modi**: - -| Modus | Bedeutung | -|---|---| -| 📦 **1:1 aus Backup** | Exakter alter Zustand wird zurückgespielt (Configs, Daten, Klonstimme). | -| 🆕 **Neu / Default** | Frische Installation mit sinnvollen Defaults (z.B. entfesseltes Hermes-Profil). | -| ⏭️ **Weglassen** | Komponente wird (vorerst) nicht installiert. | - -Ein „Alles 1:1"-Knopf wählt überall 📦 (wo ein Backup existiert), sonst 🆕. So bekommt der Nutzer -entweder den exakten alten Stand oder kann gezielt entrümpeln. - ---- - -## 2. Was es schon gibt (wiederverwenden) - -| Asset | Datei | Deckt ab | -|---|---|---| -| Code-Deploy | `deploy/deploy.sh` | git pull + venvs + Units + Restart. **Setzt Engine/llama-swap/Dirs/venvs voraus.** | -| Zustands-Backup | `deploy/backup.sh` + `mc2-backup.{service,timer}` | **Aktuell** (live): mem0, `~/.hermes/{config.yaml,.env,plugins}`, llama-swap-config. Retention 14. **Für echtes 1:1 zu erweitern** — fertiges Snippet in **Anhang B** (§5). | -| Restore | `deploy/restore.sh` | Spielt Tarball zurück (mit Pre-Restore-Sicherung). | -| Engine | `deploy/provision-engine.sh` | llama.cpp Vulkan/RADV-Build (root). | -| Voice-Setup | `voice_service/install.sh` | venv + Piper-Binary + dt. Piper-Stimmen + Chatterbox (best-effort). | -| Updates | `deploy/update-engine.sh`, `update-swap.sh` | Laufende Engine/Router-Updates. | -| Postchecks | `deploy/stack-postcheck.sh`, `hermes-postcheck.sh` | Funktionsprüfung → Wizard-Verifikationsschritt. | - ---- - -## 3. VOLLSTÄNDIGER Komponenten-Katalog - -> **Scan-verifiziert (2026-06-28)** gegen `backend/config.py`, alle `backend/services/*` & `routers/*`, -> `frontend/src/{nav.ts,views,components/voice}`, `voice_service/app.py`, `mem0_service/`, `deploy/*`. -> Alle persistenten Schreibpfade, Dienste, Secrets und Env-Vars sind unten erfasst. - -Legende Restore-Quelle: 📦 = aus Backup-Tarball · ⬇️ = Re-Download/Install (Skript) · 🌐 = Git · -🔑 = Secret (Nutzer/Generieren) · 🆕 = im Wizard neu gewählt. -„Im Backup?" = deckt der **aktuelle** `backup.sh` es ab. - -### A · OS & System (L0, root/sudo, einmalig) -| Komponente | Ort | Quelle | Im Backup? | -|---|---|---|---| -| Ubuntu 26.04, User `hitonabi`, `enable-linger` | — | ⬇️ manuell | n/a | -| System-Pakete: python3.14+venv, git, curl, jq, ttyd, uv | — | ⬇️ apt/curl | nein | -| Vulkan-Stack: mesa-vulkan-drivers, libvulkan1, vulkan-tools | — | ⬇️ apt | nein | -| Chrome-Libs (Browser): `agent-browser install --with-deps` | — | ⬇️ apt | nein | -| Verzeichnis-Layout: `/srv/models/{,mem0,mc2-backups,drafts}`, `/etc/llama-swap/` | — | ⬇️ mkdir+chown | nein | - -### B · Inferenz-Engine + Router (L1, root) -| Komponente | Ort | Quelle | Im Backup? | -|---|---|---|---| -| llama.cpp (Vulkan) → `llama-server` | `/opt/llamacpp-vulkan`, `/usr/local/bin/llama-server` | ⬇️ provision-engine.sh / update-engine.sh (ggml-org Release) | nein (re-build) | -| **llama-swap** Binary (Router `:8080`) | `/usr/local/bin/llama-swap` | ⬇️ **bekannt:** `mostlygeek/llama-swap`-Release (Rezept in `update-swap.sh`) | nein (re-download) | -| **llama-swap systemd-System-Unit** | `/etc/systemd/system/llama-swap.service` (+ `.d/` Drop-ins) | ⬇️ Bootstrap legt sie an — **kompletter Unit-Inhalt in Anhang A** (von der Box abgegriffen; Drop-ins via provision-engine.sh) | nein | -| llama-swap-Config (Modelle/Rollen) | `/etc/llama-swap/config.yaml` | 📦 | **ja** | -| Draft-Modelle (Spec-Decoding) | `/srv/models/drafts/` | ⬇️ Re-Download | nein | - -### C · MC2-App (L2, userspace) -| Komponente | Ort | Quelle | Im Backup? | -|---|---|---|---| -| MC2-Code | `~/mission-control-v2` | 🌐 Git (Gitea) | n/a | -| Backend-venv (Python 3.14) | `backend/.venv` | ⬇️ deploy.sh | nein (rebuild) | -| Frontend (gebaut) | `frontend/dist` | 🌐 Git | n/a | -| systemd-User-Dienst `mission-control-2` (`:9001`) | `~/.config/systemd/user/` | ⬇️ deploy.sh | nein | - -### D · 3D-Avatar — **ausgebaut (28.08.2026)** - -MC2 hat keinen Avatar mehr. `frontend/public/avatar.vrm` (24,5 MB) lag im Auslieferungsordner, -wurde aber von keiner Zeile des Frontends referenziert — der Renderer `Avatar3D.tsx` war schon -vorher verschwunden. Beides ist beim v3-Umbau (Etappe P0) entfernt worden; damit fällt auch die -`.gitignore`-Sonderregel und der Direkt-Deploy-Schritt weg. **Nichts wiederherzustellen.** - -### E · Voice-Sidecar (STT + TTS + Klonstimme) -| Komponente | Ort | Quelle | Im Backup? | -|---|---|---|---| -| Voice-venv (Python 3.12) | `~/.voice/venv` | ⬇️ install.sh | nein (rebuild) | -| STT (faster-whisper Modell) | `~/.voice/` (Cache) | ⬇️ install.sh / 1. Start | nein | -| Piper-Binary + dt. Stimmen (thorsten/kerstin) | `~/.voice/piper`, `~/.voice/voices` | ⬇️ install.sh | nein | -| Chatterbox (Premium-TTS, CPU-torch) | venv | ⬇️ install.sh (best-effort) | nein | -| **Klonstimme / Voice-Referenz-Audio** (Nutzer) | `~/.voice/refs/ref.wav` (`VOICE_REFS_DIR`, `/api/voice/reference`) | 📦 | **nein → Anhang B** | -| ElevenLabs-Key | `~/.hermes/.env` | 🔑 | ja | -| Stimm-/Lautstärke-Wahl | Browser-`localStorage` (pro Gerät) | 🆕 client | n/a | -| Dienst `voice-service` (`:8650`) | systemd-User | ⬇️ deploy.sh | nein | - -### F · Mem0 (Langzeitgedächtnis) -| Komponente | Ort | Quelle | Im Backup? | -|---|---|---|---| -| Mem0-venv (Python 3.12, uv) | `~/.mem0/venv` | ⬇️ deploy.sh | nein (rebuild) | -| **Gedächtnis-Daten (Chroma + history.db)** | `/srv/models/mem0/` | 📦 | **ja** | -| Hermes-Memory-Plugin | `~/.hermes/plugins/mc2-memory/` | 📦/🌐 | ja | -| Dienst `mem0-service` (`:8765`) | systemd-User | ⬇️ deploy.sh | nein | - -### G · Hermes-Agent (das Herz) -| Komponente | Ort | Quelle | Im Backup? | -|---|---|---|---| -| Hermes-Code | `~/.hermes/hermes-agent` | 🌐 Git (NousResearch) | nein (re-clone) | -| Bundled Node v22 | `~/.hermes/node/bin` | ⬇️ (kommt mit Hermes) | nein | -| Hermes-venv | `~/.hermes/hermes-agent/venv` | ⬇️ rebuild | nein | -| **`config.yaml` 1:1** (Toolsets, MCP, Engine, Browser, Personalities, alles) | `~/.hermes/config.yaml` | 📦 | **ja** | -| **`.env` (Secrets: TELEGRAM_BOT_TOKEN, API_SERVER_KEY, ElevenLabs …)** | `~/.hermes/.env` | 📦/🔑 | **ja** | -| Plugins | `~/.hermes/plugins/` | 📦 | ja | -| **Skills (eigene)** | `~/.hermes/skills/` (45M; `.hub`-Cache re-downloadbar) | 📦 | **nein → Anhang B (ohne .hub)** | -| Sessions/History (optional) | `~/.hermes/sessions/` (856K) | 📦 | **nein → Anhang B** | -| Checkpoints (optional) | `~/.hermes/checkpoints/` (existiert nicht) | ⏭️ skip | nein | -| **Token-/Ersparnis-Statistik** | `~/.hermes/token_stats.json` | 📦 | **nein → Anhang B** | -| Dienst `hermes-gateway` (`:8642`) | systemd-User | ⬇️ | nein | -| Desktop-Gateway `hermes-builtin-ui` (`:9119`) | systemd-User | ⬇️ deploy.sh | nein | -| Desktop-Gateway-Token-Drop-in | `~/.config/systemd/user/hermes-builtin-ui.service.d/session-token.conf` | 🔑 neu erzeugen (Wert auch in `~/.hermes/desktop-gateway-token`, chmod 600) | **nein** | -| Desktop-Gateway-Token-Kopie | `~/.hermes/desktop-gateway-token` (chmod 600) | 🔑 | **nein** | - -### H · MCP-Server (4) -| Komponente | Ort | Quelle | Im Backup? | -|---|---|---|---| -| `mission-control-memory`, `-stack` (MC2-venv) | `~/mission-control-v2/mcp/*.py` | 🌐 Git | über config.yaml | -| `hermes-pc-control`, `hermes-web-fetch` (Hermes-venv) | dito | 🌐 Git | über config.yaml | -| MCP-Verdrahtung | `~/.hermes/config.yaml → mcp_servers` | 📦 | ja | - -### I · Browser-Stack -| Komponente | Ort | Quelle | Im Backup? | -|---|---|---|---| -| agent-browser (npm global) | `~/.local/...`, symlink `~/.hermes/node/bin` | ⬇️ npm i -g | nein | -| Chrome (engine) + System-Libs | `~/.agent-browser/browsers/` + apt-Libs | ⬇️ install --with-deps | nein | -| lightpanda (optional, leicht) | `~/.local/bin/lightpanda` | ⬇️ Download | nein | -| Browser-Verdrahtung (engine=chrome, toolset) | `~/.hermes/config.yaml` | 📦 | ja | - -### J · Modelle (GGUF — der große Brocken) -| Komponente | Ort | Quelle | Im Backup? | -|---|---|---|---| -| GGUF-Modelle (Brain, fast, heavy, coder, vision, embedding …) | `/srv/models/` | ⬇️ Re-Download aus llama-swap-Manifest | **nein (zu groß, bewusst)** | - -### K · Extern (anderer Rechner) -| Komponente | Ort | Quelle | Im Backup? | -|---|---|---|---| -| PC-Executor (Windows) | `client/hermes-pc/` → Scheduled Task | 🌐 Git + Task | n/a (anderer Host) | -| Hermes Desktop (Windows-App) | `%LOCALAPPDATA%\hermes` + `connection.json` in `%APPDATA%\Hermes\` | ⬇️ Hermes-Setup.exe + Remote-URL/Token neu eintragen | n/a (anderer Host) | - ---- - -## 4. Ziel-Architektur - -### 4.1 `deploy/bootstrap.sh` — orchestrierter From-Zero-Lauf -- **Idempotent & resumierbar** (Schritt-Marker in `~/.mc2-bootstrap.state`). -- **Getrennt nach sudo-Bedarf**: `bootstrap-root.sh` (L0+L1, bewusst mit sudo) + `bootstrap.sh` - (L2+, sudo-frei = Kern ist `deploy.sh`). Passt zum Nordstern „Runtime ohne sudo". -- **Mündet in den Wizard**: startet MC2 im First-Run-Modus, gibt Wizard-URL aus. - -Phasen: `0 Vorflug → 1[sudo] System+Dirs → 2[sudo] Engine+llama-swap → 3 Code(MC2+Hermes+Node) -→ 4 venvs(backend/mem0/voice/hermes) → 5 Browser → 6 deploy.sh(Units/enable/restart) -→ 7 Restore(optional) → 8 Wizard hoch + postcheck`. - -### 4.2 First-Run-Wizard (browserbasiert, von MC2 serviert) -MC2 erkennt unkonfigurierten Zustand → Frontend-Route `/setup` statt Dashboard. Schritte: - -1. **Systemcheck** — Live-Ampel je Komponente aus §3 (`GET /api/setup/status` + `…/system/services`). -2. **Restore-Quelle wählen** — Backup-Tarball erkennen → globaler Modus „Alles 1:1" / „selektiv" / „frisch". -3. **Komponenten-Auswahl (Kernstück)** — pro Katalog-Eintrag aus §3 eine Kachel mit - 📦/🆕/⏭️ (siehe §1). Zeigt Größe + ob Backup-Daten vorhanden. -4. **Netzwerk & Identität** — Box-IP (→ `HERMES_TERMINAL_URL`, `PC_EXECUTOR_URL`), Hostname. -5. **Secrets** 🔑 — `TELEGRAM_BOT_TOKEN`, erlaubte User-ID, `API_SERVER_KEY` (oder generieren), - ElevenLabs-Key → `~/.hermes/.env` (chmod 600). Bei 📦 vorbefüllt aus Backup. -6. **Hermes-Profil** — Brain-Alias + Toolset-Profil (Default = entfesselt: vision/tts/memory/browser - an, image_gen/video aus — siehe [[project-hermes-setup]]). Bei 📦 = exakte alte `config.yaml`. -7. **Voice** — Stimme/Klonstimme - (📦 Referenz-Audio zurück, oder neu aufnehmen/hochladen). -8. **Modelle** — aus llama-swap-Manifest automatisch nachladen (`POST /api/models/install`, - Fortschritt `GET /api/jobs`) ODER geführte Discover-Neuauswahl. Reihenfolge: Brain → heavy → Rest. -9. **Externe Checkliste** — PC-Executor (Windows-Task), Hermes Desktop (App + Token) als Haken. -10. **Verifikation & Abschluss** — `stack-postcheck.sh` + `hermes-postcheck.sh` → grün/rot-Liste → Dashboard. - -**Technik:** neuer Router `backend/routers/setup.py` -(`GET /api/setup/status`, `POST /api/setup/{secrets,network,hermes,components,finish}`). -First-Run-Gate: MC2 prüft beim Boot Marker `~/.mc2-setup-done` bzw. Pflicht-Secrets → leitet auf `/setup`. - ---- - -## 5. Backup-Scope für echtes 1:1 ERWEITERN (umzusetzen — Snippet in Anhang B) - -Der **aktuelle** `backup.sh` (live auf der Box, unverändert) reicht für „1:1" nicht. Für die Bau-Session -liegt das **fertige Erweiterungs-Snippet in Anhang B** — es ergänzt den Tarball um: - -- **Voice-Klonstimme** → `~/.voice/refs/` (`ref.wav`) -- **Token-/Ersparnis-Statistik** → `~/.hermes/token_stats.json` -- **Hermes-Skills** → `~/.hermes/skills/` (re-downloadbarer `.hub`-Cache per `tar --exclude` ausgelassen) -- **Hermes-Sessions/History** → `~/.hermes/sessions/` - -Restore soll skills/sessions **mergen** (frisch geladenen `.hub` nicht überschreiben) und `voice-service` -mit neu starten. - -**Offen bleibt:** nichts — der letzte offene Punkt (eigene Avatare) ist mit dem Avatar-Ausbau erledigt (§3·D). - -**Bewusst NICHT im Backup (re-downloadbar/rebuildbar):** Piper-Stimmen (install.sh), `.hub`-Skill-Cache, -`/srv/models/mc2-discover.json` (Cache), `/srv/models/mc2-memory.db` (Legacy-Migration), alle venvs, GGUF-Modelle. - -→ Aufgabe der Bau-Session: `backup.sh` + `restore.sh` um diese Pfade erweitern (mit klarer -„opt-in für große/optionale Teile"-Logik), und den MANIFEST-Inhalt entsprechend. - ---- - -## 6. Secrets-Strategie -- Single Source: `~/.hermes/.env` (chmod 600), im Tarball (selbst 600). -- Rebuild ohne Backup → Wizard-Schritt 5 erzeugt sie. `API_SERVER_KEY` generierbar; Telegram/ElevenLabs liefert Nutzer. -- Nie ins Git, nie in Logs, nie in die MC2-DB. **Off-Box-Spiegelung des Tarballs noch offen** ([[mc2-backup-restore]]). - -## 7. Modell-Strategie -- **A (empfohlen):** `/etc/llama-swap/config.yaml` (im Backup) listet alle Modelle → Wizard leitet Repos/Quants ab und lädt automatisch (`/api/models/install`). „Ein Klick, lädt über Nacht." -- **B:** geführte Discover-Neuauswahl je Rolle. Reihenfolge Brain → heavy → Rest, danach `warmup.sh`. - ---- - -## 8. Implementierungs-Reihenfolge (neue Session) -1. ✅ **Box-Verifikation erledigt (2026-06-28, nur gelesen — nichts verändert):** llama-swap.service-Inhalt - in **Anhang A**; skills/sessions/refs/token_stats existieren (Pfade in §3 verifiziert); Backup-Erweiterung - als fertiges Snippet in **Anhang B**. **Keine offenen Box-Fragen mehr** außer §10·3–6. → mit Schritt 2 starten. -2. `backup.sh`/`restore.sh` um die 1:1-Lücken erweitern (§5). -3. `deploy/bootstrap.sh` + `bootstrap-root.sh` (Phasen, State-Marker); llama-swap-Install skripten. -4. `backend/routers/setup.py` + First-Run-Gate. -5. Frontend `/setup`-Wizard (Schritte 1–10), Komponenten-Auswahl als Kernstück; bestehende Views - (Cockpit/AgentView/Sprechen) wiederverwenden. -6. Modell-Restore-aus-Manifest. -7. **Abnahme in frischer VM** (§9). - -Vorschlag: 2–4 zuerst (Skelett lauffähig), Wizard danach. Branch + PR. - -## 9. Abnahmekriterien -- Frisches Ubuntu 26.04 → `bootstrap-root.sh` + `bootstrap.sh` → laufender Stack, kein Spezialwissen. -- Postchecks grün; Telegram, Voice (inkl. Klonstimme bei 📦), Browser funktionieren. -- „Alles 1:1" stellt mem0, Hermes-config.yaml, Secrets, Klonstimme, Skills exakt wieder her. -- Selektiver Modus: einzelne Komponenten ⏭️ überspringbar, Stack läuft trotzdem. -- Idempotenz: zweiter Lauf = No-op. - -## 10. Offene Entscheidungen (vor dem Bau) -1. ~~llama-swap systemd-Unit-Inhalt~~ → **geklärt: kompletter Unit in Anhang A** (in der Bau-Session anlegen). -2. ~~Voice-Referenz-Audio-Ort~~ → **geklärt: `~/.voice/refs/`** (Backup-Snippet in Anhang B, in der Bau-Session umsetzen). -3. ~~**Eigene Avatare**~~ — entfallen: Avatar am 28.08.2026 ausgebaut (§3·D). -4. **Off-Box-Backup-Ziel** (NAS/2. Platte/Cloud) — [[mc2-backup-restore]]. -5. **Python 3.14** auf frischem Ubuntu beschaffen (deadsnakes?). -6. **Bootstrap-sudo-Modell**: getrenntes `bootstrap-root.sh` (empfohlen) vs. interaktives sudo. - -## 11. Referenzen -- `backend/config.py`, [[project-mc2-architecture]], [[project-stack-state]] -- [[project-hermes-setup]], `docs/HERMES_SETUP.md` (entfesseltes Profil, Browser, PC-Executor) -- [[voice-sprechen-feature]] (Voice + 3D-Avatar), [[mem0-memory-architecture]], [[mc2-backup-restore]] -- `deploy/{deploy,backup,restore,provision-engine,stack-postcheck,warmup}.sh`, `voice_service/install.sh` - ---- - -## Anhang A — `llama-swap.service` (1:1 von der Box abgegriffen, 2026-06-28) - -Base-Unit nach `/etc/systemd/system/llama-swap.service` (root). User/GFX sind boxspezifisch -(hitonabi, gfx1151 → HSA 11.5.1) — auf anderer Hardware anpassen. Die `.d/`-Drop-ins -(`vulkan.conf`, `warmup.conf`) legt `provision-engine.sh` an. - -```ini -[Unit] -Description=llama-swap (lokaler LLM Router) -After=network-online.target -Wants=network-online.target - -[Service] -Type=simple -User=hitonabi -Environment=HSA_OVERRIDE_GFX_VERSION=11.5.1 -Environment=PATH=/usr/local/bin:/usr/bin:/bin -ExecStart=/usr/local/bin/llama-swap --config /etc/llama-swap/config.yaml --listen 0.0.0.0:8080 --watch-config -Restart=on-failure -RestartSec=3 - -[Install] -WantedBy=multi-user.target -``` -```ini -# /etc/systemd/system/llama-swap.service.d/vulkan.conf -[Service] -Environment=LD_LIBRARY_PATH=/opt/llamacpp-vulkan -``` -```ini -# /etc/systemd/system/llama-swap.service.d/warmup.conf -[Service] -ExecStartPost=-/usr/local/bin/llama-swap-warmup.sh -``` - -**Erstinstall-Reihenfolge:** `bash deploy/update-swap.sh` (Binary) → Unit kopieren → -`sudo bash deploy/provision-engine.sh` (Engine+Drop-ins) → `sudo systemctl enable --now llama-swap`. - ---- - -## Anhang B — Backup-Scope-Erweiterung für echtes 1:1 (fertiges Snippet) - -In `deploy/backup.sh` ergänzen (Variablen oben: `VOICE="${VOICE_HOME:-$HOME/.voice}"`, -Stage zusätzlich `"$STAGE/voice"`): - -```bash -# 1:1-Erweiterung (scan-verifiziert 2026-06-28): -[ -f "$HERMES/token_stats.json" ] && cp -a "$HERMES/token_stats.json" "$STAGE/hermes/" || true -[ -d "$HERMES/skills" ] && cp -a "$HERMES/skills" "$STAGE/hermes/skills" || true -[ -d "$HERMES/sessions" ] && cp -a "$HERMES/sessions" "$STAGE/hermes/sessions" || true -[ -d "$VOICE/refs" ] && cp -a "$VOICE/refs" "$STAGE/voice/refs" || true -``` -tar-Aufruf um den re-downloadbaren Skill-Cache erleichtern: -```bash -tar --exclude='./hermes/skills/.hub' -czf "$OUT" -C "$STAGE" . -``` -In `deploy/restore.sh` spiegelbildlich (skills/sessions **mergen**, nicht ersetzen) und -`voice-service` mit neustarten: -```bash -VOICE="${VOICE_HOME:-$HOME/.voice}" -SERVICES="mem0-service voice-service mission-control-2 hermes-gateway" -[ -f "$STAGE/hermes/token_stats.json" ] && cp -a "$STAGE/hermes/token_stats.json" "$HERMES/" -[ -d "$STAGE/hermes/skills" ] && { mkdir -p "$HERMES/skills"; cp -a "$STAGE/hermes/skills/." "$HERMES/skills/"; } -[ -d "$STAGE/hermes/sessions" ] && { mkdir -p "$HERMES/sessions"; cp -a "$STAGE/hermes/sessions/." "$HERMES/sessions/"; } -[ -d "$STAGE/voice/refs" ] && { mkdir -p "$VOICE/refs"; cp -a "$STAGE/voice/refs/." "$VOICE/refs/"; } -``` diff --git a/docs/RADAR.md b/docs/RADAR.md new file mode 100644 index 0000000..ce81af6 --- /dev/null +++ b/docs/RADAR.md @@ -0,0 +1,104 @@ +# Radar — Modell-Radar, Stack-Radar, Prüfstand + +_Stand 24.09.2026, Code in `main` = `75611be`. Für Techniker und Agenten._ + +Es gibt zwei Radare mit getrennten Aufgaben: + +- **Modell-Radar** (Box-Wart, `mc2-radar.timer` um 00:30): sucht und testet neue Modelle für Hirn und Coder selbst. + Getauscht wird nur per Knopf „Übernehmen". +- **Stack-Radar** (Hermes-Cron „KI und Stack Radar", Sa 08:00): Wochenbericht über den Zustand der Box und über + Neuigkeiten draußen (Releases, Entwurfsmodelle). Er testet und ändert nichts. + +## Modell-Radar + +Code: `backend/services/radar.py`, Nachtlauf `backend/radar_lauf.py`, Messungen `deploy/bench/pruefstand.py`, +Merkliste `deploy/radar-watchlist.json`. Zustand: `/srv/models/mc2-radar.json`, Baseline +`/srv/models/mc2-radar-baseline.json`, Downloads `/srv/models/radar//`. Protokoll: +`journalctl --user -u mc2-radar`. + +### Leitplanken (User-Entscheid 23.09.) + +- Zwei Rollen: Hirn (Alias `hermes`) und Coder (Alias `coder`), jede mit Bild-Zwilling (`vision` bzw. `coder-bild`). +- Tests nur nachts 00:30–02:30, höchstens ein neuer Kandidat pro Woche. Um 03:00 braucht der NerdQuiz-Nachtlauf das + Hirn. +- Nur was neben das Warm-Set passt: 27 GB Warm-Set plus Kandidat mit KV-Cache ≤ 115 GB, gerechnet mit dem Kontext + aus dem Betrieb (131 072; passt das nicht, stufenweise kleiner, nie unter 32 768). Ab 90 % des Budgets heißt es + „passt knapp". +- Pflicht: GGUF plus Bild-Projektor (mmproj), mindestens 20B Parameter; als Hirn höchstens 12B aktive Parameter + (dichte Modelle nie als Hirn). Die installierte Engine muss die Architektur kennen, und die Platte darf nach dem + Download nicht über 80 % liegen. +- Durchgefallene werden gelöscht, Bestandene bleiben liegen. Getauscht wird erst mit „Übernehmen". + +### Ablauf einer Nacht + +1. Um 00:30 startet `mc2-radar.service` (`Persistent=true`, holt einen verpassten Lauf nach). +2. **Suche**, höchstens einmal am Tag: zuerst die Merkliste in ihrer Reihenfolge, dann die Hugging-Face-Entdeckung + (höchstens 3 Funde je Rolle). Jeder Fund wird bewertet: Dateien, Größe, Engine-Unterstützung, Speicher. Der Knopf + „Jetzt suchen" macht dasselbe sofort (läuft synchron und kann Minuten dauern). +3. **Offene Urteile nachholen:** Kandidaten mit Status „getestet", bei denen die Vergleichsmessung fehlte. +4. **Test**, wenn ein Kandidat dran ist: + 1. Download nach `/srv/models/radar//`. + 2. Baseline des heutigen Modells der Rolle über llama-swap (gecacht, höchstens monatlich neu gemessen). + 3. In llama-swap bleibt nur das Warm-Set; ein Aufpasser entlädt Modelle, die nachts nachgeladen werden (etwa den + Coder). + 4. Draft-Varianten kurz messen: ohne Draft, eingebauter MTP-Kopf, Draft des heutigen Modells (nur bei gleicher + Architektur und genug Speicher). Ein Draft aus der Merkliste wird allein genommen. + 5. Mit der schnellsten Variante den Prüfstand fahren; der Kandidat läuft als eigener `llama-server` auf `:5899`. + 6. Bild-Probe über einen Zwilling ohne Draft, dann das Urteil. +5. **Frist:** Die Messungen prüfen das Fensterende 02:30 selbst. 5 Minuten danach zieht die Notbremse + (Kandidaten-Server und Download stoppen, der Prozess endet hart). `RuntimeMaxSec=2h10min` beendet die Unit + spätestens um 02:40 samt `llama-server`. +6. **Bestanden:** Meldung „[Modell-Radar] … hat den Nachttest bestanden"; nachts landet sie in der Morgenmeldung. + +Status eines Kandidaten: neu → wartet → getestet → bestanden oder durchgefallen → uebernommen oder verworfen. +Höchstens 3 Versuche je Kandidat; ein Abbruch, für den der Kandidat nichts kann, zählt nicht als Versuch. + +### Prüfstand (`deploy/bench/pruefstand.py`) + +- Der Kandidat läuft als eigener `llama-server` auf `127.0.0.1:5899`; der Live-Stack bleibt unberührt. +- **Hirn:** Tempo kurz und bei 13k Kontext (Prefill, Decode, Draft-Akzeptanz), Werkzeug-Aufrufe zwischen ~100 + Werkzeugen mit ~12k Kontext, Deutsch- und JSON-Probe. +- **Coder:** Tempo, Werkzeug-Aufrufe eines Coding-Agenten, 10 kleine Programmieraufgaben mit Unit-Tests in einem + Temp-Ordner mit Zeitlimit. +- **Bild:** ein im Code gezeichnetes Bild (Formen, Farben, Text), einmal direkt, einmal mitten in einer + Werkzeug-Aufgabe. +- **Urteil:** bestanden = keine Verschlechterung bei den Pflichtwerten UND mindestens ein echter Vorteil. +- **Handbetrieb:** `deploy/bench/pruefstand-hirn.py` misst Kandidaten mit festen Pfaden gegen die Live-Modelle, + optional mit einem Werkzeug-Test durch den echten Hermes (Provider `pruefstand` in der Hermes-Config). + +### Übernehmen und Verwerfen (Seite „Modelle") + +**Übernehmen** geht nur bei Status „bestanden" und nicht während eines Nachtlaufs: + +1. Der Ordner wandert von `/srv/models/radar/` nach `/srv/models/`. +2. Eintrag in llama-swap mit dem getesteten Draft und den Betriebs-Flags der Rolle, ohne Projektor. Dazu der + Bild-Zwilling `-Bild` in der Gruppe `bild` (ttl 900). Der alte Zwilling fliegt aus der Config, seine + Dateien bleiben. +3. **Hirn:** Alias `hermes`, Gruppe `brains`, ttl 0, `model.default` in der Hermes-Config; Hermes startet dafür kurz + neu. Der Alias `fast` wandert mit, `MC_WARMSET` im Steward-Drop-in wird umgestellt und der Steward neu gestartet. + **Coder:** Rolle `coder`; `heavy` wandert mit, die ttl des alten Coders wird übernommen. +4. Das alte Modell bleibt auf der Platte (Aufräumen-Panel). + +**Verwerfen:** Die heruntergeladenen Dateien werden gelöscht, und das Radar testet dieses Modell nie wieder. + +Ein Tausch schreibt die llama-swap-Config heute noch mehrmals (offener Punkt in +[wissen/OFFENE-FAEDEN.md](wissen/OFFENE-FAEDEN.md)). + +### Merkliste + +`deploy/radar-watchlist.json`, Felder je Eintrag: `name`, `rolle` (`hirn` oder `coder`), `repo`, `quant` (Standard +Q4_K_M), `eng` (passt nur knapp), `notiz`, `ueberspringen` (Grund, dann kein Test), optional `draft` und `flags`. +Merkliste und Hugging-Face-Entdeckung zusammen ergeben die Warteschlange; Merkliste zuerst. + +## Stack-Radar (Hermes-Cron „KI und Stack Radar", Sa 08:00) + +- `deploy/jobs/stack-radar.sh` sammelt Fakten und meldet selbst über `notify.sh` mit Betreff „[Stack-Radar]". +- `deploy/jobs/stack-ist.sh` prüft Ergebnisse statt Lebenszeichen: Lösen die Rollen-Aliase auf? Lief der letzte + Timer gut? Ist die Sicherung jünger als zwei Tage? +- `deploy/jobs/stack-upstream.py` schaut nach draußen: Releases, gemergte PRs, neue Entwurfsmodelle und Modelle der + Familien, die die Box fährt; verglichen mit dem letzten Lauf (`~/.hermes/state/stack-upstream.json`). Liste: + `deploy/trend-radar-watchlist.json`. +- Das Hirn (`fast`) schreibt aus den Fakten einen kurzen Bericht; es darf nichts dazuerfinden. Antwortet es nicht, + gehen die roten Rohbefunde trotzdem raus. +- Grundsatz: „Fakten sammelt ein Skript, Prosa schreibt das Modell." Mehr in + [deploy/jobs/README.md](../deploy/jobs/README.md). diff --git a/docs/RUNBOOK.md b/docs/RUNBOOK.md deleted file mode 100644 index 25c5a99..0000000 --- a/docs/RUNBOOK.md +++ /dev/null @@ -1,85 +0,0 @@ -# RUNBOOK — Die Box in 1 Seite (für Menschen, ohne KI-Hilfe) - -**Grundsatz:** Die Box wartet sich selbst. Du bekommst Telegram-Nachrichten und antwortest -höchstens „mach". Dieses Blatt ist NUR für den Fall, dass etwas klemmt. - -## Was die Telegram-Meldungen bedeuten - -| Meldung | Bedeutung | Dein Handgriff | -|---|---|---| -| „… aktualisiert … grün" | Update eingespielt, alles geprüft | keiner | -| „… zurückgerollt … GEPINNT" | Update war schlecht, alte Version läuft wieder | keiner (läuft stabil weiter) | -| „KRITISCH: …" | Update UND Rollback kaputt | siehe „Box tot?" unten | -| „🛰️ Evolution-Radar …" | monatlicher Chancen-Report | lesen; bei Interesse „mach" antworten | -| „[Werkstatt] … Vorschlag liegt bereit" | Box hat einen Fix vorbereitet | Branch auf Gitea ansehen → Ampel grün? → selbst auf main mergen → `deploy.sh` | - -## Box tot / Weboberfläche weg? - -1. **Strom/Netz prüfen**, dann Box **einmal neu starten** (Power-Knopf). Alles startet von selbst - (systemd, reboot-fest). 2–3 Minuten warten, dann `http://192.168.178.151:9001` aufrufen. -2. Immer noch tot → per SSH (PC, PowerShell): `ssh hitonabi@192.168.178.151` - dann: `bash ~/mission-control-v2/deploy/restore.sh` (nimmt automatisch das letzte Backup, - liegt in `/srv/models/mc2-backups/`, 14 Tage Vorrat, täglich 03:30 Uhr). -3. Totalschaden (neue Platte/Hardware) → `docs/DISASTER_RECOVERY.md` (Bootstrap von Null). - -## Lucy (am PC) - -- Start: Desktop-Verknüpfung **„Lucy"** (startet den eingefrorenen Produktiv-Build). -- Hängt? `F:\Coding Stuff\lucy\lucy-desktop\Lucy-Neustart.bat` doppelklicken. -- Lucy ist EINGEFROREN — Änderungen macht nur die Werkstatt (Telegram-Vorschlag abwarten). - -## Automatik-Fahrplan (läuft ohne dich) - -- **Nachts:** 03:15 Traum (Wissens-Vault) · 03:30 Backup · 03:50 PBS-Backup · - 04:30 Chef-Gutachter (Morgenlage) · 07:15 Selbsttest · 08:00 Daily-Briefing -- **So 04:30** Auto-Update (Router→Engine→Hermes) mit Rollback+Pin-Fangnetz (dein Ok 10.07.; - manuell geht's jederzeit über den Wartungs-Drawer) -- **Mo 05:15** Release-Radar (Hermes-Neuerungen) · **Monatlich 1., 09:00** Monats-Review - (Evolution-Radar + Selbstkritik) auf Telegram -- **Sa 05:15** Trend-Radar (misst alle Modelle, vergleicht neue Engine-Builds, sucht - bessere Modell-Kandidaten; beobachtet auch Electron/pocket-tts für den PC) · - **So 01:00** Prüfstand (testet gefundene Kandidaten nachts mit echten Aufgaben durch — - das Ergebnis kommt als Karte, DU entscheidest über Wechsel) -- **Monatlich 1., 06:40** Restore-Probe (stellt eine Datei WIRKLICH aus PBS + Tarball - wieder her — meldet laut, falls ein Backup nicht zurückkommt) · **Monatlich 2., 06:40** - Venv-Audit (prüft die Python-Nebendienste auf bekannte Sicherheitslücken → Karte) - -## Pinnwand: eine Ebene ist „GEPINNT" — was heißt das? - -Ein Update hat den Selbsttest gerissen; die Box bleibt bewusst auf der alten Version. Das ist -ein STABILER Dauerzustand, kein Fehler. Pin ansehen / lösen (per SSH): - - cat /srv/models/mc2-pins.json - jq 'del(.hermes)' /srv/models/mc2-pins.json > /tmp/p && mv /tmp/p /srv/models/mc2-pins.json - # (statt .hermes: .engine oder .swap) — nächster So-Lauf versucht das Update erneut - -## Wann Gemini rufen (der Notfall- und Review-Partner) - -Seit dem Claude-Abo-Ende ist **Gemini (in der Antigravity-App am PC)** dein externer Helfer. -Du brauchst ihn NUR in zwei Fällen — den Alltag macht die Box selbst: - -1. **Notfall:** Die Box ist kaputt UND die Schritte unter „Box tot?" (oben) haben nicht - geholfen — oder Lucy/Telegram melden wiederholt „KRITISCH". -2. **Außen-Review:** Du willst einen Fremd-Blick auf die Arbeit der Box (z. B. alle paar - Monate oder vor einem großen Umbau). - -**So rufst du ihn:** Antigravity öffnen → Projektordner `F:\Coding Stuff\mission-control-2` -→ als erste Nachricht schreiben: **„Lies docs/GEMINI_BRIEFING.md und dann [dein Problem]."** -Für ein Review stattdessen: „Lies docs/GEMINI_BRIEFING.md und führe das Review aus -docs/ANTIGRAVITY_REVIEW.md durch." - -**Grenzen (stehen auch in seinem Briefing):** Gemini schlägt vor, DU klickst/entscheidest. -Er darf Security-Sachen nur melden, nie ändern. Wenn er etwas über die Box behauptet, -darf er es nur nach echtem Test behaupten — im Zweifel nachfragen „hast du das gemessen?". - -## Einmalige sudo-Session — ✅ erledigt (02.07.2026) - -Sudo-Freischaltung für Engine/Router-Updates, unattended-upgrades und v1-Aufräumen sind -durch (`/etc/sudoers.d/mc2-autonomie` liegt). Nichts mehr zu tun. - -## Nützliche Handgriffe (SSH) - - curl -s http://127.0.0.1:9001/api/health # Gesamtzustand (brain ready?) - bash ~/mission-control-v2/deploy/autoupdate.sh # Update-Lauf sofort statt Sonntag - bash ~/mission-control-v2/deploy/notify.sh "test" # Meldeweg testen (muss auf Telegram ankommen) - tail ~/mc2-notify.log # was wurde zuletzt gemeldet diff --git a/docs/UPDATES.md b/docs/UPDATES.md new file mode 100644 index 0000000..401d669 --- /dev/null +++ b/docs/UPDATES.md @@ -0,0 +1,109 @@ +# Updates — die Sonntags-Kette, Festhalten, Freigeben + +_Stand 24.09.2026, Code in `main` = `75611be`. Für Techniker und Agenten; für den User: Seite „Updates" in +[BEDIENUNG.md](BEDIENUNG.md)._ + +Die Box aktualisiert ihre Fremd-Software jeden Sonntag selbst: erst llama-swap, dann den Motor (llama.cpp), dann +Hermes. Jeder Baustein wird danach geprüft; ist die Prüfung rot, rollt die Box zurück und hält den Baustein fest, bis +ihn jemand freigibt. Sicherheitsupdates des Betriebssystems laufen getrennt über unattended-upgrades. Modelle wechseln +nie automatisch (Modell-Radar, Knopf „Übernehmen"). + +## Ablauf + +```mermaid +flowchart TD + T["mc2-autoupdate.timer
So 04:30 (+≤10 min)"] --> S["jobs/sonntags-update.sh"] + S --> A["autoupdate.sh"] + A --> R["1 · llama-swap
update-swap.sh"] + R --> E["2 · Motor
update-engine.sh"] + E --> H["3 · Hermes
Job über MC2"] + H --> W["Wochenbericht
[Box-Update]"] + W --> N{"Neustart nötig?
nur So 04–07 Uhr"} +``` + +1. `mc2-autoupdate.timer` (`Persistent=true`, bis 10 min Zufallsverzug) startet `mc2-autoupdate.service` + (einmaliger Lauf nach `mc2-backup.service`, Zeitlimit 3 h). Die Unit ruft `deploy/jobs/sonntags-update.sh`, das + `deploy/autoupdate.sh` startet und nur dann selbst meldet, wenn `autoupdate.sh` mit Fehler endet (letzte 15 + Zeilen). Der Hermes-Cron „Updates am Sonntag" ist seit 24.09. pausiert. +2. `autoupdate.sh` bricht mit Meldung ab, wenn MC2 (`/api/health`) nicht antwortet. +3. **llama-swap und Motor:** Ein festgehaltener Baustein wird übersprungen. Sonst liefert + `GET /api/maintenance/update-details?kind=swap` bzw. `kind=engine` die installierte und die neueste Version. Gibt es + Neues, läuft `sudo -n bash deploy/update-swap.sh` bzw. `update-engine.sh` (fehlt die sudo-Freigabe, wartet der + Baustein mit Hinweis). Die Skripte sichern die alte Version (`/usr/local/bin/llama-swap.bak`, + `/opt/llamacpp-vulkan.bak`), stoppen llama-swap, tauschen, starten und prüfen mit `stack-postcheck.sh`. + Ergebnis 0 = eingespielt, 1 = zurückgerollt (Baustein wird festgehalten, Meldung), 2 = Update und Rückweg + gescheitert (Meldung „KRITISCH", dringend). +4. **Hermes:** Ein festgehaltener Hermes wird übersprungen. Sonst sagt `update-details?kind=hermes`, wie viele + Commits Hermes zurückliegt. `autoupdate.sh` startet `POST /api/maintenance/hermes-update` und wartet bis zu + 20 Minuten. Der Job: Sicherung → Dashboard stoppen → `hermes update --yes --no-gateway-restart` → `hermes doctor` → + Dashboard-Oberfläche mit Basis `/hermes-ui/` bauen → `hermes-gateway` und `hermes-builtin-ui` neu starten → + `hermes-postcheck.sh`. + - Grün: Meldung. + - Rot oder Zeitlimit: erst `self-repair.sh` (kommentiert eindeutige, nicht sicherheitsrelevante Config-Schlüssel + aus, an denen die neue Version scheitert; der Gehirn-Check muss danach grün sein). + - Hilft das nicht: Rückweg — `git reset --hard` auf den alten Commit in `~/.hermes/hermes-agent`, `config.yaml` und + `.env` aus der letzten Sicherung, Neustart, Gehirn-Check. Grün: Hermes wird festgehalten, Meldung. Sonst + „KRITISCH" (dringend). +5. **Wochenbericht** „Commander, die Wochenpflege der Box ist durch" mit einer Zeile je Baustein. +6. **Neustart**, wenn Ubuntu ihn verlangt (`/var/run/reboot-required`), nur sonntags 04:00–06:59; außerhalb gibt es + nur eine Ankündigung. Vorher prüft das Skript: Linger an, keine laufenden Jobs, `mission-control-2`, + `mc2-gateway` und `mc2-steward` im Autostart; sonst Meldung statt Neustart. `MC_AUTOUPDATE_REBOOT=0` schaltet den + Neustart ab, `MC_AUTOUPDATE_REBOOT_JETZT=1` erzwingt ihn außerhalb des Fensters. + +Nachts landen alle Meldungen außer „KRITISCH" in der Morgenmeldung um 07:00. + +## Prüfungen nach dem Update + +- **`stack-postcheck.sh`** (nach llama-swap, Motor und Betriebssystem): llama-swap aktiv; `/v1/models` antwortet + (bis 120 s); das Hirn liefert eine echte Antwort (1 Token); das Embedding-Modell liefert Vektoren; MC2 meldet den + Motor erreichbar; Gateway gesund; Steward aktiv. +- **`hermes-postcheck.sh`** (nach Hermes): keine Config-Warnungen im Gateway-Log der letzten 3 Minuten; Werkzeug-Test + durch den echten Agenten (`echo postcheck-ok`, bis 3 Versuche); Sprach-Test über `/api/voice/chat` (bis 3 + Versuche); Browser-Werkzeug `agent-browser` lauffähig (einen toten Link setzt das Skript selbst neu). + +## Festhalten und Freigeben + +- Rot mit grünem Rückweg ergibt einen Eintrag in `/srv/models/mc2-pins.json`: + `{"": {"pinned": true, "version": …, "grund": …, "datum": …}}` mit den Bausteinen `swap`, `engine`, + `hermes`. Künftige Sonntage überspringen ihn. +- Der Wächter zeigt jeden festgehaltenen Baustein als gelben Hinweis „… bekommt keine Updates mehr" mit Knopf + „Freigeben"; die Updates-Seite zeigt ihn ebenso. Freigeben löscht den Eintrag + (`POST /api/updates/festgehalten/{baustein}/freigeben`); der nächste Sonntag versucht das Update erneut. +- Warum sichtbar: Vom 06. bis 17.09. hielt die Box Motor und Hermes fest, ohne dass es jemand merkte, elf Tage ohne + Updates. + +## Von Hand + +- **Seite „Updates":** „Nach Neuem suchen" (`apt-get update`), je Baustein „Aktualisieren", „Alles jetzt + aktualisieren" (mit Rückfrage). Es läuft immer nur ein Wartungs-Job; ein zweiter Klick startet kein zweites Update. +- **„Alles jetzt aktualisieren"** kettet in einem Job, was ansteht: Motor → llama-swap → Hermes → Betriebssystem + (`apt-get upgrade`, danach `stack-postcheck.sh`). Scheitert ein Teil, stoppt die Kette. +- **Zeitlimits der Jobs:** Suche 15 min, Betriebssystem 90 min, Motor 60 min, llama-swap 30 min, Hermes 45 min, alles + zusammen 3 h. „Abbrechen" beendet die ganze Prozessgruppe. +- **Per SSH:** `bash ~/mission-control-v2/deploy/autoupdate.sh` (neu gestartet wird trotzdem nur im Wartungsfenster). +- Kann eine Update-Prüfung nicht prüfen (Netz, API, apt), zeigt die Oberfläche „Prüfung unklar" statt „aktuell". + Nach jedem Update prüft sie sofort neu. +- Update-Jobs leben im MC2-Prozess; ein Neustart von MC2 bricht sie ab. Der Deploy wartet deshalb. + +## Update-Verlauf + +`backend/services/update_verlauf.py` baut den Verlauf der Updates-Seite aus `~/mc2-notify.log`. Es liest die Zeilen +`OK telegram`, `OK telegram direkt` (Zweitweg über die Bot-API), `QUEUED für Morgen-Digest` und `FALLBACK (…)`. +Meldungen mit weniger als 45 Minuten Abstand gehören zu einem Lauf; die Morgenmeldung zählt nicht als eigener Lauf. +Der Wortlaut der Meldungen von `autoupdate.sh` und der Protokolleinträge muss deshalb bleiben: Eine Umformulierung +bricht den Verlauf ohne Fehlermeldung. + +## Betriebssystem + +Sicherheitsupdates spielt `unattended-upgrades` ein. Wartet danach ein Kernel oder libc auf den Neustart, startet der +Sonntagslauf die Box im Wartungsfenster neu. Weitere Pakete: Knopf „Aktualisieren" beim Baustein Betriebssystem. + +## Fallen + +- llama.cpp hat `--no-mmap` gestrichen (seit b10936); die Config nutzt `--load-mode none`. Vor einem Engine-Sprung + die Config-Zeilen mit dem neuen `llama-server` prüfen. +- `hermes update` meldet Exit 1 trotz Erfolg, deshalb `--no-gateway-restart`. +- Ein Timer mit `Persistent=true` holt verpasste Läufe beim Einschalten sofort nach (24.09.: Neustart um 14:51). +- Der Updater läuft nicht mehr als Hermes-Cron (20.09.: Hermes startete sich beim eigenen Update neu). + +Einzelheiten: [wissen/FALLEN.md](wissen/FALLEN.md). diff --git a/docs/WIEDERAUFBAU.md b/docs/WIEDERAUFBAU.md new file mode 100644 index 0000000..8d68cd8 --- /dev/null +++ b/docs/WIEDERAUFBAU.md @@ -0,0 +1,108 @@ +# Wiederaufbau — die KI-Box von null + +_Stand 24.09.2026. Für den Totalschaden (neue Platte, neue Hardware). Für kleinere Störungen gilt +[BETRIEB.md](BETRIEB.md), Abschnitt „Notfall"._ + +Die Reihenfolge stammt aus dem Konzept vom 28.06.2026, aktualisiert auf den Stand vom 24.09.; auf einer frischen Box +geprobt wurde sie nie. Das dort geplante Bootstrap-Skript mit Einrichtungs-Assistent wurde nie gebaut; der Volltext +steht in der Git-Historie (`docs/DISASTER_RECOVERY.md`, letzter Stand 28.08.2026). + +## Was woher zurückkommt + +| Teil | Quelle | +|---|---| +| Code des Box-Warts | Gitea, `main` | +| Hermes: `config.yaml`, `.env`, Plugins, `cron/`, `state/`, Gedächtnis (`memories/`), `SOUL.md`, Skills, Cron-Skripte | Sicherung (Tarball) | +| llama-swap-Config, Box-Wart-Zustand (`/srv/models/mc2-*.json`) | Sicherung | +| Units und Drop-ins, auch die nicht im Repo liegenden | Sicherung, Ordner `systemd/` (zum Nachschlagen), dazu `deploy/` im Repo | +| Lucys Stimmreferenz | Sicherung, `lucy-stimme/ref.wav` | +| Versionen, die zusammen liefen | Sicherung, `known-good/` | +| Modelle | neu laden; die Pfade stehen in der llama-swap-Config | +| Engine, llama-swap, Hermes, Python-Umgebungen | neu installieren | + +## Schritte + +1. **Sicherung besorgen.** Die Tarballs liegen auch auf dem Proxmox-Host unter `/var/lib/vz/mc2-backups`. Der + Spiegel-Schlüssel `~/.ssh/mc2_offsite` steckt nicht in der Sicherung: Den neuesten Tarball von Hand holen (z. B. + vom PC per `scp`) oder den Schlüssel neu einrichten (Security-Config, nur mit User-Ja). +2. **Grundsystem:** Ubuntu 26.04, Benutzer `hitonabi`, Zeitzone `Europe/Berlin`, + `sudo loginctl enable-linger hitonabi` (ohne Linger starten die User-Dienste nach einem Neustart nicht). Pakete: + Python 3.14 mit venv (Bezugsquelle offen, prüfen), git, curl, jq, rsync, ffmpeg (Sprachnachricht der Daily News), + ttyd (Konsole). Die Vulkan-Treiber installiert Schritt 5. +3. **Ordner:** `/srv/models` und `/etc/llama-swap` anlegen und `hitonabi` geben + (`sudo chown -R hitonabi:hitonabi /srv/models /etc/llama-swap`); MC2 schreibt die llama-swap-Config selbst. +4. **Code und Python-Umgebung:** + `git clone ssh://gitea@192.168.178.153:2222/Hitonabi/mission-control-v2.git ~/mission-control-v2` (der + SSH-Schlüssel der Box muss in Gitea hinterlegt sein), dann `python3.14 -m venv backend/.venv` und + `backend/.venv/bin/pip install -r backend/requirements.txt -r backend/requirements-dev.txt`. Was nachweislich + zusammen lief: `known-good/pip-backend.txt` aus der Sicherung. +5. **Engine und llama-swap** (root): `sudo bash deploy/update-swap.sh` (Binary nach `/usr/local/bin/llama-swap`), + Unit aus Anhang A nach `/etc/systemd/system/llama-swap.service`, `sudo bash deploy/provision-engine.sh` + (Vulkan-Treiber, llama.cpp-Build nach `/opt/llamacpp-vulkan`, Drop-ins `vulkan.conf` und `warmup.conf`, + `/usr/local/bin/llama-swap-warmup.sh`), dann `sudo systemctl enable --now llama-swap`. Das dritte Drop-in + `warmset.conf` liegt nur in der Sicherung (`systemd/llama-swap.service.d/`). +6. **Hermes** neu installieren (Git-Install nach `~/.hermes/hermes-agent`; welcher Stand lief, steht in + `known-good/versions.txt`). Den Dienst `hermes-gateway` legt Hermes selbst an; `hermes-builtin-ui.service` samt + Drop-ins aus `systemd/user/` der Sicherung übernehmen. +7. **Zustand zurückspielen:** Tarball nach `/srv/models/mc2-backups/` legen, dann + `bash deploy/restore.sh --yes `. Auf einer leeren Box kommen auch Gedächtnis, `SOUL.md`, Skills, + Cron-Skripte und `mc2-*.json` zurück, weil sie fehlen. +8. **Lucys Stimme:** `~/.lucy-stimme/` (pocket-tts mit `pocket_server.py`) neu einrichten; das Programm stammt aus dem + Lucy-Repo, die Referenz `ref.wav` aus der Sicherung. Offen: Die Einrichtungsschritte sind nirgends beschrieben. +9. **Deploy:** `bash deploy/deploy.sh` spielt alle Repo-Units, Drop-ins, Cron-Skripte, Skills und Plugins aus, + aktiviert Dienste und Timer und prüft nach. Das Plugin `mc2-web-lesen` ist nach dem Restore schon in der + Hermes-Config aktiv; sonst einmalig `hermes plugins enable mc2-web-lesen` und + `hermes config set web.extract_backend mc2-lesen`. +10. **Modelle laden:** Die Pfade stehen in der zurückgespielten llama-swap-Config (`-m`, `--mmproj`, + `--spec-draft-model`); `known-good/models.txt` listet die Dateien mit Größe. Zuerst das Hirn, dann den Coder, dann + die Drafts. Die Bild-Zwillinge nutzen dieselben Gewichte, brauchen also nur ihren Projektor. Laden über die + Oberfläche („Modelle selbst suchen") oder mit + `backend/.venv/bin/hf download --local-dir /srv/models/`. +11. **Prüfen:** `bash deploy/stack-postcheck.sh`, `bash deploy/hermes-postcheck.sh`, dann + `http://192.168.178.151:9001` öffnen: Alle Warnlampen sollen grün sein. +12. **Spracherkennung** (schläft, nur bei Bedarf): `bash voice_service/install.sh` legt `~/.voice/venv` an; aktivieren + mit `systemctl --user enable --now voice-service`. + +## Anhang A — `llama-swap.service` + +Von der Box abgegriffen am 28.06.2026, seitdem nicht neu verglichen. Benutzer und `HSA_OVERRIDE_GFX_VERSION` sind +boxspezifisch. Nach `/etc/systemd/system/llama-swap.service` (root): + +```ini +[Unit] +Description=llama-swap (lokaler LLM Router) +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=hitonabi +Environment=HSA_OVERRIDE_GFX_VERSION=11.5.1 +Environment=PATH=/usr/local/bin:/usr/bin:/bin +ExecStart=/usr/local/bin/llama-swap --config /etc/llama-swap/config.yaml --listen 0.0.0.0:8080 --watch-config +Restart=on-failure +RestartSec=3 + +[Install] +WantedBy=multi-user.target +``` + +Drop-ins, die `deploy/provision-engine.sh` anlegt: + +```ini +# /etc/systemd/system/llama-swap.service.d/vulkan.conf +[Service] +Environment=LD_LIBRARY_PATH=/opt/llamacpp-vulkan +``` + +```ini +# /etc/systemd/system/llama-swap.service.d/warmup.conf +[Service] +ExecStartPost=-/usr/local/bin/llama-swap-warmup.sh +``` + +Das Drop-in `warmset.conf` (laut Prüfbericht vom 24.09. `MC_WARMUP_MODELS=fast`) steht in keinem Skript; es kommt +aus der Sicherung. + +**Erstinstallation in dieser Reihenfolge:** `sudo bash deploy/update-swap.sh` (Binary) → Unit anlegen → +`sudo bash deploy/provision-engine.sh` (Engine und Drop-ins) → `sudo systemctl enable --now llama-swap`.