Files
mission-control-v2/docs/BETRIEB.md
T

263 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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`). 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-<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". 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 <ip>` — 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 <ip>` — Ausführer, Unit und Konfiguration (0600) auf den Host.
5. `bash deploy/homelab/box-partner.sh <ip>` — 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://<ip>: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-<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 |