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.
59 lines
2.5 KiB
Plaintext
59 lines
2.5 KiB
Plaintext
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
<plist version="1.0">
|
|
<dict>
|
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
|
<true/>
|
|
<key>GCSupportedGameControllers</key>
|
|
<array>
|
|
<dict>
|
|
<key>ProfileName</key>
|
|
<string>ExtendedGamepad</string>
|
|
</dict>
|
|
<dict>
|
|
<key>ProfileName</key>
|
|
<string>MicroGamepad</string>
|
|
</dict>
|
|
</array>
|
|
<key>NSBonjourServices</key>
|
|
<array>
|
|
<string>_punktfunk._udp</string>
|
|
</array>
|
|
<!-- NOTE: there is deliberately NO NSAppTransportSecurity dict here. ATS stays fully ON.
|
|
The host is self-signed at a user-supplied address, which default ATS can never accept
|
|
(it exempts only .local, unqualified names, and RFC1918/link-local literals — notably NOT
|
|
Tailscale's 100.64/10 CGNAT range), so the management API talks over MgmtTransport
|
|
(Network.framework), which is outside the URL loading system and pins the host by
|
|
SHA-256 fingerprint instead. That leaves cover-art CDN fetches as the app's only
|
|
URLSession traffic, and they keep the full ATS policy — which is the whole reason not to
|
|
reach for NSAllowsArbitraryLoads here. See MgmtTransport.swift. -->
|
|
<!-- Background keep-alive (opt-in, iOS/iPadOS): the ONLY sanctioned way to keep the long-lived
|
|
QUIC socket + pump-thread set alive while backgrounded is the audio background mode, backed
|
|
by the session's real, audible remote audio (AVAudioEngine keeps rendering). Video decode is
|
|
dropped; a bounded timer auto-disconnects. Never silence-as-keepalive (App Review 2.5.4).
|
|
tvOS ignores/tolerates the key; macOS is not gated by it. -->
|
|
<key>UIBackgroundModes</key>
|
|
<array>
|
|
<string>audio</string>
|
|
</array>
|
|
<!-- Live Activities (iOS/iPadOS): the Lock-Screen / Dynamic-Island session surface. Updated
|
|
locally (pushType nil) from the alive app process — no aps-environment. tvOS/macOS ignore it. -->
|
|
<key>NSSupportsLiveActivities</key>
|
|
<true/>
|
|
<!-- Deep links: punktfunk://connect/<host-uuid>[?launch=<GameEntry.id>]. Emitted by the
|
|
launcher widget and Siri/Shortcuts; routed by ContentView.onOpenURL into the existing
|
|
connect path. Shared across all three targets (tvOS/macOS accept it harmlessly). -->
|
|
<key>CFBundleURLTypes</key>
|
|
<array>
|
|
<dict>
|
|
<key>CFBundleURLName</key>
|
|
<string>io.unom.punktfunk.deeplink</string>
|
|
<key>CFBundleURLSchemes</key>
|
|
<array>
|
|
<string>punktfunk</string>
|
|
</array>
|
|
</dict>
|
|
</array>
|
|
</dict>
|
|
</plist>
|