Rebuild profile router as secure recoverable v2

This commit is contained in:
Mikei386
2026-08-20 13:41:00 +02:00
parent bdd6c08643
commit fd4a3055e8
18 changed files with 1162 additions and 129 deletions
+27 -1
View File
@@ -11,7 +11,7 @@ in diesem Repository oder im Modellmanifest beschrieben sind.
| Komponente | Port | Ausführung | Aufgabe |
|---|---:|---|---|
| AI Profile Router | 8081 | systemd, unprivilegiert empfohlen | zentrale Client-API und Orchestrierung |
| AI Profile Router | 8081 | systemd, gehärtet | zentrale Client-API und Orchestrierung |
| llama.cpp | 8080 | systemd | Textmodell, Tool Calling und MCP |
| Whisper | 8084, nur localhost | systemd | Speech-to-Text |
| XTTS | 8085, nur localhost | systemd | Text-to-Speech |
@@ -29,11 +29,20 @@ in diesem Repository oder im Modellmanifest beschrieben sind.
4. Der Router wartet auf Modellname und erwartete Kontextgröße.
5. Erst dann wird die Anfrage an Port 8080 weitergeleitet.
Profilwahl und Weiterleitung bilden dabei eine atomare Modell-Lease. Ein
zweiter Request kann das Profil nicht mehr zwischen Auswahl und Inferenz
wechseln. Laufende Requests werden vor einem GPU-Hotswap vollständig beendet;
bei Überschreiten des Drain-Timeouts wird der Wechsel abgebrochen, nicht der
Chat.
## Profilprinzip
Die Profile sind vollständige systemd-Overrides. Ein Profilwechsel kopiert die
gewählte Datei atomar auf `override.conf`, lädt systemd neu und startet genau
einen llama.cpp-Dienst neu. Es gibt niemals mehrere Textmodelle gleichzeitig.
Das unabhängige Register `/etc/mike-ai/router-profiles.json` definiert Kontext
und erwarteten Modellalias. Readiness gilt nur, wenn beides exakt passt; eine
abweichende oder fehlende Registry verhindert den Start.
## GPU-Hotswap
@@ -47,6 +56,23 @@ Vision und Bildgenerierung teilen sich die RTX mit dem Textmodell. Der Router:
6. stellt das ursprüngliche Textprofil wieder her,
7. prüft Modell und Kontext vor der Freigabe.
Der zuletzt stabile Zustand und temporäre Worker-PIDs werden atomar unter
`/var/lib/mike-ai-profile-router/state.json` festgehalten. Beim Routerstart
werden ausschließlich dort erfasste Prozesse nach zusätzlicher
Kommandozeilenprüfung beendet und das letzte Profil wiederhergestellt.
## Vertrauensgrenzen
- Port 8081 verlangt einen eigenen API-Key; nur `/health` und `/ready` sind
absichtlich anonym und enthalten keine privaten Daten.
- Der Client-Key wird vor dem lokalen Upstream entfernt.
- Bild-Uploads sind begrenzt. Remote-Bild-URLs sind standardmäßig aus, damit
der Vision-Pfad nicht als Zugriff auf Intranet oder Metadatenendpunkte dient.
- Maximal 16 Requests werden gleichzeitig bearbeitet; weitere erhalten 429.
- Der Dienst benötigt derzeit wegen systemd-Profilwechsel und GPU-Hotswap noch
Root-Rechte. Die systemd-Sandbox begrenzt diese, ersetzt aber keine künftige
Aufteilung in unprivilegierten Proxy und eng begrenzten Root-Helper.
## Verzeichnislayout auf dem Zielhost
```text
+7
View File
@@ -31,6 +31,10 @@ laufen, sondern alle fachlichen Funktionen geprüft wurden.
## Phase C – Router
- [ ] Start ohne API-Key schlägt bewusst fehl
- [ ] `/health` bleibt bei Hotswap 200 und `/ready` wird vorübergehend 503
- [ ] geschützte Endpunkte liefern ohne Key 401
- [ ] Router-Key erscheint weder im Upstream noch im Journal
- [ ] `/status` meldet den richtigen Upstream
- [ ] `/v1/models` liefert drei virtuelle Modelle
- [ ] `/fast`, `/medium` und `/long` wechseln zuverlässig
@@ -40,6 +44,8 @@ laufen, sondern alle fachlichen Funktionen geprüft wurden.
- [ ] Tool Calls funktionieren
- [ ] Fehler sind OpenAI-kompatibel
- [ ] ein abgebrochener Client hinterlässt keinen blockierten Job
- [ ] erzwungener Routerabbruch wird aus Zustandsdatei sauber rekonstruiert
- [ ] fehlende/falsche Profilregistry verhindert falsche Readiness
## Phase D – Web und MCP
@@ -58,6 +64,7 @@ laufen, sondern alle fachlichen Funktionen geprüft wurden.
- [ ] neues Bild löst genau einen Vision-Hotswap aus
- [ ] Folgefrage verwendet Cache und keinen zweiten Hotswap
- [ ] Bilddaten werden vor dem Textmodell sanitisiert
- [ ] Remote-/private Bild-URL wird abgewiesen und übergroße Data-URL blockiert
- [ ] Qwen-Profil wird nach Vision wiederhergestellt
- [ ] FLUX erzeugt Standard- und High-Bild
- [ ] Qwen-Profil wird nach FLUX wiederhergestellt
+11
View File
@@ -65,6 +65,17 @@ Das bestehende `deploy/install.sh` installiert Router, Vision/Bild-Worker,
Whisper und XTTS. Vor produktiver Verwendung müssen Modellpfade in der
systemd-Datei gegen das lokale Manifest geprüft werden.
Der Installer erzeugt `/etc/mike-ai/router-api-key` (0600), installiert das
Profilregister und legt die atomare Zustandsablage an. Danach wird der Key
einmal manuell in die Secret-Stores der erlaubten Clients übernommen. Er darf
nicht im Terminal-Log, in Screenshots oder in Git dokumentiert werden.
Der Router läuft aktuell als gehärteter Root-Dienst, weil er den
llama.cpp-Systemdienst und temporäre GPU-Worker kontrolliert. Das ist eine
bewusste Restabweichung. Ein späterer V3-Schritt soll den HTTP-Proxy als eigenen
Benutzer ausführen und nur Profil-/Hotswap-Befehle an einen fest
parametrisierten Root-Helper delegieren.
## 8. Websuche
TinySearch und SearXNG bleiben als einziges Docker-Teilsystem isoliert. Die
+24 -1
View File
@@ -20,12 +20,19 @@ Clients verbinden sich mit:
http://HOST:8081/v1
```
Als API-Key verwenden sie den Inhalt von `/etc/mike-ai/router-api-key` über
Bearer-Authentifizierung. Der Key gehört in den Secret-Store des Clients,
nicht in Chat, Repository oder URL. Eine Rotation erfolgt atomar durch
Ersetzen der Datei und Neustart des Routerdienstes.
Sie sollen nicht direkt Port 8080 verwenden, weil sie sonst Profilumschaltung,
Vision, Bildgenerierung, STT und TTS umgehen.
## Status
- `GET /status`: Router, Profil, Upstream, aktive Jobs
- `GET /health`: Routerprozess lebt; bleibt bei geplantem Hotswap grün
- `GET /ready`: Router und Textmodell sind einsatzbereit
- `GET /status`: authentifizierter Detailstatus, Profil, Upstream, aktive Jobs
- `GET /v1/models`: virtuelle Modelle
- llama.cpp-Metriken: Port 8080, nur im administrativen Netz freigeben
- systemd-Journal: nur Metadaten und Fehler prüfen; keine Promptinhalte sammeln
@@ -35,6 +42,22 @@ Vision, Bildgenerierung, STT und TTS umgehen.
Niemals Build, Quantisierung und Profil gleichzeitig ändern. Immer genau eine
Variable ändern und anschließend denselben Benchmark ausführen.
Profilkontext und Alias werden zusätzlich in
`/etc/mike-ai/router-profiles.json` gepflegt. Änderungen an Override und
Registry gehören in denselben getesteten Commit; andernfalls verweigert die
Readiness bewusst die Freigabe.
## Fehler- und Recovery-Verhalten
- Ein Profilwechsel bricht ab, wenn laufende Chats nicht innerhalb des
Drain-Timeouts enden. Er beendet niemals absichtlich einen Chat.
- Nach einem Routerabsturz wird das letzte stabile Profil aus der atomaren
Zustandsdatei rekonstruiert.
- `/health = 200`, aber `/ready = 503` bedeutet: Router lebt, Modell ist noch
nicht bereit oder wird gerade gewechselt.
- `429` bedeutet, dass die Parallelitätsgrenze erreicht ist; der Client soll
mit Backoff erneut versuchen.
## Kapazitätsregeln
- Systempartition dauerhaft unter 85 Prozent halten.
+58
View File
@@ -0,0 +1,58 @@
# Router V2 – Migration und Kompatibilität
## Ergebnis
V2 behält die OpenAI-kompatible Basis-URL und die virtuellen Modelle
`qwen-fast`, `qwen-medium` und `qwen-long`. Bestehende Chat-, Tool-, Audio-,
Vision- und Bildpfade bleiben erhalten. Die Änderungen betreffen absichtlich
die Stellen, an denen der alte Router unsicher oder nicht deterministisch war.
## Bewusste Änderungen
| Alt | V2 |
|---|---|
| alle LAN-Clients ohne Authentifizierung | API-Key für alle fachlichen Endpunkte |
| `GET /fast` schaltet ein Modell | nur noch `POST /fast` (analog medium/long) |
| `/health` hängt am Modellzustand | `/health` = Prozess, `/ready` = Textmodell |
| Profil durch Textvergleich erkannt | Kontext + Alias aus Profilregister geprüft |
| Profilwechsel und Request konnten sich überholen | atomare Modell-Lease |
| Drain-Timeout beendete trotzdem das Modell | Wechsel wird sicher abgebrochen |
| Workerzustand nur im RAM | atomare Zustandsdatei und Startup-Recovery |
| beliebige Bild-URL | Data-URL, Größenlimit; Remote standardmäßig aus |
| unbegrenzte Parallelität und Bildablage | Request- und Retention-Limits |
| Auth-Header potenziell am Upstream | Router-Credentials werden entfernt |
## Client-Migration
1. Router-Key aus `/etc/mike-ai/router-api-key` ohne Anzeige in einen lokalen
Secret-Store des Clients übernehmen.
2. Basis-URL unverändert auf `http://HOST:8081/v1` lassen.
3. Den Key als OpenAI-API-Key/Bearer-Token konfigurieren.
4. `GET /v1/models` testen und anschließend einen kurzen Chat über
`qwen-fast` senden.
5. Automationen, die Profile per GET schalten, auf POST umstellen.
6. Überwachung auf `/health` (Liveness) und `/ready` (Readiness) aufteilen.
## Sicheres Rollout
V2 wird nicht blind über einen laufenden Router kopiert:
1. Repository-Commit und aktuelle produktive Konfiguration sichern.
2. Profilregister gegen alle drei systemd-Overrides prüfen.
3. API-Key erzeugen und Clients vorbereiten.
4. Router installieren und zuerst lokal mit Key prüfen.
5. Fast, Medium und Long jeweils einmal schalten und Alias/Kontext prüfen.
6. Streaming und einen Tool Call testen.
7. Erst danach normale Clients auf V2 freigeben.
Ein Rollback stellt Routerdateien und Unit aus dem Installationsbackup wieder
her. Der neu erzeugte API-Key und die Zustandsdatei enthalten keine
Modelldateien oder Chatdaten.
## Verbleibender Architekturpunkt
Der Routerprozess läuft derzeit als root, weil er den systemweiten llama.cpp-
Dienst und temporäre GPU-Worker steuert. Die Unit ist stark gehärtet, dennoch
ist das nicht das langfristige Ideal. V3 soll HTTP/API und privilegierte
Orchestrierung trennen: unprivilegierter Proxy plus kleiner Root-Helper mit
festen, nicht frei parametrisierbaren Aktionen.
+14
View File
@@ -42,6 +42,20 @@ Empfohlene Trennung:
- Firewall erlaubt nur bekannte Quellnetze.
- Externe Suche erhält nur die tatsächliche Suchanfrage, keine Chat-Historie.
## Router-Grenze
- Alle fachlichen Endpunkte verlangen einen mindestens 32 Zeichen langen,
zufälligen Router-Key. Der Dienst startet ohne gültigen Key nicht.
- `/health` und `/ready` sind die einzigen anonymen Endpunkte und geben nur
groben Betriebszustand aus.
- Authentifizierungsheader werden niemals an llama.cpp weitergereicht.
- Remote-Bild-URLs sind standardmäßig gesperrt. Data-URLs werden auf MIME-Typ,
Base64-Gültigkeit und 20 MiB Maximalgröße geprüft.
- Die Zahl gleichzeitiger Requests ist begrenzt; große Uploads sind global
begrenzt und generierte Bilder werden nach Alter, Anzahl und Größe bereinigt.
- Crash-Recovery beendet keine PID nur aufgrund einer Zahl, sondern verlangt
zusätzlich einen erwarteten Prozessmarker in `/proc/<pid>/cmdline`.
## Schreibaktionen
Jede destruktive oder persistente Aktion verwendet: