# 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`). Läuft ein Update-Auftrag (Gruppe `maintenance` in `/api/jobs`), bricht der Deploy ab — das Update führt Skripte aus diesem Checkout aus. Downloads laufen weiter: Aufträge sind eigene systemd-Einheiten und überleben den Neustart von MC2. 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". Danach (Schritt 8) zieht der Deploy den Homelab-Teil mit, sobald `box-partner.sh` einen Partner eingerichtet hat: `deploy/homelab/ausrollen.sh` mit der IP aus `partner.conf`. Scheitert das, bleibt die Box live und es kommt „[Alarm] Deploy"; `MC_DEPLOY_OHNE_HOMELAB=1` lässt den Schritt aus. 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 laufendem Update-Auftrag), `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. **Probelauf auf der Box** `deploy/probelauf-box.sh` (am PC, vor dem Merge nach `main`): schickt den lokalen HEAD als Abzug nach `/tmp` auf der Box und lässt dort das Prüftor mit dem Python des Box-Checkouts laufen. Ändert auf der Box sonst nichts. Grund: Unter Linux zeigen sich Wettläufe, die am Windows-PC durchgehen (24.09.). ## Homelab-Teil einrichten (Phase 3/4) Jeder Schritt braucht das OK des Users (Container anlegen, Ausführer als root auf dem Host, Telegram-Zugang in den Container). Alles läuft am PC in Git-Bash, im Repo; SSH-Zugang zu `pve` (Schlüssel `id_lucy_infra`). 1. `bash deploy/homelab/container-anlegen.sh` — unprivilegierter Debian-13-Container (nächste freie ID, 1 Kern, 1 GB RAM, 4 GB, DHCP, Autostart, Etikett `mc2`), SSH-Schlüssel wie bei allen Gästen. Gibt die IP aus. 2. `bash deploy/homelab/ausrollen.sh ` — Code von HEAD in `/opt/mc2`, `einrichten.sh` (Nutzer `mc2`, Python- Umgebung, Units `mc2-homelab`, `mc2-homelab-steward`, `mc2-homelab-morgenmeldung.timer`). Auch jedes spätere Update des Homelab-Teils geht so (erst Prüftor und Probelauf auf der Box). 3. Telegram-Zugang (nur mit OK): `TELEGRAM_BOT_TOKEN` und `TELEGRAM_HOME_CHANNEL` in `/etc/mc2/telegram.env` (Eigentümer `root:mc2`, 0640). 4. `bash deploy/homelab/ausfuehrer-einrichten.sh ` — Ausführer, Unit und Konfiguration (0600) auf den Host. 5. `bash deploy/homelab/box-partner.sh ` — Drop-ins `partner.conf` für `mission-control-2` und `mc2-steward` auf der Box; ab dann prüfen sich beide Instanzen gegenseitig. Prüfen: `curl http://:9001/api/homelab/ziele` (nach etwa einer Minute stehen die Geräte darin), `systemctl status mc2-ausfuehrer` auf dem Host, Seite „Homelab“ der Oberfläche. ## 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 |