# Kopfloser Betrieb auf Linux Der Dienst `IBKRTrader.Daemon` fährt Trading-Kern, Worker, Accounting, Supervisor, REST-API und MCP-Light **ohne Oberfläche**. Er nutzt denselben Host-Aufbau wie die Desktop-Shell (`IBKRTrader.Hosting`), damit beide Varianten nicht auseinanderlaufen. ## Veröffentlichen ```bash dotnet publish src/IBKRTrader.Daemon -c Release -r linux-x64 --self-contained false -o out/ ``` Ergebnis: ~11 MB, ein ELF-Launcher `IBKRTrader.Daemon`, **keine Windows-Abhängigkeiten**. Mit `--self-contained true` entfällt die Runtime-Installation auf dem Zielhost (dann ~80 MB). ## Voraussetzungen auf dem Host | Paket | Wofür | Pflicht? | |---|---|---| | `dotnet-runtime-10.0` | Laufzeit | ja (außer bei `--self-contained`) | | `libicu` | Kulturen und Zeitzonen-ID-Umrechnung | **ja – siehe Warnung unten** | | `fonts-dejavu-core` | PDF-Export des Accounting-Moduls | nur für den PDF-Export | | `mariadb-client` | `mariadb-dump` für den BackupWorker | nur fürs DB-Backup | > **ICU ist nicht optional.** Ohne ICU (bzw. mit `InvariantGlobalization=true`) passieren zwei > Dinge – beide **lautlos**, ohne Fehlermeldung: > 1. `AppTimeZone` kann Windows-Zeitzonen-IDs nicht mehr auflösen und fällt auf die > Systemzeitzone zurück, im Container also meist UTC. Genau die Verschiebung von > Buchungszeitstempeln, die wir beseitigt haben. > 2. Der PDF-Export formatiert Beträge fest gegen `de-DE`. Ohne ICU liefert > `CultureInfo.GetCultureInfo("de-DE")` die invariante Kultur – aus `1.234,56` wird > `1,234.56`, in einem Dokument, das als prüfbare Aufstellung gilt. > > Der Daemon setzt deshalb ausdrücklich `InvariantGlobalization=false`. ## Verzeichnisse Die Anwendung schreibt in drei Verzeichnisse. Aufgelöst wird in dieser Reihenfolge: 1. Umgebungsvariable — `IBKRTRADER_CONFIG_DIR`, `IBKRTRADER_DATA_DIR`, `IBKRTRADER_LOG_DIR` 2. Neben der Binärdatei, **wenn dort geschrieben werden darf** (Entwicklung, portable Installation) 3. Sonst FHS — `/etc/ibkrtrader`, `/var/lib/ibkrtrader`, `/var/log/ibkrtrader` | Verzeichnis | Inhalt | |---|---| | Config | `settings.json`, `master.key`, `openrouter.key` | | Data | `Backups/` | | Logs | Textlog je Modul, JSONL je Tag | Beim Start steht die tatsächliche Ablage im Log (`Ablage: config=…, data=…, logs=…`). ## Einrichtung ```bash sudo useradd --system --no-create-home --shell /usr/sbin/nologin ibkrtrader sudo mkdir -p /opt/ibkrtrader /etc/ibkrtrader /var/lib/ibkrtrader /var/log/ibkrtrader sudo chown -R ibkrtrader:ibkrtrader /var/lib/ibkrtrader /var/log/ibkrtrader /etc/ibkrtrader sudo chmod 750 /etc/ibkrtrader ``` Master-Key ablegen (**nicht** in die Unit-Datei – die ist für alle lesbar): ```bash sudo install -o ibkrtrader -g ibkrtrader -m 600 /dev/null /etc/ibkrtrader/master.key ``` Der Dienst warnt beim Start, wenn `master.key` oder `openrouter.key` für Gruppe oder andere zugänglich sind — Windows-ACLs übertragen sich beim Kopieren auf einen Linux-Host nicht. ## Dienst einrichten ```bash sudo cp deploy/ibkrtrader.service /etc/systemd/system/ sudo systemctl daemon-reload sudo systemctl enable --now ibkrtrader journalctl -u ibkrtrader -f ``` ## Vor dem ersten Start prüfen ```bash /opt/ibkrtrader/IBKRTrader.Daemon --check ``` Baut den Host, fährt alle Startprüfungen (Zeitzone, Ablageorte, Master-Key, DB-TLS) und beendet sich — **ohne** Worker zu starten oder eine Verbindung zur Börse aufzubauen. Geeignet für Deployment-Skripte und CI. ```bash /opt/ibkrtrader/IBKRTrader.Daemon --db-version ``` Gibt die MariaDB-Serverversion aus (für das EF-`ServerVersion`-Pinning). ## Betriebszeitzone `Trading.ApplicationTimeZoneId` in `settings.json`, IANA-Schreibweise: | Instanz | Wert | |---|---| | EU | `Europe/Berlin` | | US | `America/New_York` | **Vor den ersten Trades festlegen und danach nicht mehr ändern.** Ein Wechsel verschiebt rückwirkend die Tagesgrenzen von Logs, Berichten und Buchungsperioden. Gespeichert wird immer UTC — nur so bleiben die Daten beider Instanzen vergleichbar. ## Was der Daemon *nicht* löst Das **IB Gateway** ist eine Java-Anwendung mit Oberfläche. Für den Dauerbetrieb ohne Bildschirm braucht es IBC plus Xvfb, dazu die Behandlung des täglichen Neustarts und des 2FA-Handlings — eine eigene Baustelle, unabhängig von diesem Dienst. Alternative: das Gateway bleibt auf dem Windows-Rechner, der Linux-Dienst verbindet sich über Port 4002. Dann muss in der TWS-Konfiguration die erlaubte Client-IP eingetragen sein; der TWS-API-Verkehr ist **unverschlüsselt** und gehört nicht über ein unvertrautes Netz. ## Anwendungssymbol Beide Betriebsformen tragen dasselbe Symbol. Die Quelle ist `icon.ico` im Projektstamm, daneben liegt die verlustfrei daraus entnommene `icon.png` (256×256, RGBA). | Wo es auftaucht | Woher es kommt | |---|---| | Fenster, Fensterliste, Alt-Tab der Shell | `Window.Icon`, gesetzt in `Shell/AppIcon.cs` aus der eingebetteten `icon.png` | | Programmdatei unter Windows (Explorer, Task-Manager) | `` in beiden `.csproj` – dort ist `.ico` Pflicht | | Anwendungsmenü und Dock unter Linux | `deploy/ibkrtrader.desktop` samt installiertem Symbol | | Watchdog-Dashboard des Deploymentcenters | **nicht** aus der Anwendung – im WebUI je Monitor hochzuladen | > **Warum zwei Dateien.** Für die Windows-Programmdatei ist `.ico` ein Pflichtformat – es ist eine > PE-Ressource, kein Anzeigeformat. Für `Window.Icon` wäre es der falsche Weg: Der X11-Rücken von > Avalonia dekodiert über Skia, und Skia kennt ICO nicht. Unter Windows fiele das nie auf, auf dem > Linux-Server bliebe das Fenster ohne Symbol – also genau dort, wo es gebraucht wird. Wird das > Symbol ausgetauscht, gehören **beide** Dateien getauscht; `icon.png` entsteht aus dem > 256×256-Eintrag der `.ico`. `--smoke-ui` prüft das Symbol mit: `[OK] Anwendungssymbol (Assets/icon.png)`. Schlägt es fehl, ist die `AvaloniaResource`-Zeile in `IBKRTrader.App.csproj` verlorengegangen oder die PNG beschädigt. Zur Laufzeit bleibt ein fehlendes Symbol folgenlos – die Oberfläche startet ohne. Genau deshalb muss es im Rauchtest auffallen und nicht erst auf dem Server. ### Desktop-Eintrag Nur für die Shell (`IBKRTrader.App`) auf einem Linux-Rechner mit Arbeitsumgebung. Der Dienst braucht ihn nicht – er zeigt kein Fenster. ```bash sudo install -m 644 icon.png /usr/share/icons/hicolor/256x256/apps/ibkrtrader.png sudo install -m 644 deploy/ibkrtrader.desktop /usr/share/applications/ sudo gtk-update-icon-cache -f /usr/share/icons/hicolor ``` `Exec=` in der `.desktop` zeigt auf `/opt/ibkrtrader-app/IBKRTrader.App` und ist an die tatsächliche Installation anzupassen. `StartupWMClass=IBKRTrader.App` verbindet die Fenster der laufenden Anwendung mit dem Eintrag – der Wert ist die WM_CLASS, die Avalonia unter X11 setzt (am 2026-09-01 auf der Test-VM mit `xprop WM_CLASS` nachgemessen, nicht geraten). > **Nachgemessen am 2026-09-01.** Auf der Test-VM (Xvfb `:96` mit metacity) trägt das Fenster der > Shell `_NET_WM_ICON(CARDINAL) = Icon (128 x 128)` — das Symbol kommt also bis nach X11 durch; > Avalonia skaliert dabei auf 128×128 herunter. `desktop-file-validate` beanstandet die > `.desktop` nicht, und die drei Installationsbefehle oben laufen unverändert durch. > > Ein Fenstermanager ist für diese Prüfung **Voraussetzung**: Unter blankem Xvfb fehlt > `_NET_SUPPORTED` auf dem Wurzelfenster, und Avalonia setzt dann gar keine EWMH-Eigenschaften — > weder `_NET_WM_ICON` noch `_NET_WM_PID` oder `_NET_WM_NAME`, während die ICCCM-Eigenschaften > (`WM_NAME`, `WM_CLASS`, `WM_PROTOCOLS`) gesetzt sind. Wer ohne Fenstermanager misst, hält das > für einen Fehler der Anwendung. Auf der Test-VM liegt dafür seit dem 2026-09-01 metacity bereit.