Files
Predictalytics/docs/BETRIEB-Deploymentcenter.md
T
RichardandClaude Opus 5 168f4699e1 Deployment-Center-Integration im WinFormsHost
Loest die getrennten Server Watchdog (watchdog.mhdf.de) und LicenseLabrador
(license.mhdf.de) durch das Deployment Center (dc.mhdf.de) ab.

- DcConfig: einkompilierte Basis-URL und Produkt-Slug, Version aus BuildInfo
- DcApiClient: gemeinsamer HTTP-Zugang
- DcHeartbeatService: Heartbeat mit Metriken und DB-Health-Check
- DcErrorReporter/DcErrorSink: Error- und Fatal-Meldungen an den Fehler-Stream
- DcUpdateService: Update-Pruefung gegen den UpdateService
- LicenseGuard/LicenseDialog: Lizenzgate ueber /api/license/v1/validate,
  mit LicenseSession, Hardware-ID v2 und Unterscheidung transienter Fehler

WatchdogHeartbeatService entfernt, Betriebsdoku ersetzt.

Build: 0 Fehler.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-08 20:56:45 +02:00

10 KiB
Raw Blame History

Betrieb: Deployment Center

Das Deployment Center (https://dc.mhdf.de, Schwester-Repo J:\Softwareprojekte\Deploymentcenter) hat die beiden früheren Einzelserver abgelöst:

Vorher Jetzt
WatchDog auf watchdog.mhdf.de, POST /api/heartbeat, Header X-Watchdog-Key Deployment Center, POST /api/watchdog/v1/ping, Authorization: Bearer
LicenseLabrador auf license.mhdf.de, Ed25519-signierte Envelopes Deployment Center, POST /api/license/v1/validate, Hardware-ID v2
UpdateService, Fehler-Stream und Bugtracker (neu)

Die Anbindung sitzt im Predictalytics.WinFormsHost und referenziert das C#-SDK Deploymentcenter.Client aus dem Schwester-Repo:

..\..\..\..\Deploymentcenter\client-dotnet\Deploymentcenter.Client\Deploymentcenter.Client.csproj

Das Repo muss also neben dem Predictalytics-Checkout liegen. LicenseLabrador und WatchDog werden nicht mehr gebraucht.


0. Was einmalig zu tun ist

  1. Im Deployment Center unter Token-Verwaltung → Master-Token erstellen ein Token mit den Rechten watchdog:ping und bugtracker:report anlegen.
  2. Im Host unter Settings → Deployment Center → API Token eintragen. Der alte X-Watchdog-Key funktioniert nicht mehr — es ist ein anderer Server.
  3. Beim ersten Start erscheint einmalig der Lizenzdialog: der Lizenzschlüssel muss erneut eingegeben werden, weil die Aktivierung jetzt am Deployment Center hängt.
  4. Serverseitig muss der Evaluator-Cron laufen — ohne ihn bleibt ein abgestürzter Dienst dauerhaft grün:
    * * * * * curl -fsS -H "Authorization: Bearer <SHARED_KEY>" https://dc.mhdf.de/api/watchdog/v1/evaluate > /dev/null
    

Der Token landet in der settings.json neben der Exe (nicht im Git).


1. Konfiguration (PropertyGrid, Kategorie „Deployment Center")

Feld Default Bedeutung
Server URL https://dc.mhdf.de schreibgeschützt, siehe unten
API Token (leer) Authorization: Bearer. Ohne Token: keine Heartbeats, keine Fehlermeldungen.
Heartbeat aktiv true Dead-Man's-Switch an/aus
Monitor Source Predictalytics Monitor-Name im Dashboard (Auto-Registrierung beim ersten Ping)
Monitor Instance default falls mehrere Instanzen laufen
Heartbeat-Intervall (Sekunden) 60 warning nach 2×, down nach 4×
Fehler melden true Error/Fatal an den Fehler-Stream
Update-Prüfung beim Start true prüft nur, installiert nichts von selbst
Update-Kanal prod prod, beta oder dev

Warum ist die Server-URL nicht einstellbar? Sie entscheidet, wohin die Lizenzprüfung geht und woher Update-Pakete kommen. Ein einstellbarer Endpoint würde erlauben, die App auf einen gefälschten Lizenz- oder Update-Server zu zeigen. Sie steht als Konstante in Services/DcConfig.cs.


2. Watchdog (Dead-Man's-Switch)

Services/DcHeartbeatService.cs sendet alle n Sekunden einen POST /api/watchdog/v1/ping. Beim regulären Schließen geht ein Ping mit status: "stopped" raus — der Evaluator lässt einen so beendeten Monitor in Ruhe, bis wieder ein normaler Heartbeat eintrifft. Ohne das folgt wenige Minuten nach jedem geordneten Beenden ein Fehlalarm.

Ein Ausfall des Deployment Centers darf Predictalytics nie beeinträchtigen. Alle Aufrufe sind best effort; der erste Fehlschlag wird als Warnung geloggt, Folgefehler nur noch auf Debug-Level (keine Log-Flut). Wird das Token abgelehnt (401/403), stellt der Dienst die Versuche ganz ein — Wiederholen kann daran nichts ändern.

Mitgesendeter Gesundheitszustand

Ein Heartbeat beweist nur, dass ein Timer läuft. Deshalb schickt die App ihren selbst ermittelten Zustand mit:

checks Bedeutung
db SELECT 1 gegen die konfigurierte MySQL, gedeckelt auf 5 s. Schlägt sie fehl, stuft der Server den Heartbeat auf warning.
metrics Bedeutung
uptime_sec Laufzeit des Prozesses
workers_running 0/1 — Hintergrund-Worker gestartet
webserver_running 0/1 — eingebetteter Kestrel gestartet
memory_mb verwalteter Heap
db_size_mb zuletzt ermittelte Datenbankgröße

Zusätzlich geht seit Server 2.1 das Feld version mit — im Dashboard ist damit sichtbar, welcher Build läuft, und ein Ausfall lässt sich einem Rollout zuordnen.

Gestoppte Worker sind bewusst kein fehlgeschlagener Check: das ist ein gewollter Betriebszustand und würde den Monitor sonst dauerhaft auf warning halten. Der Zustand steckt als Metrik drin und lässt sich dort auswerten.

Metriken werden serverseitig 14 Tage lang mit Verlauf gehalten (GET /api/watchdog/v1/metrics?source=Predictalytics).

Maschine mit überwachen

Der Heartbeat deckt nur diesen Prozess ab. Für die Maschine selbst gehört zusätzlich ein OS-Agent auf den Host (siehe Deploymentcenter/docs/WATCHDOG_INTEGRATION_GUIDE.md). Läuft beides, sollte im WebUI unter WatchDog → System-Hierarchie die Übergeordnete Entität des Predictalytics-Monitors auf den Host gesetzt werden — dann erzeugt ein Maschinenausfall eine Meldung statt zwei.


3. Lizenzierung

Services/LicenseGuard.cs prüft beim Start, ob eine nutzbare Lizenz vorliegt; Program.Main bricht sonst ab, bevor die MainForm entsteht. Ohne gültige Lizenz erscheint Services/LicenseDialog.cs zur Key-Eingabe.

  • Produkt-Slug: predictalytics
  • Hardware-ID v2: 2:win:<sha256> aus HKLM\...\Cryptography\MachineGuid. Der Rechnername steckt nicht im Hash — Umbenennen kostet keinen Aktivierungsplatz.
  • Offline-Cache: AES-256-GCM + DPAPI unter %AppData%\predictalytics\license\state.dat (Schema 3 seit SDK 2.1, Schema 2 wird noch gelesen). Dort liegt auch der Lizenzschlüssel — LicenseClient.TryGetCachedKey() holt ihn beim Start, damit ohne Dialog revalidiert werden kann.
  • Revalidierung zur Laufzeit: alle 12 h.
  • Gemeldete Version: LicenseClient.DefaultAppVersion wird in Program.Main auf BuildInfo.Version gesetzt; ohne das trüge jede Installation in der Aktivierungsliste dieselbe „1.0.0".

Offline-Gnadenfrist

Seit Server 2.1 ist sie echt begrenzt: der Server liefert cache_ttl_hours (je Projekt im WebUI, Standard 168 h = 7 Tage). Vorher galt faktisch das Lizenz-Ablaufdatum, bei einer Lizenz bis 2040 also unbegrenzt.

Läuft die Frist in weniger als 48 h ab, warnt der Host beim Start und bei jeder Revalidierung. Die verbleibende Frist zeigt Deployment Center → Lizenzstatus anzeigen. Falls Predictalytics planmäßig länger offline laufen soll, muss die TTL im WebUI für das Projekt predictalytics heraufgesetzt werden.

Wann die App sich beendet — und wann nicht

Maßgeblich ist LicenseValidationResult.IsTransient:

Zustände Reaktion
IsValid valid, valid_offline weiter
IsTransient server_unavailable, cache_expired kein Urteil, nur eine gescheiterte Verbindung — weiterlaufen, beim nächsten Durchlauf erneut versuchen
sonst revoked, expired, not_found, activation_limit, suspended, clock_rollback Meldung und Beenden

cache_expired beendet die laufende Sitzung also nicht — der nächste Start bleibt aber am Lizenzdialog hängen, weil IsValid dann falsch ist. Der Host loggt diesen Fall deshalb als Error, nicht bloß als Warnung.

Deaktivierung bei PC-Wechsel

POST /api/license/v1/deactivate verlangt den shared_key aus der Server-Konfiguration. Der gehört nicht in die ausgelieferte Anwendung, deshalb ist die Funktion hier nicht verdrahtet. Übliche Route: im WebUI unter Lizenzen → Hardware-Liste → „Freigeben". Die Hardware-ID zeigt der Host unter Deployment Center → Lizenzstatus anzeigen.


4. Fehler-Stream

Services/DcErrorReporter.cs meldet Laufzeitfehler an POST /api/errors/v1/report. Zwei Wege führen dorthin:

  1. Globale HandlerApplication.ThreadException (UI-Thread), AppDomain.UnhandledException (Level fatal, wird blockierend gesendet, weil der Prozess gleich weg ist) und TaskScheduler.UnobservedTaskException.
  2. Serilog-Sink (Services/DcErrorSink.cs) — jedes Log.Error/Log.Fatal, auch aus Worker und Kestrel.

Lokale Bremse, damit das Rate-Limit (60/min und IP) nicht verbrannt wird: höchstens 20 Meldungen pro Minute, derselbe Fehler höchstens alle 5 Minuten. Serverseitig werden gleiche Fehler ohnehin gruppiert und hochgezählt; betriebsbedingtes Rauschen gehört in die Ignore-Regeln des WebUI — ein Treffer zählt weiter, meldet aber nicht.

Der Serilog-Filter in Program.cs wirft Duplicate entry-Ausnahmen schon vorher weg; die erreichen den Stream also gar nicht.


5. Updates

Services/DcUpdateService.cs fragt beim Start GET /api/updateservice/v1/check ab (ohne Token). Gefunden wird nur — installiert wird nichts von selbst:

  • Kein Update: Info ins Log.
  • Update verfügbar: Warnung ins Log, Hinweis in der Statusleiste.
  • Kritisches Update: zusätzlich ein Dialog.
  • Menü Deployment Center → Nach Updates suchen prüft jederzeit von Hand.

Liegt update-agent.exe neben der Anwendung, bietet der Dialog an, ihn zu starten; die Anwendung meldet dann vorher stopped und beendet sich. Fehlt der Agent, weist der Dialog auf die manuelle Installation hin.

Seit SDK 2.1 liefert auch der API-Zweig vollständige Release-Daten (Download-Adresse, Prüfsumme, Changelog, Kritikalität) — der Dialog zeigt die Release Notes also auch dann, wenn die statische latest.json fehlt.

Versionsstand

Predictalytics.WinFormsHost.csproj importiert Deploymentcenter.BuildInfo.targets und erzeugt daraus zur Übersetzungszeit Predictalytics.WinFormsHost.BuildInfo mit Version, GitCommit, GitCommitShort, BuildDateUtc, Channel und Summary. Quelle ist <Version> in derselben csproj — beim Release dort hochziehen und mit

pack-and-deploy --project predictalytics --version <x> --channel prod

veröffentlichen. Dieselbe Version geht an die Lizenz-Aktivierungsliste, an den Heartbeat und als build an den Fehler-Stream; GitCommitShort reist im context jeder Fehlermeldung mit.


6. Bugtracker

Der Agenten-Workflow ist in ../CLAUDE.md beschrieben. Projekt-Slug: predictalytics. Items, deren resolved_in_build einer veröffentlichten Version entspricht, schließen sich beim Publish von selbst.