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 <noreply@anthropic.com>
This commit is contained in:
Hitonabi
2026-09-24 17:37:09 +02:00
co-authored by Claude Opus 5.5
parent 24e6dd81ba
commit f4bbdad311
8 changed files with 732 additions and 472 deletions
+177
View File
@@ -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<br/>Oberfläche, /api"]
GW["mc2-gateway :9010<br/>/v1, Bild-Weiche"]
ST["mc2-steward<br/>Wächter, Re-Warm"]
RD["mc2-radar<br/>00:30"]
HE["Hermes :8642<br/>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/<pfad>`** reicht an
`<partner>/api/<pfad>` 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 „<Name> 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-<Zeit>`; 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/.<app>`. 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).
-62
View File
@@ -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-<zeitstempel>.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="<Box-IP>"`, 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.
+234
View File
@@ -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`) | „<Name> 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 <unit>` (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 <unit>`.
- Dauerhaft wecken: `systemctl --user enable --now <unit>`; 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-<Zeit>` (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/<name_mit_unterstrich>/`, 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 <commit> 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-<Zeit>.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 <Datei>` als eigene
systemd-Unit (`mc2-restore-<Zeit>`), 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 <commit>` 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 |
-325
View File
@@ -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/"; }
```
+104
View File
@@ -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/<id>/`. 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/<id>/`.
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/<Repo-Name>`.
2. Eintrag in llama-swap mit dem getesteten Draft und den Betriebs-Flags der Rolle, ohne Projektor. Dazu der
Bild-Zwilling `<Eintrag>-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).
-85
View File
@@ -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
+109
View File
@@ -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<br/>So 04:30 (+≤10 min)"] --> S["jobs/sonntags-update.sh"]
S --> A["autoupdate.sh"]
A --> R["1 · llama-swap<br/>update-swap.sh"]
R --> E["2 · Motor<br/>update-engine.sh"]
E --> H["3 · Hermes<br/>Job über MC2"]
H --> W["Wochenbericht<br/>[Box-Update]"]
W --> N{"Neustart nötig?<br/>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`:
`{"<baustein>": {"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).
+108
View File
@@ -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 <datei>`. 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 <repo> <datei> --local-dir /srv/models/<Ordner>`.
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`.