From 244cafe00554f03bd0085bcaaeb962bfa60ca7a4 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Thu, 6 Aug 2026 02:08:23 +0200 Subject: [PATCH] refactor(apple): move the mgmt API off URLSession so ATS can stay on The previous commit bought the library back on VPN/remote hosts by declaring NSAllowsArbitraryLoads, which works but is blunt: it drops ATS for ALL of the app's URLSession traffic, and the only other traffic is third-party cover-art CDN fetches -- the one surface we never wanted to open. It cost the TLS-version floor, forward secrecy, and the cleartext-HTTP block on URLs the host supplies at runtime (custom entries and scanner plugins carry arbitrary ones). So take the host out of the URL loading system instead. MgmtTransport speaks HTTPS over Network.framework, which ATS does not govern, and states the trust rule we actually mean in a verify block: the leaf must hash to the fingerprint pinned during PIN pairing. That is the same rule punktfunk-core has always applied on the QUIC stream plane -- which is exactly why streaming kept working over Tailscale while the library did not. With that, the ATS dict is gone and ATS is fully enforced again. Cover-art CDN fetches keep ordinary URLSession with full system trust evaluation and no client certificate. LibraryTLSDelegate is deleted; nothing pins through URLSession now. Also here: - HTTPResponse: just enough HTTP/1.1 to read one GET -- status, headers, Content-Length and chunked framing (hyper streams the art proxy chunked). A body shorter than Content-Length throws instead of returning partial JSON, which would otherwise read as "this host has no games". - LibraryError.pinMismatch, so a re-keyed host says "pair again" rather than sending someone to debug their network. - 403 joins 401 as "unauthorized": both are the host declining the certificate. - baseURL brackets IPv6 literals; the old string interpolation did not. - 11 tests covering the framings hyper emits and the failure modes that would otherwise be silent. Known trade-off: no connection reuse yet, so each poster costs its own handshake where the pooled URLSession shared one. Fine on a LAN, worth revisiting for large libraries over a high-latency link. --- clients/apple/Config/Info.plist | 27 +-- .../Home/LibraryCoverflowView.swift | 4 +- .../PunktfunkClient/Home/LibraryView.swift | 27 +-- .../PunktfunkClient/Home/LibraryWidgets.swift | 8 +- .../PunktfunkKit/Connection/ClientTLS.swift | 67 +------ .../Connection/HTTPResponse.swift | 129 ++++++++++++ .../Connection/LibraryClient.swift | 150 +++++++++----- .../Connection/MgmtTransport.swift | 184 ++++++++++++++++++ .../LibraryClientTests.swift | 98 ++++++++++ 9 files changed, 549 insertions(+), 145 deletions(-) create mode 100644 clients/apple/Sources/PunktfunkKit/Connection/HTTPResponse.swift create mode 100644 clients/apple/Sources/PunktfunkKit/Connection/MgmtTransport.swift diff --git a/clients/apple/Config/Info.plist b/clients/apple/Config/Info.plist index 898d3e9c..287533e8 100644 --- a/clients/apple/Config/Info.plist +++ b/clients/apple/Config/Info.plist @@ -19,25 +19,14 @@ _punktfunk._udp - - NSAppTransportSecurity - - NSAllowsArbitraryLoads - - +