Files
Predictalytics/docs/BETRIEB-Deploymentcenter.md
RichardandClaude Opus 5 55011644a3 Fruehjahrsputz 1/2: toter Code, Altlast-Dateien, veraltete Verweise
Toter Code:
* ApiConfiguration.ConfigureApi (29 Z.) hatte keinen Aufrufer mehr.
  PredictalyticsHost baut die WebApplication seit Phase 2 selbst auf
  (Kestrel, CORS, Swagger, Static Files); ConfigureApi war der
  zurueckgebliebene Zwilling aus der Zeit davor.
* IAnalyticsService.GetTraitsAsync samt Implementierung. Die Methode gab
  konstant eine leere Liste zurueck; ihr eigener Kommentar hielt fest, dass
  sie ungenutzt ist. Der Endpunkt /api/traders/traits liest direkt aus dem
  DbContext und bleibt unveraendert.

Ungenutzte Paketreferenzen:
* Swashbuckle.AspNetCore aus Predictalytics.Api - wurde nur von ConfigureApi
  gebraucht; Swagger baut Hosting auf, das die Referenz selbst haelt.
* Microsoft.EntityFrameworkCore.Design aus Predictalytics.Worker - die
  Design-Time-Factory liegt in Infrastructure.
* Serilog.Sinks.File/.Console und Serilog.Formatting.Compact aus
  Predictalytics.Infrastructure - dort wird kein Logger konfiguriert, nur
  Serilog.Core/Events/Context verwendet. Die Sinks haengen am Hosting.

Altlast-Dateien:
* Spike/ - Projektdatei ohne eine einzige Quelldatei, net8.0, nicht in der
  Solution.
* query.csx - Ad-hoc-Abfrageskript von Juli mit fest eingetragenen
  DB-Zugangsdaten.
* NewDesign.zip (388 KB) und temp_new_design/ - Rohmaterial des
  Design-Entwurfs vom 15.07. Das Ergebnis liegt fertig in
  wwwroot/landing.html und wwwroot/docs.html.

Veraltete Verweise auf den entfernten WinForms-Host:
* CLAUDE.md nannte fuer die Release-Version eine csproj, die es nicht mehr
  gibt - sie steht seit der Zentralisierung in Directory.Build.props.
* docs/BETRIEB-Deploymentcenter.md: Anbindung und BuildInfo sitzen in
  Predictalytics.Hosting.
* docs/API.md: Die Aussage "ohne Authentifizierung" galt vor Phase 4. Jetzt
  mit Header X-Predictalytics-Key und den heutigen Methodennamen.
* Kommentare in Directory.Build.props, DevEndpoints.cs, PredictalyticsHost.cs.

Build ohne neue Warnungen (die 8 bestehenden CS86xx sind unveraendert),
126 Tests gruen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 12:19:46 +02:00

10 KiB
Raw Permalink 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 in Predictalytics.Hosting 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.Hosting.csproj importiert Deploymentcenter.BuildInfo.targets und erzeugt daraus zur Übersetzungszeit Predictalytics.Hosting.BuildInfo mit Version, GitCommit, GitCommitShort, BuildDateUtc, Channel und Summary. Quelle ist <Version> in Directory.Build.props — 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.