Doc/Code-Drift bei TLS. docs/09 §9.4 (Zeile ~57) behauptet: "self-signed → user confirms fingerprint on first pair, like a SSH host key". Das gibt es im Code nicht.
app/shared/src/commonMain/kotlin/iris/net/GatewayClient.kt:82-86 baut einen Default-OkHttpClient (kein sslSocketFactory, kein CertificatePinner, kein hostnameVerifier). Konsequenz:
LAN ohne TLS: egal.
Domain + gültiges CA-Zert: funktioniert out of the box.
Domain + self-signed Zert: Default-Client lehnt ab, es gibt KEIN Fingerprint-Confirm-UI. User müsste das Zert in den System-Trust-Store installieren.
Vorschlag (beides, nicht beides):
Entweder §9.4 umschreiben (Fingerprint-Confirm-Satz streichen) auf das, was der Code tut.
Oder den Confirm-Flow bauen, damit self-signed Zerts nutzbar sind.
Akzeptanz:
Doku und Code stimmen überein
Falls Confirm-Flow gebaut: self-signed Zert wird beim ersten Pairing per Fingerprint bestätigt und gepinnt
Doc/Code-Drift bei TLS. docs/09 §9.4 (Zeile ~57) behauptet: "self-signed → user confirms fingerprint on first pair, like a SSH host key". Das gibt es im Code nicht.
app/shared/src/commonMain/kotlin/iris/net/GatewayClient.kt:82-86 baut einen Default-OkHttpClient (kein sslSocketFactory, kein CertificatePinner, kein hostnameVerifier). Konsequenz:
- LAN ohne TLS: egal.
- Domain + gültiges CA-Zert: funktioniert out of the box.
- Domain + self-signed Zert: Default-Client lehnt ab, es gibt KEIN Fingerprint-Confirm-UI. User müsste das Zert in den System-Trust-Store installieren.
Vorschlag (beides, nicht beides):
- Entweder §9.4 umschreiben (Fingerprint-Confirm-Satz streichen) auf das, was der Code tut.
- Oder den Confirm-Flow bauen, damit self-signed Zerts nutzbar sind.
Akzeptanz:
- Doku und Code stimmen überein
- Falls Confirm-Flow gebaut: self-signed Zert wird beim ersten Pairing per Fingerprint bestätigt und gepinnt
ARIA
added the bug label 2026-08-24 15:00:01 +00:00
ARIA
self-assigned this 2026-08-24 15:00:18 +00:00
Fixed by building the Confirm-Flow (commit 597a280). Self-signed gateway certs are now usable:
TlsPinning.kt: PinningTrustManager wraps the platform default trust manager — a rejected cert is accepted only when its SHA-256 fingerprint matches the user-confirmed pin; anything else fails with TlsFingerprintRequired (hostname verification still applies, so the cert needs a SAN for the URL host).
Pin stored in SecureStore.pinnedCertFingerprint (EncryptedSharedPreferences / desktop settings.json), wiped on forget().
GatewayClient: pinning socket factory on the shared client (all legs inherit it), new terminal State.TlsConfirmRequired, unwraps the nested exception in connect loop / watchdog / SSE / poll / testHello.
UI: ConnectScreen shows a "Confirm gateway certificate" dialog with the fingerprint ("Confirm & pin" re-runs the connect); a changed cert re-triggers the dialog, like a changed SSH host key.
Docs: §9.4 now describes the real flow (incl. SAN requirement), gap-table item 6 → implemented, setup.md note updated.
Live E2E verified on the phone: first pair against a self-signed IRIS_HTTP_CERT gateway → fingerprint dialog → confirm → chat works; auto-reconnect after a gateway restart uses the stored pin.
Fixed by building the Confirm-Flow (commit 597a280). Self-signed gateway certs are now usable:
- `TlsPinning.kt`: `PinningTrustManager` wraps the platform default trust manager — a rejected cert is accepted only when its SHA-256 fingerprint matches the user-confirmed pin; anything else fails with `TlsFingerprintRequired` (hostname verification still applies, so the cert needs a SAN for the URL host).
- Pin stored in `SecureStore.pinnedCertFingerprint` (EncryptedSharedPreferences / desktop settings.json), wiped on forget().
- `GatewayClient`: pinning socket factory on the shared client (all legs inherit it), new terminal `State.TlsConfirmRequired`, unwraps the nested exception in connect loop / watchdog / SSE / poll / testHello.
- UI: ConnectScreen shows a "Confirm gateway certificate" dialog with the fingerprint ("Confirm & pin" re-runs the connect); a changed cert re-triggers the dialog, like a changed SSH host key.
- Docs: §9.4 now describes the real flow (incl. SAN requirement), gap-table item 6 → implemented, setup.md note updated.
- Tests: `TlsPinningTest` + `TlsPinningIntegrationTest` (real TLS handshake: unpinned → confirm data, pinned → 200). Full suites green (88 desktop + 83 Android host tests).
Live E2E verified on the phone: first pair against a self-signed `IRIS_HTTP_CERT` gateway → fingerprint dialog → confirm → chat works; auto-reconnect after a gateway restart uses the stored pin.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Doc/Code-Drift bei TLS. docs/09 §9.4 (Zeile ~57) behauptet: "self-signed → user confirms fingerprint on first pair, like a SSH host key". Das gibt es im Code nicht.
app/shared/src/commonMain/kotlin/iris/net/GatewayClient.kt:82-86 baut einen Default-OkHttpClient (kein sslSocketFactory, kein CertificatePinner, kein hostnameVerifier). Konsequenz:
Vorschlag (beides, nicht beides):
Akzeptanz:
Fixed by building the Confirm-Flow (commit
597a280). Self-signed gateway certs are now usable:TlsPinning.kt:PinningTrustManagerwraps the platform default trust manager — a rejected cert is accepted only when its SHA-256 fingerprint matches the user-confirmed pin; anything else fails withTlsFingerprintRequired(hostname verification still applies, so the cert needs a SAN for the URL host).SecureStore.pinnedCertFingerprint(EncryptedSharedPreferences / desktop settings.json), wiped on forget().GatewayClient: pinning socket factory on the shared client (all legs inherit it), new terminalState.TlsConfirmRequired, unwraps the nested exception in connect loop / watchdog / SSE / poll / testHello.TlsPinningTest+TlsPinningIntegrationTest(real TLS handshake: unpinned → confirm data, pinned → 200). Full suites green (88 desktop + 83 Android host tests).Live E2E verified on the phone: first pair against a self-signed
IRIS_HTTP_CERTgateway → fingerprint dialog → confirm → chat works; auto-reconnect after a gateway restart uses the stored pin.