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>
10 KiB
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
- Im Deployment Center unter Token-Verwaltung → Master-Token erstellen ein
Token mit den Rechten
watchdog:pingundbugtracker:reportanlegen. - Im Host unter Settings → Deployment Center → API Token eintragen.
Der alte
X-Watchdog-Keyfunktioniert nicht mehr — es ist ein anderer Server. - Beim ersten Start erscheint einmalig der Lizenzdialog: der Lizenzschlüssel muss erneut eingegeben werden, weil die Aktivierung jetzt am Deployment Center hängt.
- 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>ausHKLM\...\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.DefaultAppVersionwird inProgram.MainaufBuildInfo.Versiongesetzt; 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:
- Globale Handler —
Application.ThreadException(UI-Thread),AppDomain.UnhandledException(Levelfatal, wird blockierend gesendet, weil der Prozess gleich weg ist) undTaskScheduler.UnobservedTaskException. - Serilog-Sink (
Services/DcErrorSink.cs) — jedesLog.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.cswirftDuplicate 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.