Der offene Stand lag ueber sieben Konzepte, zwei Referenzdokumente und die
Phasen-Checkliste der Architektur verteilt. Dieselbe Aufgabe stand teils
doppelt unter zwei Namen - die asynchrone Fill-Verfolgung etwa als "bekannte
Grenze" in IBKR-Integration.md und zugleich als W-2 im OptionsWheel-Konzept.
Wer wissen wollte, was als Naechstes ansteht, musste alles neun lesen.
docs/ROADMAP.md fuehrt das zusammen:
- Fuenf Stufen in Abhaengigkeitsreihenfolge, von "Fundament schliessen" bis
zum OptionsWheel, dazu vier laufende Bahnen (Auslieferung, Accounting,
Supervisor, technische Schulden).
- Jede Zeile traegt ihre Herkunft (W-2, P5, Kapitalmodell 5, ...), damit die
Herleitung im Archiv auffindbar bleibt.
- Eigene Abschnitte fuer Zurueckgestelltes und Verworfenes. Zurueckgestellte
Ideen nennen ausdruecklich, WAS sie wieder aktuell macht; verworfene nennen
den Grund, damit sie nicht in sechs Monaten erneut vorgeschlagen werden.
- Erledigtes bleibt als Zeile mit Datum stehen statt zu verschwinden.
Archiv (git erkennt alle sieben als Umbenennung, Historie bleibt):
docs/konzepte/* -> docs/archiv/
docs/Kapital-und-Buchmodell.md -> docs/archiv/
Jedes archivierte Dokument bekommt oben einen Vermerk, warum es erhalten bleibt
und wo der lebende Stand steht. Inhaltlich ist keines veraendert.
Weiter gepflegt werden ARCHITECTURE.md (Aufbau + Phasen-Historie),
IBKR-Integration.md (Adapter-Design und Grenzen) und die TWS-Setup-Checkliste -
das sind Referenzen, keine Planung. Ihre eigenen Offen-Listen verweisen jetzt
mit Roadmap-Kennung dorthin, statt einen zweiten Stand zu fuehren.
Alle 63 relativen Markdown-Links geprueft, keiner tot. Fuenf Pfadverweise in
Code, csproj und systemd-Unit mitgezogen; die Unit zeigte auf die
Portierungsanalyse, die ausdruecklich den Stand VOR dem Umbau beschreibt - jetzt
auf ARCHITECTURE.md. Build 0 Warnungen, 198/198 Tests gruen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
673 lines
36 KiB
Markdown
673 lines
36 KiB
Markdown
> ### 📦 Archiviert am 2026-08-23
|
||
> Dieses Dokument wird **nicht mehr gepflegt**. Was davon noch offen ist, steht in der
|
||
> [Roadmap](../ROADMAP.md) (Bahn „Auslieferung / Deploymentcenter") – dort und nur dort wird der Stand nachgeführt.
|
||
>
|
||
> Es bleibt erhalten, weil es die bewussten Abweichungen vom DC-Leitfaden begruendet – Sperrbetrieb statt
|
||
> Prozessende, `exitCurrentApp: false` – und die Befunde P1–P6 und D1–D7 nachweist. Zum Nachschlagen also weiterhin richtig,
|
||
> als Aufgabenliste nicht mehr.
|
||
|
||
---
|
||
|
||
# KONZEPT: Deploymentcenter-Integration
|
||
|
||
> Stand: 2026-08-14 · Deploymentcenter-Version **2.5.1** · Quelle: `J:\Softwareprojekte\Deploymentcenter\docs`,
|
||
> `GET /api/updateservice/v1/changelog?since=2.4`
|
||
> Zielprojekt: IBKRTrader (App + Daemon), Projekt-Slug **`ibkrtrader`**
|
||
> SDK-Zielversion: `Deploymentcenter.Client` **2.5.1** (`DeploymentcenterSdk.Version` mitführen, damit
|
||
> künftige Anbindungen über `?since=2.5.1` direkt sehen, was sich seither geändert hat)
|
||
|
||
> **Nachtrag 2026-08-14:** Die ursprüngliche Fassung dieses Dokuments (Stand DC 2.4) enthielt sieben
|
||
> Befunde/Bedenken in §8, die dem Deploymentcenter-Entwickler mitgeteilt wurden. Alle sieben sind mit
|
||
> 2.5.0/2.5.1 behoben — Einzelheiten weiterhin in §8, jetzt mit Fix-Vermerk. Zwei Neuerungen aus
|
||
> derselben Runde gehen über die ursprünglichen Befunde hinaus und sind in §5.1, §7.2 und §2.2
|
||
> eingearbeitet: `exitCurrentApp` ist jetzt als harter Kill dokumentiert, und `BuildInfo.targets` liegt
|
||
> im NuGet-Paket selbst.
|
||
|
||
Das Deploymentcenter (`https://dc.mhdf.de`) bündelt fünf Dienste, die IBKRTrader bisher
|
||
alle nicht hat: **Lizenz**, **Watchdog**, **Error-Reporting**, **Bugtracker** und
|
||
**UpdateService inkl. Erstinstallation**. Dieses Dokument beschreibt, in welcher
|
||
Reihenfolge sie angebunden werden, was dabei am Projekt vorher aufgeräumt werden muss
|
||
und wo die vom Deploymentcenter vorgeschlagenen Muster für ein *handelndes* System
|
||
nicht ohne Weiteres passen.
|
||
|
||
---
|
||
|
||
## 0. Ausgangslage
|
||
|
||
| | Zustand heute |
|
||
|---|---|
|
||
| DC-Anbindung | **keine** — kein Treffer für `Deploymentcenter`, `dc.mhdf.de` oder `DC_TOKEN` im Quelltext |
|
||
| Version | Zeichenkette `"1.0.0"` in `App/Program.cs:34` und `Daemon/Program.cs:43`, keine `<Version>`, keine `Directory.Build.props` |
|
||
| Globale Fehlerbehandlung | **keine** — weder `AppDomain.UnhandledException` noch `TaskScheduler.UnobservedTaskException` |
|
||
| Auslieferung | `dotnet publish` von Hand, `deploy/README.md`, systemd-Unit |
|
||
| CI | Gitea Actions, Matrix `ubuntu-latest` / `windows-latest`, Checkout **nur** von IBKRTrader |
|
||
|
||
Referenzintegration ist **Predictalytics** (`src/Predictalytics.Hosting/Dc*.cs`,
|
||
`docs/BETRIEB-Deploymentcenter.md`). Sie liegt auf SDK-Stand 2.2 und ist damit
|
||
unvollständig — siehe [§8](#8-befunde-und-offene-punkte), Punkt D2. Übernommen wird
|
||
das Muster, nicht der Code eins zu eins.
|
||
|
||
Der Einbauort ist **`IBKRTrader.Hosting`**: den Host teilen sich Shell und Daemon
|
||
(`AppHostBuilder`), damit gilt jede Anbindung automatisch für beide Betriebsformen.
|
||
`IBKRTrader.Core` bleibt frei davon — der Core soll keine Netzabhängigkeit zu einem
|
||
Verwaltungsserver bekommen.
|
||
|
||
---
|
||
|
||
## 1. Voraussetzungen im Deploymentcenter (vor jedem Code)
|
||
|
||
Ohne diese Schritte läuft nichts von dem, was danach kommt:
|
||
|
||
1. **Projekt `ibkrtrader` anlegen** (WebUI → Projekte). Slug wird von Lizenz,
|
||
UpdateService, Fehler-Stream und Bugtracker gemeinsam benutzt.
|
||
2. **Offline-Gnadenfrist festlegen** (`default_cache_ttl_hours`). Vorgabe 168 h.
|
||
Für einen Handelsdienst, der auch bei gestörter Internetverbindung weiterlaufen
|
||
soll, ist das die entscheidende Stellschraube — siehe [§5](#5-lizenz).
|
||
3. **Lizenz für `ibkrtrader` ausstellen.** Ohne sie kommt die Anwendung nach dem
|
||
ersten Release nicht mehr an ihre Updates (Zugangsschutz seit 2.4).
|
||
4. **Master-Token** mit `watchdog:ping`, `bugtracker:report` — für die Anwendung.
|
||
Zweites Token mit `updateservice:publish` — für die Release-Pipeline, gehört
|
||
**nicht** in die Anwendung.
|
||
5. **Installationskonto** (Rolle `installer`) für die Erstinstallation.
|
||
6. **Evaluator-Cron prüfen.** `* * * * * php cli/tick.php --quiet`. Ohne ihn bleibt
|
||
ein abgestürzter Trading-Daemon im Dashboard dauerhaft grün — der Watchdog wäre
|
||
dekorativ. Derselbe Job erzeugt auch den Release-Zugangsschutz.
|
||
|
||
---
|
||
|
||
## 2. Build-Grundlage
|
||
|
||
### 2.1 `Directory.Build.props` mit `<Version>`
|
||
|
||
```xml
|
||
<Project>
|
||
<PropertyGroup>
|
||
<Version>1.0.0</Version>
|
||
</PropertyGroup>
|
||
</Project>
|
||
```
|
||
|
||
`pack-and-deploy` liest die Version aus der Hauptassembly und **bricht bei einer
|
||
Abweichung zu `--version` ab**. Der Grund ist unangenehm genug dafür: Wird `1.0.1`
|
||
als `1.0.2` veröffentlicht, aktualisiert die gesamte Installationsbasis, meldet
|
||
danach weiterhin `1.0.1`, hält das Release erneut für neu — und aktualisiert bei
|
||
jedem Start wieder.
|
||
|
||
Gleichzeitig entfallen die beiden hartkodierten `"1.0.0"` in den `Program.cs`;
|
||
sie werden durch `BuildInfo.Version` ersetzt.
|
||
|
||
### 2.2 SDK-Anbindung — entschieden, in zwei Stufen
|
||
|
||
`Deploymentcenter.Client` (`netstandard2.0;net8.0`) wird von `net10.0` problemlos
|
||
konsumiert. Seit SDK 2.5.0 liegt `BuildInfo.targets` im Paket selbst
|
||
(`build/`-Ordner, von NuGet automatisch importiert) — der frühere `<Import>` per
|
||
Cross-Repo-Pfad ist damit **nicht mehr nötig**. Das entscheidet die Frage
|
||
grundsätzlich zugunsten eines echten NuGet-Bezugs. Geprüft (2026-08-14): Weder
|
||
`https://dc.mhdf.de/nuget/` noch die Gitea-Package-Registry unter
|
||
`http://192.168.178.10:8418/api/packages/Richard/nuget/` führen aktuell ein
|
||
Paket `Deploymentcenter.Client` — es gibt also noch keine Quelle, gegen die eine
|
||
`PackageReference` auflösen könnte.
|
||
|
||
**Deshalb zweistufig:**
|
||
|
||
| Stufe | Weg | Zustand |
|
||
|---|---|---|
|
||
| **Jetzt (umgesetzt)** | Cross-Repo-`ProjectReference` (Muster Predictalytics), `Deploymentcenter.BuildInfo.targets` weiterhin per `<Import>` eingebunden — der pfadbasierte Import funktioniert laut Leitfaden unverändert weiter, auch wenn er nicht mehr nötig wäre | funktioniert sofort, keine neue Infrastruktur, keine Push-Entscheidung nötig |
|
||
| **Folgeschritt (offen)** | `dotnet pack` im Deploymentcenter-Repo, Push nach `.../api/packages/Richard/nuget/`, `NuGet.config` um Quelle `gitea` mit `packageSourceMapping` auf `Deploymentcenter.*` ergänzen, `<Import>` durch `<PackageReference Include="Deploymentcenter.Client" Version="2.5.1" />` ersetzen | braucht einen Push in ein fremdes Repo (Deploymentcenter) und Package-Registry-Zugangsdaten — bewusst nicht Teil dieses Durchlaufs |
|
||
|
||
Der Cross-Repo-Bezug bricht die Gitea-CI von IBKRTrader (der Runner checkt nur
|
||
dieses eine Repo aus) — siehe **P5**. Das ist mit der Wahl von Stufe 1 bewusst in
|
||
Kauf genommen, weil eine funktionierende lokale Integration wichtiger ist als eine
|
||
grüne CI, die eine noch nicht existierende Paketquelle voraussetzt.
|
||
|
||
### 2.3 `BuildInfo`
|
||
|
||
`Deploymentcenter.BuildInfo.targets` in die csproj von `IBKRTrader.Hosting`
|
||
importieren. Die Klasse entsteht in `$(RootNamespace)`, also `IBKRTrader.Hosting.BuildInfo`
|
||
— erreichbar ohne `using`. Liefert `Version`, `GitCommit`, `GitCommitShort`,
|
||
`BuildDateUtc`, `Channel`, `Summary`.
|
||
|
||
> **Erledigt mit 2.5.1** (vormals §8 D4): `BuildDateUtc` trug die volle Uhrzeit,
|
||
> `WriteOnlyWhenDifferent` griff dadurch nie, und jedes importierende Projekt wurde
|
||
> bei jedem Build neu übersetzt. Seit 2.5.1 ist das Feld tagesgenau — höchstens ein
|
||
> Neubau pro Tag. Vorbedingung: die Targets-Datei im Schwester-Repo muss auf 2.5.1
|
||
> oder neuer stehen (`git -C ../Deploymentcenter log -1 --oneline -- client-dotnet/Deploymentcenter.Client/Deploymentcenter.BuildInfo.targets`).
|
||
|
||
---
|
||
|
||
## 3. Konfiguration und gemeinsame Bausteine
|
||
|
||
### 3.1 `DcConfig` (kompiliert, nicht einstellbar)
|
||
|
||
```csharp
|
||
namespace IBKRTrader.Hosting.Deploymentcenter;
|
||
|
||
public static class DcConfig
|
||
{
|
||
public const string BaseUrl = "https://dc.mhdf.de"; // bewusst KEINE Einstellung
|
||
public const string ProductSlug = "ibkrtrader";
|
||
public const string MonitorGroup = "Trading";
|
||
public static string AppVersion => BuildInfo.Version;
|
||
public static string GitCommitShort => BuildInfo.GitCommitShort;
|
||
}
|
||
```
|
||
|
||
Die Basis-URL entscheidet, wohin die Lizenzprüfung geht und woher ausführbarer
|
||
Code kommt. Einstellbar wäre sie eine Einladung, die Anwendung auf einen
|
||
gefälschten Update-Server zu zeigen.
|
||
|
||
### 3.2 Neuer Abschnitt in `AppSettings`
|
||
|
||
Ergänzt `src/IBKRTrader.Core/Settings/AppSettings.cs` um
|
||
`DeploymentcenterSettings` (gleiches Muster wie `LoggingSettings`, mit
|
||
`Category`/`DisplayName`/`Description`, damit es in der Settings-Ansicht auftaucht):
|
||
|
||
| Feld | Vorgabe | Bedeutung |
|
||
|---|---|---|
|
||
| `ApiToken` | *(leer)* | `Authorization: Bearer`. Leer = kein Heartbeat, keine Fehlermeldung. `[PasswordPropertyText]` |
|
||
| `HeartbeatEnabled` | `true` | Dead-Man's-Switch |
|
||
| `MonitorSource` | *(Hostname)* | Monitorname im Dashboard |
|
||
| `MonitorInstance` | `default` | mehrere Instanzen (EU/US) desselben Dienstes |
|
||
| `HeartbeatIntervalSeconds` | `60` | `warning` nach 2×, `down` nach 4× |
|
||
| `ReportErrors` | `true` | Error/Fatal an den Fehler-Stream |
|
||
| `CheckUpdatesOnStart` | `true` | prüft nur, installiert nie von selbst |
|
||
| `UpdateChannel` | `prod` | `prod`, `beta`, `dev` |
|
||
| `LicenseKey` | *(leer)* | nur Rückfall; Regelfall ist der verschlüsselte Cache |
|
||
|
||
`ApiToken` und `LicenseKey` sind schutzbedürftig und laufen über
|
||
`SecretProtection` (AES-256-GCM at rest), wie die übrigen Geheimnisse des Projekts.
|
||
|
||
### 3.3 `DcApiClient`
|
||
|
||
Schlanker JSON-Client für Heartbeat und Fehler-Stream (Lizenz und Update bringen
|
||
ihren eigenen mit). Übernimmt von Predictalytics:
|
||
|
||
- `DcApiException` mit stabilem `error.code` aus dem Umschlag
|
||
`{"status":"error","error":{"code":"…"}}` — reagiert wird auf den Code, nie auf den Text.
|
||
- `IsPermanent` für `401`/`403`/`unauthorized`: danach wird nicht weiter versucht.
|
||
- Zeitgrenze 10 s. **Ein nicht erreichbares Deploymentcenter darf den Handel
|
||
niemals beeinflussen** — jeder Aufruf ist best effort.
|
||
|
||
---
|
||
|
||
## 4. Watchdog
|
||
|
||
Neuer Worker `DcHeartbeatWorker` in `IBKRTrader.Hosting`, registriert wie die
|
||
übrigen (`IWorker` + `AddHostedService`), damit er in der Worker-Ansicht
|
||
auftaucht und manuell auslösbar ist.
|
||
|
||
`POST /api/watchdog/v1/ping` alle *n* Sekunden mit `source`, `instance`,
|
||
`interval`, `os`, `version = BuildInfo.Version`, `group = "Trading"`.
|
||
|
||
### 4.1 `checks` — was IBKRTrader wirklich über sich weiß
|
||
|
||
Ein Heartbeat beweist nur, dass ein Timer läuft. Der klassische Fehlerfall dieses
|
||
Projekts ist ein anderer: der Prozess lebt, aber die TWS-Verbindung ist seit einer
|
||
Stunde tot und es kommen keine Kurse mehr.
|
||
|
||
| Check | Quelle | `ok` wenn |
|
||
|---|---|---|
|
||
| `db` | EF/`MySqlConnector`, `SELECT 1`, gedeckelt auf 5 s | Verbindung steht |
|
||
| `ibkr_gateway` | `IBKRGatewayService` | verbunden **oder** `UseTwsApi = false` (NullBroker ist ein gewollter Zustand, kein Fehler) |
|
||
| `market_data` | `IBKRMarketDataRepository` | letzter Tick jünger als *x* min — nur wenn TWS aktiv |
|
||
| `worker_engine` | `WorkerEngine` | kein Worker im Fehlerzustand |
|
||
|
||
Bewusst **kein** Check auf „Trading aktiv": `TradingEnabled = false` ist ein
|
||
gewollter Betriebszustand und würde den Monitor sonst dauerhaft auf `warning`
|
||
halten. Er wandert als Metrik mit.
|
||
|
||
### 4.2 `metrics`
|
||
|
||
`uptime_sec`, `trading_enabled` (0/1), `open_positions`, `orders_today`,
|
||
`account_value`, `workers_running`, `memory_mb`, `decision_journal_lag_sec`.
|
||
|
||
Serverseitig 14 Tage Verlauf mit Abweichung zum 7-Tage-Mittel desselben Monitors —
|
||
damit lässt sich „heute plötzlich 40 Orders statt 3" erkennen, ohne im
|
||
Deploymentcenter Schwellwerte für jedes Projekt zu pflegen.
|
||
|
||
### 4.3 Sauberes Beenden
|
||
|
||
Beim geordneten Herunterfahren (SIGTERM/systemd, Fenster schließen) geht ein Ping
|
||
mit `status: "stopped"` raus. Ohne ihn folgt wenige Minuten nach jedem
|
||
planmäßigen Neustart ein Fehlalarm. Der Aufruf gehört in `StopAsync`
|
||
**vor** das Beenden der übrigen Dienste und braucht eine eigene, kurze Zeitgrenze
|
||
(3 s), damit ein hängender Monitoring-Server das Herunterfahren nicht blockiert.
|
||
|
||
### 4.4 Hierarchie
|
||
|
||
Läuft der Daemon auf einem eigenen Host, gehört im WebUI unter
|
||
**WatchDog → System-Hierarchie** die *Übergeordnete Entität* auf diesen Host
|
||
gesetzt. Sonst erzeugt ein Maschinenausfall zwei Meldungen für ein Problem.
|
||
|
||
---
|
||
|
||
## 5. Lizenz
|
||
|
||
`LicenseGuard` in `IBKRTrader.Hosting`, aufgerufen aus `RunStartupChecks` —
|
||
nach der Zeitzone (Logausgaben) und **vor** dem Start der Worker.
|
||
|
||
- Produkt-Slug `ibkrtrader`, Hardware-ID v2 (`2:win:…` / `2:lin:…`).
|
||
- `LicenseClient.DefaultAppVersion = BuildInfo.Version` einmalig beim Start,
|
||
sonst trägt jede Installation in der Aktivierungsliste dieselbe `1.0.0`.
|
||
- Revalidierung zur Laufzeit alle 12 h; Warnung, wenn die Gnadenfrist in unter
|
||
48 h abläuft.
|
||
- **`LicenseConfig.SetStorageDirectory(AppPaths.ConfigFile("license"))`** —
|
||
ausdrücklich setzen. Die Vorgabe des SDK löst unter Linux über `$XDG_CONFIG_HOME`
|
||
bzw. `$HOME` auf; ein systemd-Dienst mit `User=ibkrtrader` und ohne Heimatverzeichnis
|
||
landet im Rückfall auf `AppContext.BaseDirectory/license`, also unter `/opt` —
|
||
dort ist nichts schreibbar, und es gäbe **keinen Offline-Cache**. Mit `AppPaths`
|
||
fügt sich die Ablage in die bereits vorhandene Verzeichnisauflösung des Projekts ein.
|
||
**Erledigt/entschärft mit 2.5.1** (vormals §8 D4): der Rückfall ohne `$HOME` zeigt
|
||
jetzt serverseitig auf `/var/lib/<slug>` statt ins Installationsverzeichnis — unser
|
||
eigenes explizites Setzen bleibt trotzdem die robustere Wahl und ist unabhängig vom
|
||
SDK-Rückfall.
|
||
⚠️ **Breaking Change 2.5.1:** `SetStorageDirectory` hängt jetzt selbst `<slug>/license`
|
||
an (vorher wurde der Pfad roh benutzt). Der Aufruf wird zu
|
||
`LicenseConfig.SetStorageDirectory(AppPaths.Config)` — **ohne** das eigene
|
||
`"license"`-Suffix, sonst entstünde `…/license/ibkrtrader/license`.
|
||
|
||
### 5.1 Kopflose Schalter
|
||
|
||
Konvention aus dem Lizenz-Leitfaden, im Daemon umzusetzen (das SDK bringt keine CLI mit):
|
||
|
||
```
|
||
IBKRTrader.Daemon --license-status
|
||
IBKRTrader.Daemon --license-set-key XXXXX-XXXXX-XXXXX-XXXXX-XXXXX
|
||
```
|
||
|
||
`--license-deactivate` **nicht** verdrahten: es verlangt den `shared_key` des
|
||
Servers, der gehört nicht in eine ausgelieferte Anwendung. Freigeben läuft über
|
||
das WebUI (Hardware-Liste → „Freigeben").
|
||
|
||
> **Geändert mit 2.5.0:** der `update-agent` nimmt den Lizenzschlüssel nicht mehr
|
||
> als `--license-key`-Argument entgegen — `ps` zeigt Prozessargumente jedem Nutzer
|
||
> der Maschine, dieselbe Begründung wie beim Crontab-Verzicht in UPGRADE §5. Der
|
||
> Weg ist jetzt die Umgebungsvariable `DC_LICENSE_KEY` (`LaunchUpdateAgent` setzt
|
||
> sie selbst, wenn `licenseKey` übergeben wird — kein eigener Code nötig). Unsere
|
||
> `--license-set-key`-Implementierung ruft also `LicenseClient.ValidateAsync`
|
||
> direkt auf und schreibt **nicht** `--license-key` in einen eigenen Aufruf des
|
||
> Agenten.
|
||
>
|
||
> Ebenfalls neu: `EnsureLicensedAsync()` auf `LicenseClient` — nimmt den
|
||
> zwischengespeicherten Schlüssel, fragt bei Bedarf über `ILicensePrompt` nach,
|
||
> prüft, fragt bei Ablehnung erneut. `allowPrompt:false` lehnt ohne Cache ab, statt
|
||
> im Daemon auf eine Eingabe zu warten, die nie kommt — **das ist für den Daemon
|
||
> die richtige Einstiegsmethode**, nicht das manuelle Zusammensetzen aus
|
||
> `TryGetCachedKey` + `ValidateAsync`.
|
||
|
||
### 5.2 Reaktion — hier weicht IBKRTrader bewusst vom Leitfaden ab
|
||
|
||
Die DC-Doku empfiehlt bei einem endgültigen Urteil `Environment.Exit(1)`.
|
||
**Für einen Dienst mit offenen Positionen ist das die falsche Reaktion.** Ein
|
||
abgelaufener Vertrag ist kein Grund, ein Portfolio unbeaufsichtigt zu lassen —
|
||
Stop-Loss- und Exit-Logik müssen weiterlaufen, sonst richtet die Lizenzdurchsetzung
|
||
mehr Schaden an als der unlizenzierte Betrieb.
|
||
|
||
Vorschlag — drei Stufen statt zwei:
|
||
|
||
| Ergebnis | Zustände | Reaktion |
|
||
|---|---|---|
|
||
| `IsValid` | `valid`, `valid_offline` | Normalbetrieb |
|
||
| `IsTransient` | `server_unavailable`, `cache_expired` | **kein Urteil, nur keine Auskunft.** Warnen, weiterlaufen, beim nächsten Durchlauf erneut fragen |
|
||
| Urteil | `revoked`, `expired`, `not_found`, `activation_limit`, `suspended`, `clock_rollback` | **Sperrbetrieb:** `TradingEnabled` hart auf `false`, keine neuen Einstiege, Risiko-/Exit-Worker und Buchhaltung laufen weiter, Meldung an Watchdog (`status: "warning"`) und Fehler-Stream. Beendet wird erst, wenn keine Position mehr offen ist — oder auf ausdrückliche Anweisung |
|
||
|
||
Bei der Desktop-Shell darf es beim Start weiterhin ein Lizenzfenster geben; der
|
||
**Daemon** darf niemals auf eine Eingabe warten.
|
||
|
||
---
|
||
|
||
## 6. Error-Reporting
|
||
|
||
Zwei Wege in `POST /api/errors/v1/report`, beide in `IBKRTrader.Hosting`:
|
||
|
||
1. **Senke am `LoggingService`.** `LoggingService.EntryWritten` existiert bereits
|
||
und ist genau dafür gedacht (die Oberfläche hängt schon dran). Ein
|
||
`DcErrorSink` filtert auf `AppLogLevel.Error` und meldet mit
|
||
`level: "error"`, `build: BuildInfo.Version`, `file`/`line` aus dem Stacktrace,
|
||
`context` mit `GitCommitShort` und `CorrelationId`/`SignalId`, wenn vorhanden.
|
||
Die `SignalId` mitzuschicken ist der eigentliche Gewinn: eine Fehlermeldung
|
||
lässt sich damit im Deploymentcenter direkt gegen das Entscheidungsjournal halten.
|
||
2. **Globale Handler** — `AppDomain.UnhandledException` (`level: "fatal"`,
|
||
**blockierend** gesendet, der Prozess ist gleich weg) und
|
||
`TaskScheduler.UnobservedTaskException`. Beide fehlen im Projekt heute
|
||
vollständig; sie müssen sowieso her.
|
||
|
||
### 6.1 Bremse
|
||
|
||
Verpflichtend, sonst verbrennt ein Fehler in einer Handelsschleife das
|
||
serverseitige Rate-Limit binnen Sekunden:
|
||
|
||
- höchstens 20 Meldungen pro Minute,
|
||
- derselbe Fehler höchstens alle 5 Minuten,
|
||
- bei `401`/`403` dauerhaft abschalten,
|
||
- bei `429` Intervall verdoppeln.
|
||
|
||
Betriebsbedingtes Rauschen gehört nicht in diese Bremse, sondern in die
|
||
**Ignore-Regeln** des WebUI: dort wird weitergezählt, aber nicht gemeldet — und
|
||
eine Alarmschwelle schlägt an, wenn ein bekannter Fehler plötzlich hundertmal so
|
||
oft auftritt.
|
||
|
||
> **Nicht melden:** alles, was Kontodaten, Positionen oder Schlüsselmaterial im
|
||
> Text führen könnte. Der Fehler-Stream ist eine externe Ablage. Vor dem Senden
|
||
> wird der Text durch dieselbe Maskierung geschickt, die schon für die Logs gilt.
|
||
|
||
---
|
||
|
||
## 7. UpdateService, Erstinstallation und Release-Pipeline
|
||
|
||
### 7.1 Prüfen, nicht installieren
|
||
|
||
`DcUpdateService.CheckAsync` beim Start (`GET /api/updateservice/v1/check`).
|
||
**Mit Zugangsdaten**, sonst gibt es seit 2.4 nur noch `401`:
|
||
|
||
```csharp
|
||
var key = settings.Deploymentcenter.LicenseKey is { Length: > 0 } k
|
||
? k
|
||
: LicenseClient.TryGetCachedKey(DcConfig.ProductSlug);
|
||
|
||
var check = await new UpdateClient().CheckForUpdateAsync(
|
||
baseUrl: DcConfig.BaseUrl,
|
||
projectId: DcConfig.ProductSlug,
|
||
currentVersion: BuildInfo.Version,
|
||
channel: settings.Deploymentcenter.UpdateChannel,
|
||
credentials: ReleaseCredentials.FromLicenseKey(key),
|
||
cancellationToken: ct);
|
||
|
||
if (check.Unauthorized) { /* Lizenzproblem, KEIN Netzwerkfehler — eigene Meldung */ }
|
||
```
|
||
|
||
Gefunden wird nur, installiert wird nichts von selbst. Ein Trading-Dienst, der
|
||
sich unangekündigt neu startet, ist keine akzeptable Vorgabe.
|
||
|
||
- Kein Update → Info ins Log.
|
||
- Update verfügbar → Warnung + Hinweis in der Shell.
|
||
- Kritisch → zusätzlich Dialog (Shell) bzw. Watchdog-Event (Daemon).
|
||
|
||
### 7.2 Anwenden
|
||
|
||
Über `UpdateClient.LaunchUpdateAgent(..., licenseKey: key)`; der Agent bekommt
|
||
`--restart`, `--wait-for-pid`, `--platform` von selbst, den Lizenzschlüssel seit
|
||
2.5.0 **nicht mehr** als `--license-key`-Argument, sondern über die
|
||
Umgebungsvariable `DC_LICENSE_KEY` (siehe §5.1). Vorher geht ein
|
||
`status: "stopped"` an den Watchdog.
|
||
|
||
**Vorbedingung, die die Anwendung selbst prüfen muss:** kein offener Auftrag,
|
||
keine laufende Order. Ein Update, das den Prozess zwischen Order-Absendung und
|
||
Statusrückmeldung abräumt, hinterlässt eine Order bei IBKR ohne Gegenstück im
|
||
Journal. Das Update wird also erst nach dem Handelsschluss bzw. bei leerem
|
||
Auftragsbuch angeboten.
|
||
|
||
> **Verschärft mit 2.5.0:** `exitCurrentApp: true` ist jetzt ausdrücklich als
|
||
> **harter Kill** dokumentiert — laufende `finally`-Blöcke, `IHostApplicationLifetime`
|
||
> und Destruktoren kommen nicht mehr zum Zug. Für IBKRTrader heißt das: Solange
|
||
> die Vorbedingung oben nicht sicher geprüft ist (offenes Auftragsbuch, laufende
|
||
> Buchungstransaktion), muss `exitCurrentApp: false` gesetzt werden. Der Agent
|
||
> wartet über `--wait-for-pid` ohnehin auf das Prozessende; das eigene
|
||
> Herunterfahren läuft dann über `host.StopAsync(...)` wie in `App/Program.cs`
|
||
> bereits vorhanden — **nicht** über `LaunchUpdateAgent` selbst. Zusätzlich
|
||
> `waitTimeoutSeconds` heraufsetzen, falls das geordnete Beenden (EF-Flush,
|
||
> Kestrel, Log-Flush, DB-Backup-Worker) länger als die Vorgabe von 60 s dauern
|
||
> kann — läuft die Zeit ab, bricht der Agent ohne Änderung ab.
|
||
|
||
Für den Linux-Dienst siehe [§8 D3](#8-befunde-und-offene-punkte) — dafür gibt es
|
||
im Deploymentcenter derzeit keinen fertigen Weg.
|
||
|
||
### 7.3 Was ausgeliefert wird — Ausschließen vs. Schützen
|
||
|
||
Das ist die Stelle, an der Updates in anderen Projekten schon Konfigurationen
|
||
zerstört haben. Für IBKRTrader gilt:
|
||
|
||
| Datei | Liste | Begründung |
|
||
|---|---|---|
|
||
| `settings.example.json` | *(im Paket)* | Vorlage mit Platzhaltern — Erstinstallation braucht sie |
|
||
| `settings.json` | `preservePatterns` | enthält DB-Zugang, DC-Token, Flex-Token. Muss ein Update überleben |
|
||
| `appsettings.Local.json` | `preservePatterns` | Verbindungszeichenfolge des Zielsystems |
|
||
| `master.key`, `openrouter.key` | `excludePatterns` | **darf nie ins Paket** |
|
||
| `Logs/**`, `Backups/**` | `excludePatterns` | Laufzeitdaten |
|
||
| `*.pdb`, `*.xml` | `excludePatterns` | Build-Artefakte |
|
||
| `update-agent*` | bewusst entscheiden | mitliefern = aktualisiert sich selbst mit |
|
||
|
||
`pack-and-deploy` warnt von sich aus bei Dateien, die nach Zugangsdaten aussehen.
|
||
Die Warnung ist ernst zu nehmen: Release-Pakete liegen zwar hinter einem
|
||
Zugangsschutz, aber jeder lizenzierte Kunde kann sie auspacken.
|
||
|
||
### 7.4 `setup.json`
|
||
|
||
Beschreibt der Erstinstallation, was abzufragen ist; wird ins Paket kopiert.
|
||
Entwurf für IBKRTrader:
|
||
|
||
```json
|
||
{
|
||
"schema": 1,
|
||
"displayName": "IBKRTrader",
|
||
"targets": [
|
||
{ "id": "app", "file": "settings.json", "location": "install", "format": "json" }
|
||
],
|
||
"fields": [
|
||
{ "key": "Database:Host", "label": "MySQL-Host", "type": "string" },
|
||
{ "key": "Database:Database", "label": "Datenbank", "type": "string" },
|
||
{ "key": "Database:User", "label": "Benutzer", "type": "string" },
|
||
{ "key": "Database:Password", "label": "Passwort", "type": "secret" },
|
||
{ "key": "IBKR:Host", "label": "TWS/Gateway", "default": "127.0.0.1" },
|
||
{ "key": "IBKR:Port", "label": "Port (Paper 4002 / Live 4001)", "type": "int", "default": "4002" },
|
||
{ "key": "Trading:ApplicationTimeZoneId", "label": "Betriebszeitzone", "default": "Europe/Berlin" },
|
||
{ "key": "Deploymentcenter:BaseUrl", "source": "detect:baseurl", "type": "url" },
|
||
{ "key": "Deploymentcenter:ApiToken", "label": "Token dieser Installation",
|
||
"source": "provision", "scopes": ["watchdog:ping", "bugtracker:report"] },
|
||
{ "key": "Deploymentcenter:MonitorSource", "source": "detect:hostname" }
|
||
]
|
||
}
|
||
```
|
||
|
||
Zwei Punkte dazu:
|
||
|
||
- **`location: "install"` ist nur unter Windows richtig** — und auch dort nur,
|
||
solange nicht nach `C:\Program Files` installiert wird. Siehe
|
||
[§8 P2](#8-befunde-und-offene-punkte); solange `AppPaths` nicht korrigiert ist,
|
||
passen `setup.json` und die tatsächliche Leseposition der Anwendung unter Linux
|
||
nicht zusammen. **Dieser Punkt blockiert die Erstinstallation und gehört vor §7.4.**
|
||
- Der Installer schreibt **Klartext**. `SecretProtection` muss also Klartext
|
||
unverändert durchreichen und beim ersten Speichern verschlüsseln — die Weiche
|
||
ist zu prüfen und im Test ausdrücklich als *aktiv genutzt* zu beschreiben, damit
|
||
sie nicht eines Tages als tot entfernt wird.
|
||
|
||
### 7.5 Release-Pipeline
|
||
|
||
`scripts/release.ps1` und `scripts/release.sh` sind per `curl` von
|
||
`/docs/release-template/` geholt (wie `release.md` empfiehlt, statt aus dem
|
||
lokalen Schwester-Repo kopiert — damit ist sichergestellt, dass die kanonische
|
||
Fassung im Projekt liegt) und **unverändert** gelassen. Nur
|
||
`scripts/release.config.json` ist ausgefüllt:
|
||
|
||
```json
|
||
{
|
||
"project": "ibkrtrader",
|
||
"csproj": "src/IBKRTrader.Daemon/IBKRTrader.Daemon.csproj",
|
||
"runtimes": ["win-x64", "linux-x64"],
|
||
"selfContained": true,
|
||
"setupJson": "setup.json"
|
||
}
|
||
```
|
||
|
||
**Entschieden: nur der Daemon, keine Zwei-Produkte-Aufteilung.** IBKRTrader hat
|
||
zwei Einstiegspunkte (`App` = Avalonia-Shell, `Daemon` = kopflos). Die Vorlage
|
||
nimmt aber genau **eine** `csproj` für alle Runtimes in der Liste — sie ruft
|
||
`dotnet publish <csproj> -r <rid>` in einer Schleife auf, es gibt keine
|
||
Möglichkeit, App für `win-x64` und Daemon für `linux-x64` in **demselben** Lauf
|
||
zu veröffentlichen, ohne das (laut Anleitung unveränderte) Skript anzufassen.
|
||
Ursprünglich in diesem Dokument vorgeschlagen war genau das ("Windows = App,
|
||
Linux = Daemon") — das ist mit der Vorlage so nicht umsetzbar und wurde beim
|
||
Umsetzen korrigiert.
|
||
|
||
`IBKRTrader.Daemon` ist der bewusste Kandidat: laut README „plattformneutral"
|
||
(`net10.0` ohne Plattformbindung), läuft kopflos auf beiden Zielsystemen und
|
||
ist ohnehin die für den Serverbetrieb vorgesehene Form (`deploy/README.md`).
|
||
Eine automatisierte Auslieferung der Avalonia-Shell (`App`) als
|
||
Windows-Desktop-Distribution ist damit **nicht** Teil dieses Durchlaufs — dafür
|
||
bräuchte es entweder einen zweiten Produkt-Slug (`ibkrtrader-app`, doppelte
|
||
Lizenz-/Update-Pflege) oder ein angepasstes Release-Skript. Zurückgestellt, bis
|
||
tatsächlich Bedarf an einer automatisierten Desktop-Auslieferung besteht — bis
|
||
dahin bleibt `dotnet run --project src/IBKRTrader.App` bzw. eine manuelle
|
||
`dotnet publish` der übliche Weg für die Shell.
|
||
|
||
Zugangsdaten kommen aus `DC_FTP_HOST` / `DC_FTP_USER` / `DC_FTP_PASS` / `DC_TOKEN`,
|
||
`.dc-tools/` und `artifacts/` sind in der `.gitignore`.
|
||
|
||
**`packager.config.json` (`excludePatterns`/`preservePatterns`) ist nicht Teil
|
||
dieses Durchlaufs.** Sie liegt "neben dem Werkzeug", also unter `.dc-tools/` —
|
||
das entsteht erst, wenn `pack-and-deploy` beim ersten echten Lauf von
|
||
`release.ps1`/`release.sh` selbst heruntergeladen wird (Prüfsummen-geprüft).
|
||
Ein erster Release-Lauf braucht `DC_FTP_*`/`DC_TOKEN` und ist damit eine
|
||
credentialierte, vom Betreiber auszulösende Aktion. **Sicherheitsnetz bis
|
||
dahin:** Der Packager bricht seit 2.5.0 ohnehin hart ab, wenn er im
|
||
Publish-Verzeichnis nach Zugangsdaten aussehende Dateien findet (Name *und*
|
||
Inhalt) — die in §7.3 beschriebenen Dateien (`settings.json`,
|
||
`appsettings.Local.json`) sind ohnehin gitignored und landen nur dann im
|
||
Publish-Output, wenn sie lokal im Projektverzeichnis liegen (`<None Update
|
||
Condition="Exists(...)">` in den `.csproj`-Dateien kopiert sie dann mit). Bricht
|
||
`release.ps1` mit einem Hinweis auf eine vermutete Zugangsdatei ab, ist das der
|
||
Packager, der genau das abfängt — kein Fehlschlag, sondern die Absicherung, die
|
||
2.5.0 dafür eingeführt hat.
|
||
|
||
---
|
||
|
||
## 8. Befunde und offene Punkte
|
||
|
||
> **Status 2026-08-23:** P1–P4 sind umgesetzt (Einzelheiten in §9). **P5 ist eingetreten**,
|
||
> P6 entscheidet sich erst beim ersten Release. Der ursprüngliche Vermerk von 2026-08-14:
|
||
>
|
||
> **Status 2026-08-14:** Alle sieben ursprünglich an den Deploymentcenter-Entwickler
|
||
> gemeldeten Befunde (D1–D7) sind mit den Versionen 2.5.0/2.5.1 behoben — bestätigt über
|
||
> `GET /api/updateservice/v1/changelog?since=2.4`. Der Abschnitt bleibt als Nachweis
|
||
> stehen, jeder Punkt trägt jetzt einen Fix-Vermerk. Die Projekt-Befunde (P1–P6) sind
|
||
> davon unabhängig und weiterhin offen — sie werden in diesem Durchlauf abgearbeitet.
|
||
|
||
### Am Projekt (P) — muss vor der Integration bereinigt werden
|
||
|
||
**P1 · Echte Zugangsdaten als Vorgabewerte im Quelltext.**
|
||
`src/IBKRTrader.Core/Settings/AppSettings.cs:14-35` trägt Host, Datenbankname,
|
||
Benutzer und **Passwort** einer produktiven MariaDB als Vorgabe. Die Datei ist
|
||
versioniert, die Werte stehen damit auch in der Git-Historie — `settings.json`
|
||
im `.gitignore` hilft dagegen nicht. Das Passwort ist als kompromittiert zu
|
||
behandeln und zu wechseln; die Vorgaben gehören auf Platzhalter. Zusätzlich
|
||
brisant, weil die Vorlage künftig im Release-Paket ausgeliefert wird.
|
||
|
||
> **Verschärft mit 2.5.0:** der Packager **bricht jetzt hart ab**, wenn er
|
||
> Zugangsdaten im Publish-Verzeichnis erkennt (Dateiname *und* Inhalt, u. a.
|
||
> gefülltes `Password=`) — vorher nur eine Warnung, die auf der Preserve-Liste
|
||
> sogar unterdrückt wurde. Damit ist P1 nicht mehr „sollte bereinigt werden",
|
||
> sondern **release-blockierend**: Ohne Fix lässt sich `settings.example.json`
|
||
> gar nicht mehr veröffentlichen.
|
||
|
||
**P2 · `AppPaths` fällt unter Windows auf einen Linux-Pfad zurück.**
|
||
`Configuration/AppPaths.cs:57-71`: ist das Binärverzeichnis nicht beschreibbar,
|
||
gilt der FHS-Rückfall `/etc/ibkrtrader`. Unter Windows löst .NET das zu
|
||
`C:\etc\ibkrtrader` auf. Genau dieser Fall tritt ein, sobald der DC-Installer
|
||
seinen Windows-Vorschlag `Programme\<slug>` benutzt — dann liegen `settings.json`,
|
||
`master.key` und `Logs/` in `C:\etc\`. Nötig: plattformabhängiger Rückfall
|
||
(`%ProgramData%\IBKRTrader` bzw. `%APPDATA%`).
|
||
|
||
**P3 · Keine globalen Ausnahmebehandler.** Ohne
|
||
`AppDomain.UnhandledException` und `TaskScheduler.UnobservedTaskException` gibt es
|
||
keine `fatal`-Meldungen — und heute auch keinen Logeintrag, wenn der Prozess
|
||
wegbricht. Unabhängig vom Deploymentcenter überfällig.
|
||
|
||
**P4 · Version an zwei Stellen hartkodiert.** `App/Program.cs:34` und
|
||
`Daemon/Program.cs:43`. Ohne `Directory.Build.props` bricht `pack-and-deploy` beim
|
||
ersten Release ab — zu Recht.
|
||
|
||
**P5 · CI kennt das Schwester-Repo nicht.** `.gitea/workflows/build.yml` checkt
|
||
nur IBKRTrader aus. Eine `ProjectReference` nach `..\..\..\..\Deploymentcenter\…`
|
||
macht den Build auf beiden Matrix-Zielen rot. Siehe die Entscheidung in §2.2.
|
||
|
||
> **Seit 2026-08-23 eingetreten, nicht mehr nur vorhergesagt.** Die
|
||
> `ProjectReference` ist mit der Integration auf `main` gelandet, die CI ist
|
||
> damit rot. Das war die bewusst in Kauf genommene Folge der Interimslösung aus
|
||
> §2.2 — lokal baut die Projektmappe, sobald das Schwester-Repo daneben liegt.
|
||
> Auflösung ist Schritt 10: SDK als NuGet-Paket in die Gitea-Registry, dann
|
||
> `PackageReference` statt Cross-Repo-Pfad.
|
||
|
||
**P6 · Konfiguration liegt im Installationsverzeichnis.** Unter Windows schreibt
|
||
`AppPaths` neben die Binärdatei. Das ist mit `preservePatterns` beherrschbar
|
||
(§7.3), muss aber beim ersten Release stimmen — ein Update, das `settings.json`
|
||
überschreibt, nimmt einer laufenden Installation Datenbank, Token und Flex-Zugang
|
||
gleichzeitig.
|
||
|
||
### Am Deploymentcenter (D) — gemeldet, Stand vor 2.5.0
|
||
|
||
**D1 · Der Lizenzschlüssel als Update-Zugang war nirgends vollständig erklärt.**
|
||
✅ **Behoben (2.5.1, Doku).** Laut Changelog explizit als „Lückenschluss" genannt:
|
||
„Woher die Anwendung den Lizenzschlüssel für den Update-Zugang nimmt
|
||
(`TryGetCachedKey`)". Für uns bleibt zusätzlich `EnsureLicensedAsync()` (neu in
|
||
2.5.0) relevant — siehe §5.1.
|
||
|
||
**D2 · Predictalytics wurde vom Zugangsschutz ausgesperrt.**
|
||
✅ **Bestätigt und behoben (2.5.1).** Der Changelog bestätigt den Verdacht wörtlich:
|
||
„bei Predictalytics ist genau das passiert". Fix: Zugangsschutz ist jetzt je
|
||
Produkt abschaltbar (WebUI → UpdateService → Zugangsschutz) — für Installationsbasen
|
||
ohne SDK 2.4 lässt sich das Produkt ausnehmen, mit Schlüssel neu ausliefern, danach
|
||
wieder einschalten. **Für IBKRTrader heißt das:** genau diese Falle von Anfang an
|
||
vermeiden, indem der erste ausgelieferte Build bereits `licenseKey` mitgibt (§7.5,
|
||
§9 Reihenfolge).
|
||
|
||
**D3 · Für einen systemd-Dienst gab es keinen dokumentierten Update-Weg.**
|
||
✅ **Behoben (2.5.1, Doku).** Laut Changelog ergänzt: „wie ein laufender
|
||
systemd-Dienst aktualisiert wird". Bleibt in §7.2/§8-P laufend zu beobachten, ob
|
||
die neue Beschreibung mit unserem tatsächlichen Vorgehen (`--target-dir`,
|
||
Dienstbenutzer-Rechte) übereinstimmt — bei der Umsetzung von Aufgabe „Update-Prüfung
|
||
mit ReleaseCredentials" gegenprüfen.
|
||
|
||
**D4 · `LicenseConfig.GetStorageDirectory`/`SetStorageDirectory` war für Dienste
|
||
unglücklich.** ✅ **Behoben, mit Breaking Change (2.5.1).** Zwei Korrekturen:
|
||
(a) Rückfall ohne `$HOME` zeigt jetzt auf `/var/lib/<slug>` statt ins
|
||
Installationsverzeichnis. (b) `SetStorageDirectory` hängt jetzt selbst
|
||
`<slug>/license` an, statt den Pfad roh zu verwenden — **das ändert unseren
|
||
Aufruf**, siehe §5 (`AppPaths.Config` statt `AppPaths.ConfigFile("license")`).
|
||
|
||
**D5 · `BuildInfo.targets` verhinderte inkrementelle Builds.**
|
||
✅ **Behoben (2.5.1).** `BuildDateUtc` ist jetzt tagesgenau statt mit voller Uhrzeit,
|
||
`WriteOnlyWhenDifferent` greift wieder. Siehe §2.3.
|
||
|
||
**D6 · Die Empfehlung „bei einem Urteil beenden" passte für handelnde Systeme nicht.**
|
||
✅ **Behoben (2.5.1, Doku)** — als Lückenschluss genannt: „was statt
|
||
`Environment.Exit(1)` zu tun ist, wenn Beenden die gefährlichere Option wäre".
|
||
Unser Sperrbetrieb-Ansatz (§5.2) bleibt die konkrete Umsetzung für IBKRTrader.
|
||
**Zusätzlich, über D6 hinausgehend:** 2.5.0 dokumentiert `exitCurrentApp: true`
|
||
jetzt als harten Kill ohne `finally`/Lifecycle-Hooks — praktisch relevanter für uns
|
||
als die Lizenz-Frage allein, siehe §7.2.
|
||
|
||
**D7 · Kleinigkeiten** (README-Verweis auf `AGENT_RELEASE_GUIDE.md`, Rate-Limits
|
||
nicht durchgängig beziffert). ✅ **Behoben (2.5.1, Doku)** — Changelog nennt
|
||
„die Drosselungsgrenzen aller Endpunkte" als ergänzt; `docs/README.md` führt jetzt
|
||
`/docs/release.md` und den Changelog-Endpunkt im Index.
|
||
|
||
---
|
||
|
||
## 9. Reihenfolge
|
||
|
||
| Schritt | Inhalt | Abhängig von | Stand |
|
||
|---|---|---|---|
|
||
| 0 | **P1** Zugangsdaten wechseln, Vorgaben auf Platzhalter | — | ✅ Vorgaben sind Platzhalter. **Die Rotation des geleakten Passworts steht weiterhin aus** (Nutzer-Aktion) |
|
||
| 1 | **P2** `AppPaths`-Rückfall plattformabhängig, **P4** `Directory.Build.props` | — | ✅ 2026-08-23 |
|
||
| 2 | SDK-Bezug Stufe 1: Cross-Repo-`ProjectReference` (§2.2) | — | ✅ 2026-08-23, inkl. Prüf-Target mit lesbarer Fehlermeldung |
|
||
| 3 | `DcConfig`, `DeploymentcenterSettings`, `DcApiClient`, `BuildInfo` | 1, 2 | ✅ 2026-08-23 |
|
||
| 4 | **P3** globale Handler + Fehler-Stream | 3 | ✅ 2026-08-23 (`DcCrashHandlers`, `DcErrorSink`, `DcErrorReporter`) |
|
||
| 5 | Watchdog-Heartbeat inkl. `stopped` | 3 | ✅ 2026-08-23 (`DcHeartbeatWorker`) |
|
||
| 6 | Lizenz mit Sperrbetrieb statt Abbruch (§5.2), `EnsureLicensedAsync` | 3 | ✅ 2026-08-23 (`LicenseGuard`) |
|
||
| 7 | Update-Prüfung **mit** `ReleaseCredentials`, `exitCurrentApp:false` bei offenem Zustand | 6 (braucht den Schlüssel) | 🔶 Die **Prüfung** läuft beim Start. Das **Anwenden** ist es nicht: `LaunchAgent` ist fertig, aber kein Aufrufer fährt danach geordnet herunter |
|
||
| 8 | `setup.json`, `preserve`/`exclude`, Release-Pipeline (Vorlage per `curl` von `/docs/release-template/`) | 1–7 | ✅ 2026-08-23 (`setup.json`, `scripts/release.*`). **Noch nie ausgeführt** – das erste Release steht aus |
|
||
| 9 | Bugtracker-Baustein nach `AGENTS.md` / `.agents/rules` | Projekt-Slug angelegt | ⬜ offen — Projekt `ibkrtrader` muss im WebUI erst angelegt werden |
|
||
| 10 | **Folgeschritt, nicht Teil dieses Durchlaufs:** `Deploymentcenter.Client` als NuGet-Paket in die Gitea-Registry pushen, `<Import>` durch `PackageReference` ersetzen, Gitea-CI (**P5**) auf den Paketbezug umstellen | 2 | ⬜ offen — siehe §2.2 |
|
||
|
||
Schritt 7 und 8 hängen zusammen: **der erste ausgelieferte Build muss die
|
||
Zugangsdaten schon mitbringen.** Für ein Produkt, das noch nie veröffentlicht hat,
|
||
entsteht `/releases/ibkrtrader/` mit dem ersten Upload und wird vom nächsten
|
||
`tick.php`-Lauf geschützt — es gibt kein Zeitfenster, das SDK danach nachzurüsten.
|
||
Wird Schritt 7 übersprungen, fällt die Tür hinter dem ersten Release zu (genau der
|
||
Fall, der laut Changelog bei Predictalytics bereits eingetreten ist, siehe D2).
|
||
|
||
Schritt 9 (Bugtracker) setzt voraus, dass das Projekt `ibkrtrader` im
|
||
Deploymentcenter-WebUI angelegt ist und ein Token mit `bugtracker:report`
|
||
vorliegt — beides serverseitige Handarbeit, die dieser Durchlauf nicht auslösen
|
||
kann. Wird zurückgestellt.
|