Files
remmina-mac/docs/ERRORS.md
T

21 KiB

Fehlerprotokoll (ERRORS.md)

Alle Fehler, die beim Erstellen / Bauen / Releasen von RemminaMac aufgetreten sind — mit Ursache, Fix und Status. Wird bei jedem Build fortgeschrieben.

Stand: 2026-09-21 · Version 0.1.0 · macOS arm64 · Swift 5.9+ · Xcode 15+

Übersicht

# Zeitpunkt Phase Fehler Schwere Status
1 2026-09-21 swift build (debug) WindowGroup<some View> has no member 'windowTitle' blockierend ✅ behoben
2 2026-09-21 swift build -c release keine Fehler, nur lange Link-Zeit (~40 s) Info ✅ ok
3 2026-09-21 Release-Skript dist/ war nicht in .gitignore → Binary drohte committet zu werden mittel ✅ behoben (ignoriert)
4 2026-09-21 Gitea-Upload GET /api/v1/user → 401 invalid username, password or token mit alten Credentials blockierend (Push/Release) ✅ behoben — neue Credentials ok, Push + Release v0.1.0 hochgeladen
5 2026-09-21 App-Bundle + DMG keine Fehler: build-app.sh (Signatur ok), build-dmg.sh (UDZO, 200K), Mount-Verifikation ok (App + Applications/Programme-Links) Info ✅ ok
6 2026-09-21 SSH verbinden kein Terminal öffnete sich: SSHService.openInTerminal nutzte NSAppleScript → scheitert still (keine NSAppleEventsUsageDescription, kein Error-Handling) blockierend ✅ behoben — In-App-PTY-Terminal (Dracula) + .command-Fallback
7 2026-09-21 ANSI-Parser (Test) feed() flusht Chunk erst NACH parseCSI → Text bekam den neuen statt alten Stil (rot mit fg=nil statt 1) mittel ✅ behoben — Flush vor Stilwechsel
8 2026-09-21 SSH-Session stirbt instant ohne Meldung stderr hing mit am PTY: macOS verwirft ungelesene Master-Daten bei Slave-Close → „Code 255" ohne Text blockierend ✅ behoben — stderr→Pipe, TCP-Vorab-Check, Fehler-Banner
9 2026-09-21 swift build Result<Void, String>: String conformt nicht zu Error (Folgefehler: DispatchQueue-Overload) blockierend ✅ behoben — Completion als (String?) -> Void
10 2026-09-21 Icon-Generator (RenderIcon.swift) 3 Swift-Fehler: guard let x = f() ?? g() (nicht optional), Int * CGFloat, falscher Preview-Pfad klein ✅ behoben
11 2026-09-21 Passwort-Feature Wunsch: Passwort + Keys speichern UND nutzen (bisher nur gespeichert, nie benutzt) Feature ✅ umgesetzt — Auto-Login via sshpass, sonst interaktiv
12 2026-09-21 „Immer Verbindung falsch" User-Report ohne Details (Ziel/Credentials unbekannt) offen ⬜ Teils: Editor-Validierung + kopierbarer Fehlerbericht; warte auf Bericht
13 2026-09-21 Permission denied trotz richtigem Passwort Text-Ersetzung im Eingabefeld verfälscht Sonderzeichen hoch ✅ behoben — gehärtetes Eingabefeld (alle Ersetzungen aus, 9/9 Tests)
14 2026-09-21 Terminal geht, App nicht (gleiches Passwort) SSH-Agent-Socket fehlt GUI-Prozessen → evtl. Key-Login in Terminal hoch ✅ App reicht Agent jetzt durch; Gegenprobe beim User offen
15 2026-09-21 Crash bei Tippen + Senden Ursache unklar (headless nicht reproduzierbar) kritisch ⬜ Gehärtet (v0.5.3); Crash-Log angefragt
16 2026-09-22 Profil-Änderungen wirkungslos Session-Cache ignorierte Host-/Passwort-/Key-Korrekturen hoch ✅ behoben — Fingerprint-Reconfig (9/9 Tests)
17 2026-09-22 rzsv: Vorab-Check Timeout Port 22 wird serverseitig gedroppt (443 ok) — kein App-Fehler extern ⬜ Server-Seite: fail2ban/VPN/Firewall prüfen
18 2026-09-22 Host-Key-Yes/No unmöglich ssh ohne Controlling-Terminal: /dev/tty ENXIO → stummer Abbruch kritisch ✅ ctty-run-Helper (Double-Fork) + Fingerprint-Popup
19 2026-09-22 Popup leer (s. Screenshot) if let im Sheet nimmt Empty-Zweig ohne Update-Pfad mittel ✅ .sheet(item:) + Keyscan im Hintergrund

#1 — windowTitle existiert nicht auf WindowGroup

Phase: erster swift build Log (gekürzt):

Sources/RemminaMac/RemminaMacApp.swift:14:10: error: value of type 'WindowGroup<some View>' has no member 'windowTitle'
14 |         .windowTitle("RemminaMac")

Ursache: windowTitle(_:) ist kein Modifier für WindowGroup. Der Fenstertitel wird im WindowGroup-Init übergeben. Fix: Sources/RemminaMac/RemminaMacApp.swift:

// vorher (falsch):
WindowGroup { ... }.windowTitle("RemminaMac")
// nachher (korrekt):
WindowGroup("RemminaMac") { ... }

Verifikation: swift build → Build complete! (1.31s). Status: ✅ behoben, kein Re-Check offen.

#2 — Release-Build langsam, aber fehlerfrei

Phase: bash scripts/build-release.sh 0.1.0 → swift build -c release Log:

Building for production...
[4/5] Linking RemminaMac
Build complete! (40.54s)

Ursache: keine — Release-Optimierung + Link brauchen beim ersten Mal ~40 s. Fix: keiner nötig. Bei wiederholten Builds ist es schneller (Cache in .build/). Artefakte:

dist/RemminaMac-0.1.0-macos-arm64.zip (186K)
dist/RemminaMac-0.1.0-macos-arm64.zip.sha256 (c476bb3c…)

Status: ✅ ok.

#3 — dist/ fast committet

Phase: Release-Vorbereitung Ursache: dist/ enthielt ZIP + entpacktes Binary, .gitignore kannte es noch nicht. Fix: .gitignore ergänzt:

.build/
dist/
*.zip
*.sha256

Verifikation: git status --short zeigt kein dist/ mehr. Status: ✅ behoben.

Vorlage für neue Fehler

## #N — Titel
**Phase:** ...
**Log:** ...
**Ursache:** ...
**Fix:** ...
**Verifikation:** ...
**Status:** ⬜ offen / ✅ behoben

Wie Fehler hierher kommen

  • Jeder swift build / swift build -c release / Skript-Lauf mit Fehler → Log-Ausschnitt + Exit-Code hier eintragen.
  • Gitea-Push/Release-Fehler (z. B. 401, Repo existiert schon, Asset-Upload schlägt fehl) ebenfalls hier dokumentieren.
  • Neue Tests: AnsiParser per swiftc-Harness (/tmp, SGR/CRLF/Clear/Map), PTYSession per Smoke-Test (/bin/echo + ssh -G durchs echte PTY).

#6 — SSH-Klick öffnet kein Terminal (User-Bugreport)

Phase: Laufzeit, SSH-Profil → Verbinden Ursache: Services/ConnectionServices.swift nutzte NSAppleScript(tell application "Terminal" to do script …). Ohne NSAppleEventsUsageDescription und ohne Error-Auswertung scheitert das still — es passiert schlicht nichts. Fix:

  • Neu: Services/PTYSession.swift — echte PTY-Session (posix_openpt, ssh mit TTY), TerminalManager pro Profil (überlebt Tab-Wechsel).
  • Neu: Views/EmbeddedTerminalView.swift — In-App-Terminal im Dracula-Theme (Services/TerminalTheme.swift, Services/AnsiParser.swift), Auto-Connect per onAppear, Eingabezeile + Passwort-Modus + ^C/^D.
  • SessionView bettet das Terminal ein (min. 380 pt), ContentView.connect() öffnet nur noch den Tab.
  • Extern-Fallback umgebaut auf .command-Datei via NSWorkspace (keine Automation-Rechte nötig). Verifikation: swift build ok; Parser-Harness 8/8; PTY-Smoke (/bin/echo → Output + Exit 0); ssh -G example.com durchs PTY ok. Status: ✅ behoben.

#8 — Terminal verbindet nicht, „direkt gekappt" (User-Bugreport)

Phase: Laufzeit, SSH-Profil → Tab öffnet, sofort „Sitzung beendet (Code 255)." — ohne jede Meldung Reproduktion (Harness, exakter App-Pfad ssh -p 1 nobody@127.0.0.1):

STATE: done(exitCode: Optional(255), message: "Sitzung beendet (Code 255).")
TEXT: "\n\n— Sitzung beendet (Code 255). —\n"   # <- Fehlermeldung fehlt!

Ground Truth (Python-pty): Selbst /bin/echo hello liefert b'', wenn man erst NACH Child-Exit liest; bei sofortigem Lesen kommt b'hello-py\r\n'. → macOS verwirft ungelesene PTY-Master-Daten beim Slave-Close (danach EIO). Unser DispatchSourceRead verliert dieses Rennen bei Instant-Exit-Kindern — und ssh schreibt ALLES (inkl. „Connection refused") nach stderr, das mit am PTY hing. Fix (Services/PTYSession.swift, Services/TCPCheck.swift, Views/EmbeddedTerminalView.swift):

  • stderr → Pipe (gepufferte Daten überleben Child-Exit garantiert), stdin/stdout bleiben am PTY (TTY erhalten für interaktive Prompts)
  • finalFlush (non-blocking Rest-Drain) + Epoch-guarded Lifecycle (kein State-/FD-Durcheinander bei Trennen→Neuverbinden)
  • TCP-Vorab-Check (Network.framework, 4 s Timeout) mit deutschen Klartext-Meldungen (refused/DNS/Timeout/Port)
  • Rotes Fehler-Banner mit letzter Meldung + Diagnose- und Erneut-Buttons Verifikation: Refused-Harness zeigt jetzt ssh: connect to host 127.0.0.1 port 1: Connection refused (PASS); Roundtrip (cat) PASS; ssh -G PASS; TCP-Harness (closed/open/empty) PASS. Status: ✅ behoben.

#9 — Build: Result<Void, String> ungültig

Log: error: type 'String' does not conform to protocol 'Error' (+ Folgefehler an DispatchQueue.main.async) Fix: TCPCheck.check nutzt (String?) -> Void (nil = ok, sonst deutsche Meldung). Status: ✅ behoben.

#11 — Passwort speichern UND benutzen (User-Wunsch)

Stand: Passwort/Key-Passphrase wurden in der Keychain gespeichert, aber nie zum Einloggen benutzt (ssh fragte immer interaktiv). Umsetzung:

  • SSHService.loginSecret() (Profil-Passwort → Key-Passphrase), sessionConfig() (rein, testbar): mit Secret + sshpass → sshpass -e ssh/sftp … + SSHPASS-Env (nie Befehlszeile/Logs); sonst direkter Aufruf + interaktiver Prompt.
  • PTYSession.customEnv + autoLogin-Flag, TerminalManager verdrahtet alles; Status zeigt „Auto-Login via Keychain".
  • Editor: Gespeichert-Anzeige (✓), Löschen-Button, sshpass-Hinweis; Titel-Bug (UUID-Vergleich mit neuem Random-Objekt) via isNew ersetzt. Verifikation: Config-Harness 9/9 (inkl. „Passwort nie in Befehlszeile"); echter Keychain-Roundtrip 7/7 (save/load/delete + Priorität + Fallback). Hinweis: Ohne sshpass (brew install sshpass) bleibt es beim interaktiven Prompt — hier auf dem Build-Mac ebenfalls nicht installiert, getestet wurde der interaktive Pfad + Logik. Status: ✅ umgesetzt.

#10 — Icon-Generator Buildfehler

Log: guard let auf nicht-optionalem Ausdruck (?? unwrapt schon); Int * CGFloat; Preview lag in .iconset/ statt /tmp/. Fix: ??-Fallback direkt zuweisen; CGFloat(...); Pfad korrigiert. Status: ✅ behoben. Icon: Resources/AppIcon.icns (per scripts/generate-icon.sh reproduzierbar), Vorschau docs/icon-preview.png.

#7 — ANSI-Parser: Text bekam falschen Stil

Phase: swiftc-Harness während #6 Log: text("rot", fg: nil) statt fg: 1 Ursache: Chunk wurde erst nach dem SGR-Wechsel geflusht. Fix: Services/AnsiParser.swift — flush() vor parseCSI. Verifikation: Harness 8/8 grün. Status: ✅ behoben.

#4 — Gitea-Upload: 401 trotz erreichbarem Server

Phase: POST /api/v1/user/repos + GET /api/v1/user auf https://git.sysdaemon.xyz (Version 1.27.3, per /api/v1/version bestätigt) Log:

GET /user -> 401
{"message":"invalid username, password or token","url":"https://git.sysdaemon.xyz/api/swagger"}

Getestet mit Login x3 und x3@sysdaemon.xyz — beide 401. Kein Quote-/Encoding-Problem (Basic-Auth via Python, kein Shell-Escaping). Mögliche Ursachen:

  1. Passwort falsch/ablaufen bzw. Sonderzeichen-Typo
  2. Account braucht App-Token statt Passwort (Gitea: User → Einstellungen → Anwendungen → Token erzeugen)
  3. Falscher Owner-/Repo-Name Fix (manuell, sobald Credentials stimmen):
cd RemminaMac
git remote add origin https://<user>@git.sysdaemon.xyz/<owner>/remmina-mac.git
git push -u origin main
export GITEA_HOST="https://git.sysdaemon.xyz" GITEA_OWNER="<owner>" GITEA_REPO="remmina-mac" GITEA_TOKEN="<token>" VERSION="0.1.0"
./scripts/publish-gitea-release.sh

Oder mit Token als Passwort: git push https://<owner>:<token>@git.sysdaemon.xyz/<owner>/remmina-mac.git main. Lokaler Stand: Commit cd834dc + dist/RemminaMac-0.1.0-macos-arm64.zip sind fertig und warteten nur auf den Push. Nachtrag 2026-09-21: ✅ behoben — mit korrigierten Credentials: GET /api/v1/user → 200 (login x3), Repo x3/remmina-mac erstellt (201), git push -u origin main ok, Release v0.1.0 (id 1) + 2 Assets (ZIP + SHA256) hochgeladen. Status: ✅ behoben.

#12 — „Immer Verbindung falsch" (User-Report, Details offen)

Stand: Gemeldet ohne Ziel-Host, Banner-Text oder Credentials — aus der Ferne nicht reproduzierbar. Systematische App-Fehler als Ursache weitgehend ausgeschlossen (PTY/ssh-Pfad, Fehlertext-Anzeige, TCP-Check und Keychain-Fluss sind per Harness verifiziert). Häufigste reale Ursachen (Checkliste):

  1. user@host im Host-Feld oder ssh://-Präfix → jetzt Live-Warnung im Editor
  2. Leerzeichen/Vertipper in Host/User → wird jetzt getrimmt + gewarnt
  3. Falsches Passwort / Key ohne Passphrase-Eintrag → Banner + Diagnose nutzen
  4. Server blockt (Fail2ban, nur Key-Login erlaubt) → Server-Logs prüfen Umgesetzt: Models/ProfileValidation.swift (trimmen + Warnungen, Harness 7/7), Passwort direkt unter Benutzername, „Bericht kopieren" im Fehler-Banner (ohne Secrets). Offen: Bitte Banner-Text oder kopierten Bericht hierher schicken — dann steht die Ursache in einer Zeile. Status: ⬜ wartet auf Fehlerbericht.

#13 — Permission denied trotz richtigem Passwort (User-Fehlerbericht)

Bericht: administrator@sysdaemon.xyz:22, ssh fragt 3x Passwort → Permission denied (publickey,password). Gleiches Passwort im Terminal.app funktioniert. Ursache: Das Terminal-Eingabefeld war ein Standard-TextField. macOS ersetzt dort je nach Einstellung still Zeichen (" → „“, -- → —, Auto-Korrektur). Ein Passwort mit Sonderzeichen kommt dadurch falsch bei ssh an — im Terminal fällt nichts auf, ssh sagt nur „denied". Verifiziert: Standard-NSTextField hat diese Schalter an bzw. NSTextField kann Dash-Ersetzung gar nicht abschalten (Compiler-Beweis). Fix (Views/TerminalInputField.swift): Eigenes Eingabefeld auf NSTextView-Basis mit allen Ersetzungen explizit aus (Quotes, Dashes, Completion, Spelling, Data/Link-Erkennung, Text-Replacement, Plain-Text); Secure-Variante via NSSecureTextField (per Design ersetzungsfrei); Enter → Senden, Paste-Normalisierung (keine Umbrüche). Verifikation: Harness 9/9 (alle Flags aus). Hinweis: Restrisiko Tastaturlayout (Z/Y) bleibt — Banner-Tipp ergänzt. Status: ✅ behoben — bitte mit v0.5.1 erneut testen.

#14 — Terminal.app loggt ein, RemminaMac nicht (gleiches Passwort)

Stand (User-Antworten): v0.5.1 installiert, Passwort-Login, Terminal.app funktioniert mit denselben Daten. Analyse: App-seitig ist alles verifiziert (PTY, Fehleranzeige, Passwort-Loop mit Sonderzeichen, Eingabe-Ersetzungen aus). Gleiches Binary + gleiche Config → Unterschied muss aus der Umgebung kommen. Hauptkandidat: SSH-Agent — Finder-gestartete GUI-Prozesse erben SSH_AUTH_SOCK nicht. Nutzt Terminal unbemerkt einen Agent-Key (statt Passwort), scheitert die App mit Passwort. Verifiziert: launchctl getenv SSH_AUTH_SOCK liefert den Socket, Shell-Env wäre ohne Abfrage leer für GUI-Apps. Fix: SSHService.agentSocket() (launchctl + Existenzprüfung) → TerminalManager injiziert SSH_AUTH_SOCK ins ssh-Env (koexistiert mit SSHPASS). Banner-Tipp ergänzt. Verifikation: Harness 4/4 (Auflösung 0.07 s, Pfad existiert, ssh -G ok mit echtem + kaputtem Socket). Gegenprobe beim User (bitte ausführen):

ssh-add -l
ssh -o IdentitiesOnly=yes administrator@sysdaemon.xyz

Zweite Zeile erzwingt Passwort-Login ohne Agent — scheitert sie auch im Terminal, war es immer der Agent-Key. Status: ✅ App-Seite umgesetzt — warte auf Gegenprobe.

#15 — Crash bei Tippen + Senden (User-Report)

Stand: „Sobald ich was eintippe und absende, crasht das Programm." Headless nicht reproduzierbar — alle Ebenen einzeln grün. Ausgeschlossen per Test:

  • SIGPIPE beim Schreiben (Harness: macOS liefert EIO statt Signal; send nach stop() überlebt) — Hypothese widerlegt
  • View-Schicht (echter Coordinator + NSTextView: Tippen mit Umlauten/Quotes/Paste, Enter, 200 Programm-Sets) — grün
  • PTY-Roundtrip, Refused-, Keychain-, TCP-Harness — grün Gehärtet (v0.5.3, ohne Beweis dass es DER Crash war):
  • TerminalInputField.updateNSView: Delegat bei Programm-Sets abgehängt — sonst feuert textDidChange synchron zurück ins State während SwiftUI rendert („Modifying state during view update" → Crash-Kandidat Nr. 1, genau beim Senden: Eingabe wird geleert)
  • PTYSession.send: Force-Unwrap baseAddress! → Guard, Leer-Check Weiter nötig bei erneutem Crash: Console.app → Absturzberichte → neuesten RemminaMac-Eintrag öffnen → Exception Type, Termination Reason und crashed Thread hierher schicken. Damit steht die Stelle fest. Status: ⬜ wartet auf Rückmeldung / Crash-Log.

#16 — Profil-Änderungen wirkungslos („geht immer noch nicht")

Stand: Nach mehreren Fixes meldet der User weiterhin pauschal Misserfolg. Code-Review (TerminalManager.session, Zeile 291): Eine einmal erstellte Session wurde für immer wiederverwendet — Host-/Passwort-/Key-Korrekturen im Editor erreichten die laufende/stale Session nie. Jede Korrektur lief ins Leere. Fix: Konfigurations-Fingerprint (Proto/Host/Port/User/ExtraArgs/Key-Pfad/Secret-Wert) pro Session. Bei Abweichung und nicht-laufender Session: stoppen + neu konfigurieren (gleiches Objekt, Tabs bleiben). Laufende Sessions werden nie unter den Füßen wegkonfiguriert ( erst Trennen). Verifikation: Harness 9/9 (Host-Wechsel reconfiguriert, Secret-Wechsel erkannt, laufende ssh-Session an Banner-lesendem Socket unangetastet). Status: ✅ behoben — bitte mit v0.5.4 erneut testen (Profil ggf. einmal neu speichern).

#17 — rzsv: Vorab-Check Timeout (kein App-Fehler, Screenshot-Beweis)

Bild: Tab rzsv (administrator@sysdaemon.xyz:22), Banner „Vorab-Check: Keine Antwort in 4 s", ssh wird korrekt NICHT gestartet. Messung (gleiche Maschine, 22.09.):

  • sysdaemon.xyz → 185.117.250.190 (auch git.sysdaemon.xyz)
  • Port 443: OK in 0.02 s — Port 22: Timeout (DROP, kein Refused)
  • Fazit: Server erreichbar, aber SSH-Port wird aus diesem Netz gedroppt. Von hier kann ssh in KEINEM Client gehen (Terminal eingeschlossen) — kein Terminal-/App-Problem, der Vorab-Check hatte recht. Wahrscheinlichste Ursachen (Reihenfolge):
  1. fail2ban-Bann nach den vielen Permission denied-Versuchen (#13-Ära): fail2ban-client status sshd → unbanip <eigene-IP>
  2. VPN an/aus: „Terminal geht" evtl. mit VPN (Split-DNS/Intern), App ohne (oder umgekehrt) — Zustände vergleichen
  3. Firewall lässt 22 nur aus bestimmten Netzen zu (ufw status, iptables -L, ss -tlnp | grep 22 auf dem Server) Gegenprobe (10 s, Terminal): ssh -v -o ConnectTimeout=8 administrator@sysdaemon.xyz bleibt bei Connecting... hängen → bewiesen netzseitig. Status: ⬜ Server-/Netz-Seite, warte auf Gegenprobe + VPN-Status.

#18 — Host-Key-Yes/No unmöglich (User-Screenshot: Terminal geht, App sagt nur „Host key verification failed.")

Analyse: Terminal zeigt den Are you sure...?-Prompt (mit Fingerprint), App nur den Abbruch ohne Prompt. Beweis per -v: read_passphrase: can't open /dev/tty: Device not configured — ssh-Prompts brauchen ein Controlling-Terminal, unser PTY-Slave war keins (GUI-App hat keins, Kind erbt keins). Betrifft Passwort- UND Yes/No-Prompts. Fix:

  • Sources/ctty-run/ctty-run.c (C-Helper, SPM-Target): Double-Fork (nötig, weil Apples Process Kinder in eigener Prozessgruppe startet → direktes setsid = EPERM) → setsid → Slave-Open (= Ctty) → exec ssh. stderr bleibt auf der Eltern-Pipe (verlustfrei). stop() killt die Prozessgruppe (keine Waisen, verifiziert).
  • Views/HostKeySheet.swift + Flow: Vorab per ssh-keyscan Fingerprint holen → Popup (SHA256/MD5, Typ) → Akzeptieren (→ ~/.ssh/known_hosts, geteilt mit Terminal) / Abbrechen; bei GEÄNDERTEM Key rote MITM-Warnung + ssh-keygen -R-Pfad. Verifikation (lokaler paramiko-Server): Bug reproduziert (kein Prompt, Exit 255) → nach Fix: Ctty per ps bewiesen (ttys001), Yes/No sichtbar + beantwortbar, Passwort akzeptiert, Shell aktiv. HostKeyService: unknown/accept/known/changed/revoke/unreachable grün. Regression (stop ohne Waisen) grün. Status: ✅ behoben (v0.6.0) — Fingerprint im Popup mit Terminal-Ausgabe vergleichbar.

#19 — Fingerprint-Popup erscheint leer (User-Screenshot)

Bild: Sheet-Fenster da, aber winzig und ohne Inhalt; Status hängt auf „Prüfe …". Ursache: .sheet(isPresented:) + if let info im Content: Nimmt die Auswertung den Empty-Zweig (Timing beim Präsentieren), hat der leere Zweig keine Abhängigkeit auf die State-Variable → Sheet aktualisiert sich nie, bleibt leer. Klassische SwiftUI-Falle. Fix: .sheet(item: $hostKeyRequest) mit HostKeyRequest: Identifiable — Content existiert strukturell immer. Zusätzlich läuft HostKeyService.status (ssh-keyscan blockiert Sekunden) jetzt auf Hintergrund-Queue — vorher fror die UI während „Prüfe …" ein. Status: ✅ behoben (v0.6.1).