Files
Predictalytics/docs/PLAN-Linux-Portierung.md
T
RichardandClaude Opus 5 1f230fe12c Fruehjahrsputz 2/2: Dokumentation auf den tatsaechlichen Stand gebracht
Die Plandokumente waren durchweg veraltet: 85 von 91 Punkten im
UMSETZUNGSPLAN standen auf offen, obwohl der Code sie enthielt, und in
FIXPLAN-UI-Ranglisten war keine einzige der 18 Aufgaben abgehakt, obwohl
beide zugehoerigen Commits laengst im Zweig stecken. Alles abschnittsweise
gegen den Code geprueft und die Haekchen gesetzt - mit Belegstellen, damit
die naechste Pruefung nicht wieder bei null anfaengt.

Neu: STATUS.md als Einstiegsseite - wo das Projekt steht, was fertig ist,
was offen ist, und was beim Aufraeumen bewusst stehengeblieben ist. CLAUDE.md
verweist darauf.

Nachgezogen:
* UMSETZUNGSPLAN.md - 66 Punkte abgehakt. Offen bleiben A4 (Strategie-
  Klassifikation), B2 (Secrets) sowie C3/D1/D2 (SQL-Arbeiten des Nutzers).
* FIXPLAN-UI-Ranglisten.md - abgeschlossen bis auf den als "Optional"
  markierten Pfeilrichtungs-Punkt.
* FIXPLAN-TODO.md - Teil D und F abgehakt; die Test-Baseline "16 gruen"
  auf die heutigen 126 korrigiert. Offen: F5 und zwei Tests aus F6.
* FIXPLAN-G-Speicher.md - G1 bis G4 als erledigt vermerkt, Baseline "39/1"
  korrigiert, Pfad auf die nicht mehr existierende WinFormsHost/appsettings.json
  richtiggestellt.
* docs/PLAN-Linux-Portierung.md - der Watchdog-Warnhinweis war ueberholt
  (DcHeartbeatService meldet an /api/watchdog/v1/ping). Abschnitt 11 empfahl
  noch, mit Phase 0 zu beginnen; jetzt benennt er Phase 5 und 6 als das, was
  wirklich aussteht. Die Randnotiz zu Zugangsdaten in LicenseGuard.cs ist
  gegenstandslos, die Datei laeuft ueber den Deploymentcenter.Client.

Zwei Befunde, die keine Aufraeumarbeit sind und deshalb nur dokumentiert
wurden - beide in STATUS.md Abschnitt 3:

1. In src/Predictalytics.Api/appsettings.json steht ein echter
   OpenRouter-API-Key im Klartext, versioniert seit 7045002. Nicht
   eigenmaechtig entfernt: ohne Rotation beim Anbieter bringt das nichts
   (die History behaelt ihn), wuerde aber die KI-Analyse abschalten.
   Der Schluessel muss zurueckgezogen und neu ausgestellt werden.
2. Die Portierung ist auf keinem Linux-System je ausgefuehrt worden. Die
   Verifikation vom 2026-08-08 lief unter Windows.

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

43 KiB
Raw Blame History

Implementierungsplan: Predictalytics auf Windows + Linux

Stand: 2026-08-06 · Basis: ANALYSE-Linux-Portierung.md Ziel: Ein Code-Stand, der unter Windows und Linux identisch läuft — Bedienhülle in Avalonia, gesamte Solution auf .NET 10.


0. Drei Entscheidungen vorab — alle getroffen (2026-08-06)

# Entscheidung Ergebnis
0.1 EF Core Weg A — TFM net10.0, EF Core 9.0.18 + Pomelo 9.0.0
0.2 UI-Framework Avalonia 12.1.1, kein LiveCharts2
0.3 Linux-Ziel Beides — Avalonia-GUI und Headless-Modus (AP 3.4 verbindlich)

0.1 EF Core: Version 9 behalten, nicht auf 10 gehen — entschieden: Weg A

Das ist der einzige Punkt, an dem „alles auf .NET 10" nicht sauber aufgeht.

Befund (verifiziert über nuget.org am 2026-08-06):

Pomelo.EntityFrameworkCore.MySql  → neueste stabile Version: 9.0.0 (2025-08-17)
  nuspec: targetFramework net8.0
          Microsoft.EntityFrameworkCore.Relational  [9.0.0, 9.0.999]   ← harte Obergrenze

Es gibt keinen Pomelo-Provider für EF Core 10. Die Versionsspanne ist nach oben geschlossen; ein Upgrade auf EF Core 10 ist mit Pomelo technisch unmöglich.

Weg Konsequenz
A — empfohlen: TFM net10.0, EF Core 9.0.18, Pomelo 9.0.0 Alle Projekte laufen auf .NET 10. EF-Core-9-Pakete (net8.0) laufen problemlos auf der .NET-10-Runtime. Kein Provider-Wechsel, kein DB-Risiko. Nur +1 EF-Major statt +2.
B — EF Core 10.0.10 + MySql.EntityFrameworkCore 10.0.9 (Oracle) Erzwingt den Wechsel des Datenbankproviders. Andere SQL-Generierung, andere Migrations-Historie, abweichendes Verhalten bei Typ-Mapping und Bulk-Operationen — gegen die Produktiv-DB. Realistisch +35 PT plus Regressionsrisiko in 94 Infrastructure-Dateien und 30+ Migrationen.

→ Gewählt: Weg A. „Auf .NET 10 gleichbleiben" ist das eigentliche Ziel und wird vollständig erreicht — das Zielframework aller Projekte ist net10.0. Dass die EF-Core-Bibliothek bei 9.0.18 bleibt, ist ein Implementierungsdetail ohne Auswirkung auf Runtime, Sprachfeatures oder Toolchain. Sobald Pomelo einen EF-Core-10-Provider veröffentlicht, ist der Nachzug ein Einzeiler.

Weg B entfällt; ein Providerwechsel gegen die Produktiv-DB findet nicht statt.

0.2 Avalonia 12.1.1 — entschieden

Avalonia 12.1.1 (2026-07-29), lib-TFMs: net8.0, net10.0   ← natives .NET 10
Avalonia.Desktop 12.1.1 · Avalonia.Themes.Fluent 12.1.1 · Avalonia.Controls.DataGrid 12.1.2

Eine Einschränkung: Avalonia.Diagnostics liegt noch bei 11.3.18, es gibt keine 12.x-Version. Das ist das DevTools-Inspektorfenster (F12) — reines Entwicklerwerkzeug, für die Laufzeit irrelevant. Falls beim Layouten vermisst: temporär eine 11.3.18-Referenz nur in der Debug-Konfiguration, oder ohne auskommen.

LiveCharts2 wird nicht benötigt. Die Bedienhülle enthält kein einziges Diagramm; alle Charts liegen in der Web-UI (Chart.js). Keine Chart-Bibliothek im Shell-Projekt.

0.3 Wo läuft die Linux-Instanz? — entschieden: beides

Eine Avalonia-GUI braucht unter Linux eine Desktop-Umgebung: X11 (bzw. Wayland über XWayland), dazu libx11, libice, libsm, libfontconfig1 und installierte Fonts. Auf einem headless Server ohne Desktop startet sie nicht.

→ Gewählt: beides in einem Binary. Die Avalonia-Shell für Desktop-Betrieb (Windows und Linux), zusätzlich ein Schalter --headless für Server ohne Desktop-Session.

Damit ist AP 3.4 verbindlicher Bestandteil, nicht optional. Nach der Hosting-Extraktion (Phase 2) kostet er ca. 0,5 PT, weil der gesamte Kern dann bereits GUI-frei ist. Konsequenzen, die dadurch fix werden:

  • Der Lizenzschlüssel muss auch ohne Dialog beziehbar sein (Konfiguration / Umgebungsvariable) — koppelt an die laufende HardwareID-Arbeit.
  • Das Deployment liefert zwei Betriebsarten: Desktop-Start und systemd/Container.
  • Phase 6 verifiziert beide Modi.

1. Zielarchitektur

Der entscheidende Umbau ist nicht die GUI, sondern das Herausziehen eines plattformneutralen Hosting-Kerns. Heute liegen ~870 portable Zeilen (EmbeddedWebServer, Watchdog, Serilog-Setup, Settings) im windows-gebundenen Projekt fest. Danach ist die GUI nur noch eine dünne Schale darüber — und der Headless-Modus fällt fast von selbst ab.

src/
  Predictalytics.Domain              net10.0    unverändert
  Predictalytics.Application         net10.0    unverändert
  Predictalytics.Infrastructure      net10.0    unverändert
  Predictalytics.Api                 net10.0    unverändert
  Predictalytics.Worker              net10.0    unverändert
  Predictalytics.Hosting        NEU  net10.0    ← plattformneutraler Kern
  Predictalytics.Shell          NEU  net10.0    ← Avalonia-Bedienhülle (Win + Linux)
  Predictalytics.Application.Tests   net10.0    net10.0 statt bisher net10.0 (bleibt)
  Predictalytics.WinFormsHost        net10.0-windows   ← Übergang, s. 6.2

Predictalytics.Hosting (neu, GUI-frei) nimmt auf:

Herkunft Ziel
WinFormsHost/Services/EmbeddedWebServer.cs (365 Z.) Hosting/PredictalyticsHost.cs — unverändert bis auf Binding + wwwroot
WinFormsHost/AppSettings.cs (137 Z.) Hosting/PredictalyticsOptions.cs — ohne [Category]/[Editor]-Attribute
WinFormsHost/Services/WatchdogHeartbeatService.cs (141 Z.) 1:1, ist bereits portabel
WinFormsHost/Program.cs:33-148 (Serilog-Setup) Hosting/LoggingSetup.cs — 1:1, ist bereits portabel
WinFormsHost/Services/LicenseGuard.cs (102 Z.) Hosting/LicenseGuard.cs — ohne WinForms-Timer/Dialog
Infrastructure/Logging/RichTextBoxSink.cs umbenennen → DelegateSink.cs (ist schon portabel)

Predictalytics.Shell (neu, Avalonia) enthält nur noch View + ViewModel.


2. Phase 0 — Vorarbeiten erledigt (2026-08-06)

# Aufgabe Ergebnis
0.1 InvariantCulture ergänzen LimitlessProvider.cs:270 (Volume)
0.1b Nachtrag: 13 × lokalzeit-verzerrendes DateTime.TryParse neuer Helfer Infrastructure/Helpers/DateParsing.cs, alle Fundstellen umgestellt
0.2 Eingecheckte Logdatei entfernen nicht nötig — Datei ist nicht getrackt, .gitignore:14 logs/ greift bereits
0.3 j:\-Absolutpfad-Fallback entfernen EmbeddedWebServer.cs
0.4 Branch anlegen feature/linux-net10

Abnahme erreicht: dotnet build → 0 Fehler, 10 Warnungen (alle vorbestehend). dotnet test100 bestanden, 0 Fehler, 1 übersprungen.

Nachtrag 0.1b — Umfang und offener Punkt

DateTime.TryParse("2026-08-06T12:00:00Z") ohne DateTimeStyles liefert Kind=Local und rechnet in Lokalzeit um. Betroffen waren in beiden Providern StartDate, EndDate, CreatedAt, ClosedAt, ExecutedAt, ExpirationDate — während der Fallback derselben Zuweisungen (DateTime.UtcNow) korrektes UTC schrieb. Auf der Berliner Maschine also 12 h Versatz, auf einem Linux-Server mit TZ=UTC hingegen nicht: derselbe Code hätte plattformabhängig unterschiedliche Werte erzeugt.

Altdaten-Backfill: entfällt (Entscheidung 2026-08-06). Der Fix wirkt nur für neue Schreibvorgänge; bereits gespeicherte Zeilen tragen den 12-h-Versatz weiter. Bewusst akzeptiert — die Daten verlieren mit steigendem Alter ohnehin an Relevanz, und die betroffenen Felder werden bei jedem Markt-Sync überschrieben.

Konsequenz für Phase 6: Beim Vergleich Linux-Lauf ↔ Windows-Referenz (Prüfung 8.3) dürfen historische Zeilen nicht als Referenz dienen — nur frisch geschriebene Datensätze sind aussagekräftig.


3. Phase 1 — .NET 10 erledigt (2026-08-06)

Abnahme erreicht: dotnet build → 0 Fehler, 8 Warnungen (alle vorbestehend, 2 weniger als vorher). dotnet test100 bestanden, 0 Fehler, 1 übersprungen, Testhost net10.0. Alle Projekte laufen auf net10.0, WinFormsHost auf net10.0-windows.

Ergebnis gegenüber der Planung:

Erwartung Tatsächlich
Swashbuckle 6.9 → 10.x „wahrscheinlichster Reibungspunkt", ggf. Wechsel auf AddOpenApi() Keine Bruchstelle. Der genutzte API-Ausschnitt (AddSwaggerGen/SwaggerDoc/UseSwagger/UseSwaggerUI) ist stabil geblieben. Kein Umbau nötig.
EF Core 8 → 9 „überschaubar" Bestätigt, keine Anpassung nötig
SQLite-Sicherheitswarnung „löst sich mit dem EF-Bump von selbst" Falsch — EF Core 9.0.18 zieht SQLitePCLRaw 2.1.10, weiterhin von GHSA-2m69-gcr7-jv3q betroffen. Behoben über einen expliziten transitiven Pin auf 2.1.12.

Bewusst vor dem Avalonia-Umbau erledigt: eine Baustelle nach der anderen, und das Shell-Projekt entsteht direkt auf dem Zielframework.

AP 1.1 — Zentrale Frameworkverwaltung

Directory.Build.props setzt TargetFramework=net10.0 zentral; <TargetFramework> aus allen .csproj entfernt (Ausnahme: WinFormsHost → net10.0-windows). Zusätzlich zentral: SatelliteResourceLanguages=en, das bisher in vier Projekten einzeln stand.

Directory.Packages.props mit ManagePackageVersionsCentrally eingeführt — 21 Pakete, Versionen aus allen .csproj entfernt. Zusätzlich CentralPackageTransitivePinningEnabled, um Sicherheits-Pins auf transitive Abhängigkeiten setzen zu können.

Nicht angefasst: Spike/Spike.csproj — leeres Restprojekt ohne Quelldateien, nicht in der Solution, baut auch im Ist-Zustand nicht (CS5001: keine Main-Methode). Kandidat zum Löschen, aber nicht Teil dieser Portierung.

AP 1.2 — Paketmatrix

Paket heute Ziel Anmerkung
Microsoft.EntityFrameworkCore(.Relational/.Design) 8.0.11 9.0.18 Obergrenze durch Pomelo
Pomelo.EntityFrameworkCore.MySql 8.0.2 9.0.0 zieht MySqlConnector 2.4.0
Microsoft.EntityFrameworkCore.InMemory/.Sqlite (Tests) 8.0.11 9.0.18 muss zu EF Core passen
Swashbuckle.AspNetCore 6.9.0 10.2.3 ⚠️ 6.x ist nicht ASP.NET-Core-10-tauglich
Serilog 4.2.0 4.4.0
Serilog.AspNetCore 8.0.3 10.0.0
Serilog.Sinks.File 6.0.0 7.0.0
Serilog.Extensions.Logging / .Sinks.Console / .Formatting.Compact 8.0.0 / 6.0.0 / 3.0.0 jeweils aktuell
Microsoft.Extensions.Http / .Hosting.Abstractions / .Logging.Abstractions 8.0.x 10.0.x
Microsoft.NET.Test.Sdk / xunit / Moq / coverlet aktuell prüfen

Tatsächlich aufgelöste Versionen (verifiziert über dotnet list package): EF Core 9.0.18, Pomelo 9.0.0, Serilog 4.4.0 / Extensions.Logging 10.0.0 / Sinks.File 7.0.0 / Sinks.Console 6.1.1, Swashbuckle 10.2.3, Test-SDK 18.8.1, coverlet 10.0.1. Sicherheits-Pin: SQLitePCLRaw 2.1.12 (statt transitiv 2.1.10).

AP 1.3 — Erwartete Bruchstellen (Rückblick: keine eingetreten)

  • Swashbuckle 6.9 → 10.x ist der wahrscheinlichste Reibungspunkt: geänderte API-Oberfläche, und ASP.NET Core 10 bringt mit Microsoft.AspNetCore.OpenApi eine eigene OpenAPI-Generierung mit. Betrifft Api/ApiConfiguration.cs:19-20 und WinFormsHost/Services/EmbeddedWebServer.cs:143-144,154-155. Alternative: auf das eingebaute AddOpenApi()/MapOpenApi() umsteigen und Swashbuckle ganz ablösen — sauberer, aber die Swagger-UI müsste ersetzt werden (z. B. Scalar).
  • EF Core 8 → 9: überschaubar. Zu prüfen sind Migrationsverhalten und die ExecuteSqlRawAsync-Aufrufe in EmbeddedWebServer.cs:56-100.
  • Predictalytics.Application.Tests steht bereits auf net10.0 — hier wird die Inkonsistenz gerade aufgelöst statt vergrößert.

Abnahme: dotnet build + dotnet test grün; WinFormsHost startet, Worker laufen, Web-UI erreichbar; Migrationen gegen die Dev-DB (bergisnu_db0) verifiziert, nicht gegen Prod.


4. Phase 2 — Hosting-Kern extrahieren erledigt (2026-08-06)

Abnahme erreicht: dotnet build → 0 Fehler. dotnet test100 bestanden, 0 Fehler, 1 übersprungen. Predictalytics.Hosting baut ohne Windows-Bezug; WinFormsHost enthält nur noch UI-Code (MainForm, LicenseDialog, TerminalHelper, Program) und referenziert Api/Worker/Infrastructure/LicenseLabrador nur noch transitiv über Hosting.

⚠️ Betrieblicher Hinweis: settings.json ging verloren

Beim Aufräumen der Build-Ausgabe in Phase 1 wurde bin/ gelöscht — und dort lag die zur Laufzeit erzeugte settings.json mit DB-Zugangsdaten und Watchdog-API-Key. Die Datei war nie versioniert (.gitignore:11) und ist nicht wiederherstellbar.

Beim nächsten Start neu einzutragen: DB Server/Database/User/Password, Watchdog API Key, ggf. Egress-Kanäle.

Der Fehler kann sich nicht wiederholen: durch AP 2.3 liegen die Einstellungen jetzt unter %APPDATA%\Predictalytics\settings.json bzw. ~/.config/Predictalytics/settings.json — außerhalb des Build-Verzeichnisses. dotnet clean oder ein Löschen von bin/ sind damit folgenlos.

Abweichungen von der Planung

Geplant Tatsächlich
Hosting als Microsoft.NET.Sdk.Web mit OutputType=Library (analog Api) Microsoft.NET.Sdk + <FrameworkReference Include="Microsoft.AspNetCore.App" />. Der Web-SDK globbt wwwroot/** automatisch als Static Web Asset und kollidierte mit dem Content-Eintrag aus AP 2.5 (DiscoverPrecompressedAssets: doppelter Schlüssel). Gilt auch für den Host: WinFormsHost ist jetzt ebenfalls normaler SDK. Für die Avalonia-Shell gleich so anlegen.
Process.Start("explorer.exe", …) erst in AP 3.3 ersetzen Vorgezogen — MainForm wurde ohnehin umgeschrieben. Jetzt ProcessStartInfo { UseShellExecute = true }, funktioniert unter Windows und Linux.
LicenseGuard komplett nach Hosting Aufgeteilt: der plattformneutrale Teil liegt in Hosting/LicenseGuard.cs, der interaktive Dialogaufruf in WinFormsHost/Services/LicenseGate.cs. Genau die Naht, an der Phase 3 den Avalonia-Dialog bzw. den Headless-Pfad einhängt.
[Editor]-Attribut für den mehrzeiligen Egress-Editor erhalten Entfallen. Es verwies auf die .NET-Framework-Assembly System.Design, die es in .NET (Core) nicht gibt — der Editor dürfte schon vorher nicht gegriffen haben. Die Avalonia-Settings-View bekommt ein echtes mehrzeiliges Textfeld. Alle übrigen System.ComponentModel-Attribute sind plattformneutral und bleiben, das PropertyGrid behält Gruppen und Beschreibungen.

Zusätzlich mitgenommen

  • PredictalyticsOptions.WebserverHost — Bind-Adresse konfigurierbar (Vorbereitung AP 4.1), Default localhost, also verhaltensgleich.
  • PredictalyticsOptions.DbSslMode — Default bewusst None, damit sich das Verbindungs- verhalten in einem reinen Refactoring nicht ändert. Umstellung auf Preferred ist jetzt eine reine Konfigurationsfrage.
  • PredictalyticsHost.GetDatabaseSizeMbAsync() — die DB-Abfrage lag vorher direkt im MainForm.
  • RichTextBoxSinkDelegateSink umbenannt (war nie WinForms-abhängig).

Reiner Verschiebe- und Entkopplungsvorgang, keine neue Fachlogik.

AP 2.1 — Projekt Predictalytics.Hosting anlegen

net10.0, Referenzen auf Api, Worker, Infrastructure, LicenseLabrador.Client.

AP 2.2 — Verschieben (Tabelle in Abschnitt 1)

EmbeddedWebServerPredictalyticsHost, Watchdog, Serilog-Setup, LicenseGuard. RichTextBoxSinkDelegateSink umbenennen (der Sink ist ein reiner Action<string, LogEventLevel>-Delegat und war nie WinForms-abhängig).

AP 2.3 — Konfiguration entkoppeln

AppSettingsPredictalyticsOptions ohne System.ComponentModel-Attribute. Ladereihenfolge: appsettings.jsonappsettings.{Environment}.json → Umgebungsvariablen (PREDICTALYTICS_) → settings.json im Benutzerprofil (Kompatibilität). Ablageort plattformabhängig über Environment.SpecialFolder.ApplicationData (Windows: %APPDATA%, Linux: ~/.config) statt neben der Executable — das Installationsverzeichnis ist unter Linux üblicherweise nicht beschreibbar.

⚠️ Beim Umbau ist SslMode = MySqlSslMode.None (AppSettings.cs:100) fest verdrahtet. Da die Zugangsdaten künftig auch über Netz zu einer entfernten DB gehen, sollte das konfigurierbar und standardmäßig aktiv werden.

AP 2.4 — Lifecycle GUI-frei machen

  • System.Windows.Forms.TimerPeriodicTimer bzw. IHostedService (MainForm.cs:71 DB-Größe, LicenseGuard.cs:70 Revalidierung)
  • Application.Exit()IHostApplicationLifetime.StopApplication()
  • PredictalyticsHost bekommt Events/Observables (StatusChanged, DbSizeChanged), an die sich später sowohl das Avalonia-ViewModel als auch der Headless-Modus hängen.

AP 2.5 — wwwroot sauber deployen

FindWwwrootPath() (EmbeddedWebServer.cs:308-335) mit der Verzeichnis-Heuristik ersatzlos streichen. Stattdessen in den Host-.csproj:

<Content Include="..\Predictalytics.Api\wwwroot\**"
         Link="wwwroot\%(RecursiveDir)%(Filename)%(Extension)"
         CopyToOutputDirectory="PreserveNewest" />

und im Code nur noch Path.Combine(AppContext.BaseDirectory, "wwwroot").

Abnahme: WinFormsHost benutzt nur noch Predictalytics.Hosting und enthält ausschließlich UI-Code. Predictalytics.Hosting baut ohne Windows-Referenzen.


5. Phase 3 — Avalonia-Bedienhülle erledigt (2026-08-07)

Abnahme: dotnet build → 0 Fehler, 0 Warnungen. dotnet test → 100 bestanden. Beide Betriebsarten laufen an:

Prüfung Ergebnis
Predictalytics.Shell.exe --headless Logging läuft, Lizenzprüfung greift, sauberer Abbruch mit Exit-Code 2 und handlungsfähiger Meldung
Predictalytics.Shell.exe (GUI) startet, Lizenzfenster erscheint, kein Absturz
Konsolen-Kodierung UTF-8 explizit gesetzt — ohne das zeigt die Windows-Konsole nur Fragezeichen statt Rahmen und Emoji

Nachtrag 2026-08-07: Hauptfenster verifiziert

Zunächst blockierte eine abgelaufene Lizenz die Prüfung. Nach der Umstellung auf das Deploymentcenter (Abschnitt 5a) startet die GUI vollständig durch — belegt durch die Logdateien des Laufs:

[INF] Shell initialisiert. Bereit.
[INF] 'Start Webserver' startet die WebUI auf http://localhost:5000
[INF] Einstellungen: C:\Users\bergm\AppData\Roaming\Predictalytics\settings.json

Damit sind Lizenzschranke, Serilog-Aufbau mit Terminal-Sink, MainWindowViewModel.Start() und der neue Einstellungspfad nachgewiesen. Noch nicht durchgeklickt: die einzelnen Wartungsaktionen und die Einstellungsansicht — dafür fehlen die in Phase 1 verlorenen DB-Zugangsdaten.

Umsetzung

Datei Zweck
Program.cs Einstiegspunkt. Argumentauswertung vor jeder Avalonia-Initialisierung — sonst stirbt der Prozess auf einem Server ohne X11, bevor --headless greift.
App.axaml(.cs) Lizenzschranke, dann Hauptfenster. ShutdownMode.OnExplicitShutdown während der Aktivierung, danach OnMainWindowClose.
ViewModels/MainWindowViewModel.cs Bindungsziel, CommunityToolkit.Mvvm-Quellgeneratoren, kapselt PredictalyticsHost
ViewModels/LogLine.cs Terminalzeile mit Einfärbung nach Loglevel (Farbschema aus WinForms übernommen)
Views/MainWindow.axaml Menü, Werkzeugleiste, Terminal-Tab, Einstellungen-Tab, Statusleiste
Views/LicenseWindow.axaml Aktivierungsfenster, ersetzt LicenseDialog
Views/Dialogs.cs Ersatz für MessageBox.Show — im Code aufgebaut, kein Drittanbieterpaket
Services/HeadlessRunner.cs AP 3.4: Betrieb ohne Oberfläche
Services/ConsoleAttach.cs Windows-Konsole anhängen + UTF-8

Abweichungen und Entscheidungen

  • PropertyGrid-Ersatz handgeschrieben, wie empfohlen. Vier Gruppen als HeaderedContentControl, Passwortfelder mit PasswordChar, echtes mehrzeiliges Textfeld für die Egress-Kanäle (besser als das in Phase 2 entfallene [Editor]-Attribut). SSL-Modus als ComboBox; bewusst nur None/Preferred/RequiredVerifyCA/VerifyFull bräuchten hinterlegte Zertifikate und würden hier nur zu Fehlkonfiguration einladen.
  • Speichern ist jetzt explizit („Speichern & übernehmen") statt wie beim PropertyGrid bei jeder Einzeländerung. Klarer, und erspart INotifyPropertyChanged auf PredictalyticsOptions.
  • Feste Fenstergröße aufgelöst (vorher 1886×1088 fix): jetzt MinWidth/MinHeight mit skalierendem DockPanel/Grid.
  • Schrift-Fallbackkette Cascadia Code, DejaVu Sans Mono, Liberation Mono, Consolas, monospace fürs Terminal; Avalonia.Fonts.Inter als mitgelieferte UI-Schrift, weil Linux-Distributionen sehr unterschiedliche Standardfonts haben.
  • Ringpuffer statt Leeren: die WinForms-Fassung verwarf bei 500 Zeilen das gesamte Terminal (rtb.Clear()), jetzt fällt jeweils nur die älteste Zeile heraus.
  • Avalonia.Diagnostics nicht referenziert — gibt es nur bis 11.3.18, nicht für 12.x.
  • global::Avalonia.Application: innerhalb von Predictalytics.* löst der kurze Name Application auf den eigenen Namespace Predictalytics.Application auf. Betrifft jede künftige Avalonia-Klasse in diesem Projekt.
  • Microsoft.NET.Sdk statt Sdk.Web, wie in Phase 2 gelernt (Static-Web-Assets-Kollision mit dem wwwroot-Content-Eintrag).

Headless-Betrieb

Lizenz über PREDICTALYTICS_LICENSE_KEY; ohne nutzbare Lizenz Abbruch mit Exit-Code 2 statt eines Dialogs, den niemand sieht. SIGTERM und SIGINT werden über PosixSignalRegistration abgefangen, damit systemd sauber stoppen kann.

Exit-Codes: 0 planmäßig beendet · 2 keine nutzbare Lizenz · 3 Abbruch mit Fehler.

AP 3.1 — Projekt Predictalytics.Shell

<PackageReference Include="Avalonia" Version="12.1.1" />
<PackageReference Include="Avalonia.Desktop" Version="12.1.1" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="12.1.1" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.2" />

MVVM mit CommunityToolkit.Mvvm (Source-Generatoren) — bei ~15 Settings-Properties plus Status-Bindings spart das spürbar Boilerplate gegenüber handgeschriebenem INotifyPropertyChanged.

AP 3.2 — Control-Mapping

WinForms (heute) Avalonia Aufwand
MenuStrip + 3 Menüs Menu / NativeMenuBar klein
ToolStrip + 2 Buttons StackPanel + Button klein
TabControl / TabPage TabControl / TabItem klein
StatusStrip + 3 Labels DockPanel unten + TextBlock klein
MessageBox.Show (7 Aufrufe) eigener Dialog oder MsBox.Avalonia klein
RichTextBox (farbiges Log) ListBox/ItemsControl über ObservableCollection<LogLine>, DataTemplate mit Foreground-Binding mittel
PropertyGrid kein Äquivalent in Avalonia Core mittelgroß

Zum Log-View: die bisherige Ringpuffer-Logik (TerminalHelper.cs:26-31 — bei 500 Zeilen rtb.Clear()) wird zur ObservableCollection mit RemoveAt(0) — angenehmer, weil die Historie nicht mehr komplett verworfen wird. Marshalling über Dispatcher.UIThread.Post statt Control.BeginInvoke. Farbschema aus TerminalHelper.cs:33-39 direkt übernehmbar.

Zum PropertyGrid — das ist der einzige echte UI-Entwurfsaufwand. Drei Optionen:

Option Bewertung
Handgeschriebene Settings-View (empfohlen) 4 Gruppen / 15 Properties (Webserver 2, Egress 1, Watchdog 6, Database 4 + abgeleiteter ConnectionString). Als Expander je Gruppe mit TextBox/CheckBox/NumericUpDown. Volle Kontrolle über Passwortfelder (PasswordChar) und Mehrzeilen-Editor für Egress. ~11,5 PT.
Drittanbieter Avalonia.PropertyGrid Spart Zeit, bringt aber eine Abhängigkeit fragwürdiger Pflegelage und passt selten zum Fluent-Theme.
Reflection-basierter Generator Elegant, aber Overengineering für 15 Properties.

AP 3.3 — Plattformdetails

  • Fixe Fenstergröße auflösen: MinimumSize/MaximumSize = 1886×1088 (MainForm.Designer.cs:233-234) ist bei abweichender DPI/Auflösung unter Linux untauglich. → skalierendes Grid-Layout, sinnvolles MinWidth/MinHeight.
  • Schriftart: "Cascadia Code" (MainForm.cs:25) existiert unter Linux meist nicht. → Fallback-Kette Cascadia Code, DejaVu Sans Mono, monospace.
  • Process.Start("explorer.exe", …) (3×, MainForm.cs:192,207,209) →
    Process.Start(new ProcessStartInfo(target) { UseShellExecute = true });
    
    funktioniert auf Windows und Linux (dort über xdg-open).
  • Lizenzdialog (LicenseDialog.cs, 118 Z.) in Avalonia nachbauen: Label, TextBox (Großschreibung — CharacterCasing.Upper hat kein direktes Pendant, per TextInput-Handler oder Converter lösen), Status-Label, zwei Buttons. Der Aktivierungsablauf (ValidateAsync + VerifyChecksum) bleibt unverändert. Hinweis: greift auf die HardwareID-Arbeit zu, die separat läuft.

AP 3.4 — Headless-Modus (verbindlich, 0,5 PT — Entscheidung 0.3)

Schalter --headless in Program.cs der Shell: statt BuildAvaloniaApp().StartWithClassicDesktopLifetime() direkt PredictalyticsHost starten und auf IHostApplicationLifetime warten, Logs nur auf Console. Nach Phase 2 ist das reine Verdrahtung, weil der Kern bereits GUI-frei ist.

Zwingend mitzudenken:

  • Lizenz ohne Dialog: Schlüssel aus PREDICTALYTICS_LICENSE_KEY bzw. Konfiguration; bei fehlender/ungültiger Lizenz sauberer Abbruch mit Exit-Code ≠ 0 und klarer Logzeile statt eines Dialogs, den niemand sieht.
  • Keine Avalonia-Initialisierung vor der Argumentauswertung — sonst versucht der Prozess auf einem Server ohne X11 zu starten und stirbt, bevor der Schalter greift.
  • SIGTERM sauber behandeln (IHostApplicationLifetime), damit systemd korrekt stoppt.

Abnahme: Shell startet unter Windows und Linux-Desktop mit GUI; derselbe Build startet mit --headless auf einem Server ohne X11. In beiden Modi: Worker laufen, Web-UI erreichbar, Settings persistieren; im GUI-Modus zusätzlich farbiger Log-View und Lizenzdialog.


5b. WinForms-Host abgelöst (2026-08-08)

main brachte eine vollständige Deployment-Center-Integration mit (Lizenz, Heartbeat mit DB-Health-Check, Fehler-Stream, UpdateService), die im WinForms-Host entstanden war. Diese wurde nach feature/linux-net10 gemerged und dabei in den plattformneutralen Kern gezogen. Predictalytics.WinFormsHost ist entfernt — die Avalonia-Shell ist ab jetzt die einzige Bedienoberfläche.

Was nach Predictalytics.Hosting wanderte

Datei Anmerkung
DcConfig, DcApiClient, DcErrorSink, DcHeartbeatService, DcUpdateService unverändert, waren bereits plattformneutral
DcErrorReporter Application.ThreadException + MessageBox entfernt; stattdessen ReportUiThreadException(Exception), das der Host aufruft. Avalonia hängt sich über Dispatcher.UIThread.UnhandledException ein.
LicenseGuard + LicenseSession Dialogaufruf und System.Windows.Forms.Timer entfernt. Neu: TryUseCachedAsync(), ActivateAsync() und StartPeriodicRevalidation(session, onUnusable) über PeriodicTimer. Die Unterscheidung transienter Fehler und die Gnadenfrist-Warnung sind unverändert übernommen.
Dc-Einstellungen aus AppSettings nach PredictalyticsOptions; die Watchdog-Einstellungen entfielen
DcErrorSink in Serilog in LoggingSetup aufgenommen, Startbanner nutzt jetzt DcConfig.AppVersion

Deploymentcenter.BuildInfo.targets wird nun von Predictalytics.Hosting importiert; BuildInfo entsteht damit im Namensraum Predictalytics.Hosting.

Was in der Avalonia-Shell nachgezogen wurde

  • Menü Deployment Center mit „Nach Updates suchen" und „Lizenzstatus anzeigen"
  • Einstellungsgruppe Watchdog → Deployment Center (Token, Heartbeat, Fehler-Reporting, Update-Kanal als ComboBox; die Server-URL wird nur angezeigt, nicht editiert)
  • Heartbeat-Snapshot samt SELECT 1-DB-Probe, identisch zur WinForms-Fassung
  • Update-Prüfung beim Start (still) und über das Menü (interaktiv), inklusive NotifyStopping() vor dem Start des Update-Agenten
  • Lizenzfenster nutzt IsTransient: bei fehlender Serververbindung wird nicht behauptet, die Lizenz sei ungültig

Verifiziert

🔑 Lizenz geprüft: valid (HWID 2:win:a765bd47…, Quelle machine-guid)
Shell initialisiert. Bereit.
🐕 Heartbeat ist aktiviert, aber es fehlt das Deployment-Center-Token
Update-Prüfung: v1.0.0 ist aktuell (Kanal prod)

--license-status meldet gültig samt Offline-Gnadenfrist. Build 0 Fehler, 100 Tests grün. Noch offen: Heartbeat und Fehler-Stream laufen erst mit gesetztem DC-Token; die Wartungsaktionen brauchen DB-Zugangsdaten.


5a. Zusatz — Umstellung auf das Deploymentcenter-Lizenzsystem (2026-08-07)

Überholt durch 5b. Der hier beschriebene eigene LicenseGuard gegen Deploymentcenter.Client war ein Provisorium und wurde durch die reifere Fassung aus main ersetzt (LicenseSession, IsTransient, TryGetCachedKey, Gnadenfrist-Warnung). Die Begründungen zur Hardware-ID v2 und zum AES-GCM-Cache gelten unverändert.

Nicht ursprünglich geplant. Beim Aktivieren der neuen Lizenz stellte sich heraus, dass Predictalytics noch am alten Lizenzsystem hing (LicenseLabrador, license.mhdf.de), während die Lizenzen inzwischen im Deploymentcenter (dc.mhdf.de) gepflegt werden.

Vor dem Umbau mit einem Wegwerf-Probelauf gegen den echten Client abgesichert: Slug predictalytics, HWID v2 aus machine-guid, Server meldet valid.

Was sich geändert hat

vorher (LicenseLabrador) jetzt (Deploymentcenter)
Endpunkt license.mhdf.de/public/api/v1 dc.mhdf.de/api/license/v1
Client LicenseLabrador.Client (netstandard2.0) Deploymentcenter.Client (netstandard2.0;net8.0)
Authentifizierung Ed25519-Signaturprüfung + Basic Auth im Quelltext serverseitig, kein Geheimnis im Client
Hardware-ID SHA-256 aus MachineGuid + Rechnername v2: 2:<plattform>:<hex>, ohne Rechnername
Linux-HWID /etc/machine-id, sonst MAC /etc/machine-id → dbus → DMI-UUID → MAC → Schlüsseldatei
Container nicht vorgesehen DEPLOYMENTCENTER_HWID überschreibt
Cache DPAPI, stiller Klartext-Rückfall unter Linux AES-256-GCM, HKDF aus HWID+Slug, kein Klartext-Rückfall, 0600 unter Linux
Ablageort %APPDATA% zusätzlich XDG_CONFIG_HOME unter Linux

Damit erledigt sich Analyse-Punkt 3.2 Nr. 2 von selbst: der stille Klartext-Rückfall des Lizenz-Caches unter Linux existiert im neuen Client nicht mehr. Der Rechnername steckt außerdem nicht mehr im Hash — Umbenennen verbraucht keinen Aktivierungsplatz.

Neue CLI-Schalter (Services/LicenseCli.cs)

Aus dem Deploymentcenter-Integrationsleitfaden übernommen, für Server ohne Oberfläche:

Predictalytics.Shell --license-status              # HWID, Cache-Ablage, Serverprüfung
Predictalytics.Shell --license-set-key <SCHLÜSSEL> # einmalig aktivieren
Predictalytics.Shell --license-deactivate          # Aktivierungsplatz freigeben

Die Schalter werden vor jeder Avalonia-Initialisierung ausgewertet, wie --headless. Der Schlüssel wird in der Ausgabe maskiert (LLAB3-*****-*****-*****-AABB2).

Nachgetragen: Watchdog ist migriert (Stand 2026-08-23)

Der hier notierte Rest — WatchdogHeartbeatService gegen watchdog.mhdf.de mit dem Header X-Watchdog-Key — ist erledigt. An seiner Stelle steht Hosting/DcHeartbeatService.cs, das gegen POST /api/watchdog/v1/ping am Deployment Center meldet (:105,143). Damit laufen Lizenz, Watchdog, UpdateService, Fehler-Stream und Bugtracker über dieselbe Anbindung; watchdog.mhdf.de wird nicht mehr angesprochen.


6. Phase 4 — Aufräumen und Absichern erledigt (2026-08-08)

Abnahme: Build 0 Fehler, 8 Warnungen (alle vorbestehend). dotnet test124 bestanden (vorher 100; 24 neue Tests zur Absicherung).

AP 4.1 — Steuerendpunkte werden jetzt tatsächlich geschützt

Der Befund war schlimmer als in der Analyse notiert: /api/capabilities meldete CanControl/AuthRequired aus der Konfiguration, erzwungen wurde nichts. MapPredictalyticsControlEndpoints() mappte Jobs, Trader-Steuerung und die Dev-Endpunkte bedingungslos und ungeschützt.

Neu:

Bindung Token Verhalten
localhost Steuerendpunkte aktiv, ungeschützt (das Betriebssystem schützt)
localhost gesetzt Steuerendpunkte aktiv, Token wird verlangt
extern fehlt Steuerendpunkte werden gar nicht gemappt, dazu ein Log.Error
extern gesetzt Steuerendpunkte verlangen X-Predictalytics-Key

Bewusst fail-safe: eine unbedachte Umstellung der Bind-Adresse öffnet nichts stillschweigend. ApiTokenFilter vergleicht laufzeitkonstant (CryptographicOperations.FixedTimeEquals), damit die Antwortzeit nicht verrät, wie viele Zeichen stimmen. Swagger ist bei externer Bindung abgeschaltet — es legt die gesamte API-Oberfläche offen. /api/capabilities meldet nun den tatsächlichen Zustand statt einer Konfigurationsabsicht.

AP 4.2 — Globalisierung

RuntimeSetup.UseInvariantCulture(), aufgerufen als Erstes in beiden Startpfaden. Bewusst nicht über InvariantGlobalization im Projekt: das änderte zusätzlich die Semantik von Zeichenkettenvergleichen und wäre ein größerer Eingriff als nötig.

AP 4.3 — Log-Verzeichnis

Standard ist jetzt %APPDATA%\Predictalytics\logs bzw. ~/.config/Predictalytics/logs — neben den Einstellungen statt neben der Programmdatei, die unter Linux üblicherweise nicht beschreibbar ist und beim Update überschrieben würde. Übersteuerbar über PREDICTALYTICS_LOG_DIR, etwa auf /var/log/predictalytics im systemd-Betrieb.

AP 4.4 — Solution und Repo-Kopplung

Der WinFormsHost-Sonderfall entfällt, weil das Projekt entfernt ist — die Solution baut unter Linux ohne Bedingung durch. Die relative Referenz auf das Schwester-Repo bleibt (vier Ebenen aufwärts), scheitert jetzt aber mit einer verständlichen Meldung samt Klon-URL statt mit einem Fehler über eine fehlende Projektdatei.

Offen: Git-Submodul oder NuGet-Paket statt Pfadreferenz — für CI und Container relevant, aber eine Entscheidung über Repo-Grenzen hinweg.

AP 4.5 — Egress SourceIp (dokumentiert)

Code ist korrekt; der Unterschied ist zur Laufzeit. Linux verlangt, dass die Adresse tatsächlich auf einem Interface konfiguriert ist (sonst EADDRNOTAVAIL), Windows ist toleranter. Als Kommentar an der Bind-Stelle hinterlegt. Praktischer Test steht in Phase 6 aus.

Neue Tests

  • PredictalyticsOptionsTestsIsPubliclyBound für Loopback-Schreibweisen, Leerwerte und externe Adressen; WebserverUrl; SSL-Modus im Connection-String; und dass leere Eingaben bestehende DB-Zugangsdaten nicht überschreiben.
  • ApiTokenFilterTests — richtiger Token passiert, fehlender/falscher/zu kurzer/zu langer sowie abweichend geschriebener Token werden abgewiesen.

Ende-zu-Ende verifiziert (2026-08-08, gegen die echte Datenbank)

Nachgeholt, sobald die DB-Zugangsdaten wieder vorlagen. Geprüft wurde mit einem Wegwerf-Host, der ausschließlich den Webserver startet — die Worker blieben aus, weil parallel eine ältere Instanz auf derselben Datenbank läuft. Zusätzlich mit ApiSettings__ReadOnlyDatabase=true, sodass weder Migrationen noch Seeding liefen (im Log bestätigt).

Als „externe" Bindung diente 127.0.0.2: die Adresse gilt der Prüfung als öffentlich, bleibt aber Loopback — der Test öffnet also nichts im Netz.

Fall /api/capabilities /api/jobs Swagger
localhost, kein Token canControl:true, authRequired:false 200 200
extern, kein Token canControl:false 404 404
extern, mit Token canControl:true, authRequired:true, tokenHeader:"X-Predictalytics-Key" ohne Header 401, falscher Token 401, richtiger 200 404

Damit ist belegt: eine externe Bindung ohne Token stellt die Steuerendpunkte gar nicht erst bereit, und Swagger ist in beiden externen Fällen abgeschaltet.

Gleichzeitig verifiziert (Vorgriff auf Phase 6):

  • Datenbankverbindung steht — 37.803 MB, SELECT-Pfad über GetDatabaseSizeMbAsync
  • Web-UI und Assets werden ausgeliefert: /, /css/style.css, /js/app.js je 200
  • /api/dashboard liefert echte Daten: 151.094 Trader, 38.187.946 Trades

6a. Phase 4 — ursprüngliche Planung

AP 4.1 — Kestrel-Binding und Absicherung ⚠️

UseUrls($"http://localhost:{port}") (EmbeddedWebServer.cs:122) konfigurierbar machen (ASPNETCORE_URLS bzw. Options), CORS-AllowedOrigins (heute fest http://localhost:5000) mitziehen.

Sobald nicht mehr nur auf localhost gelauscht wird, entfällt die bisher implizite Absicherung. MapPredictalyticsControlEndpoints() (schreibende Operationen) und Swagger wären dann offen erreichbar. Das vorhandene CanControl/AuthRequired-Konzept (Api/ApiConfiguration.cs:66) muss davor scharf geschaltet werden.

AP 4.2 — Globalisierung

Im Host-Bootstrap CultureInfo.DefaultThreadCurrentCulture = CultureInfo.InvariantCulture setzen, damit Zahlenformatierung unabhängig von der Systemlocale ist. InvariantGlobalization=true (spart libicu im Container) erst später erwägen — es ändert String-Vergleichssemantik und sollte nicht im selben Sprung passieren.

AP 4.3 — Log-Verzeichnis

Path.Combine(AppContext.BaseDirectory, "logs") (Program.cs:33) konfigurierbar machen. Default: Windows %APPDATA%, Linux ~/.local/share bzw. /var/log/predictalytics.

AP 4.4 — Solution und Repo-Kopplung

  • Predictalytics.slnx: WinFormsHost mit Condition="$([MSBuild]::IsOSPlatform('Windows'))" versehen, damit dotnet build der Gesamtsolution unter Linux durchläuft.
  • Die ..\..\..\..\LicenseLabrador\…-Projektreferenz (4 Ebenen aufwärts) ist für CI und Container fragil → Git-Submodul oder NuGet-Paket.

AP 4.5 — Egress-Kanäle prüfen ⚠️

EgressPoolService.cs:70-88 bindet Sockets an eine Quell-IP. Unter Linux muss die IP tatsächlich auf einem Interface konfiguriert sein (sonst EADDRNOTAVAIL); Windows ist toleranter. Für nicht-lokale IPs bräuchte es IP_FREEBIND/CAP_NET_ADMIN. Nur relevant, falls Egress-Kanäle produktiv genutzt werden. Proxy-Kanäle sind unkritisch.


7. Phase 5 — Deployment und CI (12 PT)

  • dotnet publish -r linux-x64 (und win-x64); bei self-contained an --self-contained + Trimming-Verhalten von Avalonia denken.
  • Systemabhängigkeiten Linux (GUI): libx11-6, libice6, libsm6, libfontconfig1, libharfbuzz0b, Monospace-Fonts (z. B. fonts-dejavu). (GUI-Variante braucht eine Desktop-Session — siehe 0.3.)
  • systemd-Unit bzw. Dockerfile (für den Headless-Modus); es existiert bereits ein J:\Softwareprojekte\dockerCompose — vermutlich anknüpfbar.
  • Reverse Proxy (nginx/Caddy) mit TLS vor Kestrel.
  • Secrets: DB-Passwort und Deployment-Center-Token liegen im Klartext in settings.json (nicht versioniert). Unter Linux → EnvironmentFile mit 0600 oder Secret-Store. Erledigt: Die früher hier genannten Basic-Auth-Zugangsdaten in LicenseGuard.cs gibt es nicht mehr — die Datei läuft vollständig über den Deploymentcenter.Client. Offen und dringlicher: In src/Predictalytics.Api/appsettings.json steht ein echter OpenRouter-API-Key im Klartext, und diese Datei ist versioniert. Schlüssel zurückziehen, neu ausstellen, künftig über OpenRouter__ApiKey setzen.
  • CI: Build + Test auf Linux und Windows.
  • docs/BETRIEB-Deploymentcenter.md um den Linux-Betrieb ergänzen.

8. Phase 6 — Verifikation (2 PT)

# Prüfung
8.1 Worker-Dauerlauf ≥ 24 h unter Linux gegen die Dev-DB, Logvergleich mit einem Windows-Lauf
8.2 EF-Migrationen von Null gegen Dev-DB (Weg A: nur EF-9-Regression)
8.3 Zahlenformatierung: Lauf unter LANG=de_DE.UTF-8 — Volumina/Preise/PnL gegen Windows-Referenz
8.4 Web-UI vollständig durchklicken (case-sensitive Assets, alle Endpunkte)
8.5 Lizenz: Erstaktivierung, Neustart aus Cache, Offline-Gnadenfrist, Revalidierung
8.6 Watchdog-Heartbeats erscheinen unter watchdog.mhdf.de
8.7 Egress-Kanäle (falls genutzt)
8.8 Shell unter Windows und Linux-Desktop: alle Buttons, Menüs, Settings-Persistenz
8.9 Derselbe Build mit --headless auf einem Server ohne X11: Start, Lizenz aus Konfiguration, SIGTERM-Stopp

9. Zeitplan

Phase Inhalt PT
0 Vorarbeiten 0,5
1 .NET 10 + Paketmatrix 1,52,5
2 Hosting-Kern extrahieren 1,52
3 Avalonia-Shell inkl. Headless-Modus (AP 3.4) 4,56,5
4 Aufräumen, Binding, Absicherung 11,5
5 Deployment + CI (Desktop und systemd/Container) 1,52,5
6 Verifikation (beide Betriebsarten) 2
Summe 12,517,5 PT

≈ 2,53,5 Wochen fokussiert.

Phasen 02 sind risikoarm und liefern bereits Wert (aufgeräumte Architektur, aktuelles Framework). Phase 3 ist der Hauptaufwand. Die Phasen sind strikt sequenziell — jede baut auf der vorigen auf; einzig AP 5 (Deployment) kann parallel zu Phase 3 vorbereitet werden.


10. Risiken

Risiko Eintritt Wirkung Gegenmaßnahme
Pomelo hat keinen EF-Core-10-Provider sicher blockiert EF Core 10 gelöst durch Weg A: EF Core 9 auf net10.0
Swashbuckle 6.9 → 10.x mit Brüchen wahrscheinlich halber bis ganzer Tag ggf. auf eingebautes AddOpenApi() wechseln
PropertyGrid-Ersatz wird aufwendiger als geschätzt mittel +1 PT handgeschriebene View, 15 Properties sind überschaubar
Avalonia-GUI läuft auf dem Linux-Ziel nicht (kein Desktop) sicher auf Servern Shell unbrauchbar abgedeckt durch AP 3.4 (verbindlich)
Avalonia.Diagnostics fehlt für 12.x sicher nur Komfort ohne DevTools arbeiten oder 11.3.18 in Debug
Lizenzaktivierung headless sicher (AP 3.4) mittel koppelt an laufende HardwareID-Arbeit — früh abstimmen
Egress SourceIp unter Linux niedrig mittel AP 4.5, früh testen
Regression in der Datenschicht niedrig (Weg A) hoch ausschließlich Dev-DB, nie Prod

11. Nächster Schritt

Stand 2026-08-23: Phase 0 bis 4 sind abgeschlossen (jeweils oben mit vermerkt), ebenso der Zusatz 5a (Deploymentcenter-Lizenzsystem) und 5b (WinForms-Host abgelöst). Der ursprüngliche Text an dieser Stelle empfahl noch, mit Phase 0 zu beginnen — das ist überholt und deshalb ersetzt.

Offen sind Phase 5 und Phase 6:

  1. Phase 5 — Deployment und CI (Abschnitt 7). Nichts davon ist bisher angefasst: kein dotnet publish -r linux-x64, keine systemd-Unit, kein Dockerfile, kein Reverse Proxy, keine CI. Das ist der eigentliche verbleibende Arbeitsblock.
  2. Phase 6 — Verifikation (Abschnitt 8, Punkte 8.18.9). Der Dauerlauf unter Linux (8.1) und die Prüfung unter LANG=de_DE.UTF-8 (8.3) stehen aus. Bisher wurde die Anwendung noch auf keinem Linux-System ausgeführt — die Portierung ist zwar vollständig gebaut, aber unter Linux unbewiesen.
  3. Unabhängig davon und vorgelagert: den OpenRouter-API-Key aus src/Predictalytics.Api/appsettings.json zurückziehen und neu ausstellen (siehe Abschnitt 7, Secrets).