From 725746d20479536732eb96d19509a45584df9884 Mon Sep 17 00:00:00 2001 From: Richard Date: Thu, 30 Jul 2026 17:42:02 +0200 Subject: [PATCH] Watchdog-Integration gegen Produktivserver verifiziert und korrigiert - Shutdown-Event sendet stopped_graceful statt stopping: der Server kennt "stopping" im Router, aber event_log.kind ist ein ENUM ohne diesen Wert. Folge war HTTP 500 nach dem Zustandswechsel, das Event fehlte in der Historie. - Default-Source auf "Predictalytics" korrigiert (Schreibweise des bereits angelegten Monitors auf dem Server). Heartbeat, Metrics-Payload, Lizenzvalidierung und der Startpfad ueber den Cache sind end-to-end gegen die Produktivserver getestet. Co-Authored-By: Claude Fable 5 --- docs/BETRIEB-Watchdog-Lizenz.md | 26 ++++++++++++------- .../AppSettings.cs | 4 +-- .../Services/WatchdogHeartbeatService.cs | 8 ++++-- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/docs/BETRIEB-Watchdog-Lizenz.md b/docs/BETRIEB-Watchdog-Lizenz.md index edccc80..1d131a1 100644 --- a/docs/BETRIEB-Watchdog-Lizenz.md +++ b/docs/BETRIEB-Watchdog-Lizenz.md @@ -15,8 +15,13 @@ binden zwei eigenständige Schwester-Projekte an: `Services/WatchdogHeartbeatService.cs` sendet alle *n* Sekunden einen `POST /api/heartbeat` an den Watchdog. Bleiben die Heartbeats aus — weil die App abgestürzt ist oder die ganze Maschine weg ist — schlägt der Watchdog Alarm. -Beim regulären Schließen geht ein `POST /api/event` mit `kind=stopping` raus, -damit ein geplantes Beenden nicht als Crash alarmiert wird. +Beim regulären Schließen geht ein `POST /api/event` mit `kind=stopped_graceful` +raus, damit ein geplantes Beenden nicht als Crash alarmiert wird. + +> **Nicht auf `kind=stopping` ändern.** Der Watchdog akzeptiert den Wert im +> Router, aber `event_log.kind` ist ein ENUM ohne `stopping`. Der Server setzt +> dann zwar noch den Zustand auf `stopped`, scheitert aber am Insert und liefert +> HTTP 500 — das Event fehlt in der Historie. Verifiziert am 2026-07-30. **Wichtig:** Ein Ausfall des Watchdogs darf Predictalytics nie beeinträchtigen. Alle Aufrufe sind best effort; der erste Fehlschlag wird als Warnung geloggt, @@ -29,7 +34,7 @@ Folgefehler nur noch auf Debug-Level (keine Log-Flut). | `Enabled` | `true` | Heartbeats an/aus | | `Server URL` | `https://watchdog.mhdf.de` | Basis-URL | | `API Key` | *(leer)* | `X-Watchdog-Key` — Shared Key **oder** Agent-Token. Ohne Key passiert nichts. | -| `Source` | `predictalytics` | Monitor-Name im Dashboard (Auto-Registrierung beim ersten Beat) | +| `Source` | `Predictalytics` | Monitor-Name im Dashboard (Auto-Registrierung beim ersten Beat) | | `Instance` | `default` | falls mehrere Instanzen laufen | | `Interval (Sekunden)` | `60` | Sende-Takt; Alarm nach ca. `Intervall × 1,5 + 30 s` | @@ -60,13 +65,16 @@ einen gefälschten Lizenzserver zu zeigen. - **Härtung:** `VerifyChecksum` (HMAC über State + Key + Hardware-ID) gegen Memory-Patches; Nonce-Reflexion und Signaturprüfung übernimmt das SDK -### Einmalige Server-Schritte +### Status -1. Im Admin-Backend von `license.mhdf.de` das Produkt mit Slug **`predictalytics`** anlegen - (ohne den Eintrag liefert der Server `not_found`). -2. Lizenzschlüssel ausstellen und beim ersten Start im Dialog eingeben. -3. Prüfen, dass der einkompilierte Public Key in `LicenseGuard.PublicKeyBase64` - dem `signing.pub` des Servers entspricht. +Produkt `predictalytics` und Lizenzschlüssel sind angelegt; die Aktivierung wurde +am 2026-07-30 gegen den Produktivserver verifiziert (`Valid`, gültig bis +2040-12-31, Checksum-Prüfung bestanden). Der einkompilierte Public Key passt zum +`signing.pub` des Servers — sonst käme `TamperSuspected` statt `Valid`. + +Die Aktivierung ist an die Hardware-ID der jeweiligen Maschine gebunden. Auf +einem neuen Rechner erscheint einmalig der Dialog; der Cache liegt danach unter +`%AppData%\predictalytics\license`. ### Deaktivierung bei PC-Wechsel diff --git a/src/Predictalytics.WinFormsHost/AppSettings.cs b/src/Predictalytics.WinFormsHost/AppSettings.cs index 8961568..fe25583 100644 --- a/src/Predictalytics.WinFormsHost/AppSettings.cs +++ b/src/Predictalytics.WinFormsHost/AppSettings.cs @@ -48,8 +48,8 @@ public class AppSettings [Category("Watchdog")] [DisplayName("Source")] [Description("Eindeutiger Monitor-Name dieses Dienstes im Watchdog-Dashboard.")] - [DefaultValue("predictalytics")] - public string WatchdogSource { get; set; } = "predictalytics"; + [DefaultValue("Predictalytics")] + public string WatchdogSource { get; set; } = "Predictalytics"; [Category("Watchdog")] [DisplayName("Instance")] diff --git a/src/Predictalytics.WinFormsHost/Services/WatchdogHeartbeatService.cs b/src/Predictalytics.WinFormsHost/Services/WatchdogHeartbeatService.cs index 295a459..0305f57 100644 --- a/src/Predictalytics.WinFormsHost/Services/WatchdogHeartbeatService.cs +++ b/src/Predictalytics.WinFormsHost/Services/WatchdogHeartbeatService.cs @@ -49,7 +49,11 @@ public sealed class WatchdogHeartbeatService : IDisposable _baseUrl, _source, _intervalSeconds); } - /// Sends a lifecycle event (e.g. "stopping") so a planned shutdown is not alarmed as a crash. + /// + /// Reports a planned shutdown so it is not alarmed as a crash. + /// Must be "stopped_graceful": the server's event_log.kind ENUM has no "stopping" + /// value, and that variant fails the insert with HTTP 500 after the state update. + /// public void NotifyStopping() { try @@ -58,7 +62,7 @@ public sealed class WatchdogHeartbeatService : IDisposable { source = _source, instance = _instance, - kind = "stopping", + kind = "stopped_graceful", severity = "info", message = "Predictalytics wird planmäßig beendet." };