Skip to content

Devices and runtimes

In-app SDK

Since 0.3.0 Requires Cloak Proxy 0.3.0 or later.

Add CloakProxyKit to a debug build of your iOS or macOS app and see its URLSession requests in Cloak Proxy with no proxy settings or certificate.

CloakProxyKit is a small Swift package you add to the debug build of your own iOS or macOS app. The app then sends its own URLSession requests to Cloak Proxy on your Mac over the local network, so they show up under the app’s name and icon with no Wi-Fi proxy, no certificate profile and no SSL Proxying. Use it when you’re testing an app you build, especially one that pins its certificates or runs on a phone where changing the proxy is a chore.

In-app SDK Proxy
Setup on the device Add a package and one line of code to your app Wi-Fi proxy and a trusted certificate profile
Which traffic Your app’s URLSession requests Everything on the device that uses the proxy
Apps that pin certificates Works, because nothing is intercepted Fails unless pinning is turned off
Debugging tools (breakpoints, Map Local, rules) Don’t apply: Cloak Proxy only observes Apply
Other people’s apps No Yes, with SSL Proxying

You can use both at once. Requests from a phone that also uses your Mac as its proxy appear under the same device.

  1. Open Cloak Proxy › Settings… and click the In-app SDK tab.

  2. Turn on Accept traffic from apps using CloakProxyKit.

    Cloak Proxy listens on port 9092 and announces itself on the local network over Bonjour. The page shows Listening on port 9092. Apps on this network find this Mac over Bonjour. and the first 16 digits of the Certificate fingerprint.

  3. To use another port, type it in Port and click Apply.

The listener is off until you turn it on.

Open Settings (Ctrl ,) › In-app SDK and turn on Accept traffic from apps using CloakProxyKit. The tab shows the addresses and port it listens on and the first 16 digits of its certificate’s fingerprint.

Cloak Proxy advertises the listener the way a Mac does, with avahi-publish-service while it’s on, if the avahi-utils package is installed (sudo apt install avahi-utils; the Cloak Proxy package suggests it but doesn’t need it). Without it, or on networks where multicast DNS doesn’t get through, give the app the address and port shown in the tab:

CloakProxyKit.start(.init(host: "203.0.113.20", port: 9092))

host skips discovery and connects straight to that address. Add macFingerprint: with the digits from the tab to make sure the app only talks to this computer.

When an unknown app connects, Cloak Proxy asks over the main window, as on the Mac. Don’t Allow is the default (Return or Esc). A question nobody answers within two minutes refuses the app for now without blocking it, and it asks again the next time it connects. There are no iOS Simulators on Linux, so this is for iPhones, iPads and Macs on the same network.

CloakProxyKit is an open-source (MIT) Swift package at github.com/subcloak/CloakProxyKit. It needs iOS 15 or macOS 12 and has no dependencies.

  1. Add the package.

    Choose File › Add Package Dependencies…, enter https://github.com/subcloak/CloakProxyKit, and add the CloakProxyKit library to your app target.

  2. On iOS, let the app use the local network. Add these keys to the app’s Info.plist:

    Info.plist
    <key>NSLocalNetworkUsageDescription</key>
    <string>Sends this app's network requests to Cloak Proxy on your Mac while debugging.</string>
    <key>NSBonjourServices</key>
    <array>
    <string>_cloakproxy-sdk._tcp</string>
    </array>

    A sandboxed macOS app needs the Outgoing Connections (Client) capability instead.

  3. Start the SDK as early as you can, inside #if DEBUG:

    WeatherApp.swift
    #if DEBUG
    import CloakProxyKit
    #endif
    @main
    struct WeatherApp: App {
    init() {
    #if DEBUG
    CloakProxyKit.start()
    #endif
    }
    var body: some Scene { WindowGroup { ContentView() } }
    }

    In a UIKit app, call it at the start of application(_:didFinishLaunchingWithOptions:).

  4. Run the app. On iOS, allow local network access when the app asks.

The first time an app connects from a device, Cloak Proxy asks, for example:

Allow “Weather” on iPhone at 192.168.1.23 to send traffic?

The message says the app uses CloakProxyKit, that its requests will show in Cloak Proxy including headers and bodies, and that rules don’t apply to them. Below that are the app’s bundle ID, version and device. Click Allow or Don’t Allow. The question appears over the main window, one app at a time.

Cloak Proxy remembers the answer for that app on that device:

  • An allowed app gets a token and sends it from then on, so it isn’t asked about again. If the token is missing or wrong (the app was reinstalled, or something else claims to be it), you’re asked again.
  • A blocked app is refused without asking. It tries again about once a minute, in case you change your mind in Settings.

Until you answer, and whenever the Mac is away, the app keeps finished requests in a buffer and sends them once it’s allowed.

The device is named by what it reports. Since iOS 16 an iPhone reports only “iPhone”, so Cloak Proxy adds its address. A Simulator shows as its model, such as iPhone 17 Pro Simulator, and a Mac app on the same Mac as this Mac.

Requests from the SDK are ordinary requests in the traffic list, with a few differences:

  • Attributed to the app. In the sidebar, an app on a phone or another Mac is listed under Devices, under its device, with the icon it sent. An app in the iOS Simulator or a Mac app on this Mac is listed under Apps.
  • An SDK tag marks each request in the list.
  • The request’s Summary tab shows Captured by (CloakProxyKit, inside the app), the app’s version and bundle ID (App), the Device and its OS, the time spent on each phase (DNS, Connect, TLS, Request, Waiting, Download), the Server address, and whether the Connection was new, reused or served from the app’s cache.
  • Everything else works: filtering, search, Trace Value, Insights, Privacy Map, export and the MCP server. HAR exports include the app’s own timings and the server’s address. Pausing recording and muting the app apply too.

Settings › In-app SDK lists every app you’ve answered for:

Section What it shows
Allowed apps Each app with its icon, the device it runs on, its version and when it was last seen. A green dot means it’s connected now.
Blocked apps Apps you chose Don’t Allow for. Shown only when there are some.

Click Remove next to an app to forget it. An allowed app is disconnected right away and asked about again the next time it connects. A blocked app is asked about again too.

Pass options to start to change what’s recorded and which Mac is used:

var options = CloakProxyKit.Options()
options.macName = "Ada's MacBook Pro"
options.maxBodySize = 256 * 1024
options.ignoredHosts = ["analytics.example.com"]
CloakProxyKit.start(options)
Option Default What it does
macName Any Mac Connect only to the Mac with this name, as set in System Settings › General › Sharing.
macFingerprint None Connect only to a Mac whose certificate fingerprint starts with this. Copy it from Certificate in Settings › In-app SDK, without the spaces.
host, port Bonjour, 9092 Skip Bonjour and connect to this address and port, for networks where Bonjour doesn’t work.
maxBodySize 1 MB The most of each request and response body kept. 0 sends no bodies. At most 4 MB.
maxBufferedRequests 500 Finished requests kept while the Mac is away or hasn’t allowed the app. The oldest are dropped first.
maxBufferedBytes 16 MB The size limit for the same buffer.
ignoredHosts None Hosts not to record. example.com covers its subdomains; *.example.com covers only the subdomains.
appName The app’s display name The name shown in Cloak Proxy.
appIcon The app icon A square PNG to show instead.
enabledInReleaseBuilds false Deprecated since CloakProxyKit 0.4.0 and has no effect: Release builds don’t contain the recording code. In CloakProxyKit 0.3.0 it made start() run in builds without the DEBUG flag.

CloakProxyKit.stop() stops recording and disconnects. CloakProxyKit.state says whether the SDK is searching, connecting, waiting for approval, connected or blocked.

  • Recording. URLSession hands each request to an internal connection object. CloakProxyKit wraps the calls that object makes when the response arrives, when body data arrives and when the request finishes, copies what it needs, and then calls the original. Delegates, completion handlers, async/await, cookies, caching and authentication behave exactly as before. Timings come from the request’s own URLSession metrics. Proxyman’s Atlantis works the same way.
  • Finding the Mac. While the listener is on, Cloak Proxy announces _cloakproxy-sdk._tcp over Bonjour with its name and the start of its certificate fingerprint. Bonjour also works over a USB cable when the Mac and the device see each other on the cable’s network interface.
  • Sending. The app connects over TLS 1.2 or later and checks the Mac’s certificate by its fingerprint, which must match what the Mac announced. Requests are sent once finished, in batches, and kept until the Mac confirms them, so a dropped connection loses nothing and nothing arrives twice.
  • The listener uses a self-signed certificate that Cloak Proxy creates once and keeps next to its root certificate. It isn’t issued by the root certificate Cloak Proxy uses to decrypt HTTPS, so it can’t be used to impersonate other sites.
  • Nothing appears until you allow an app, and an allowed app has to present its token. Allowed apps are kept in sdk-sources.json (readable only by you) next to the certificate, with only a hash of each token. Icons are kept in sdk-icons.
  • Anyone on your network can see the Bonjour announcement, and a program pretending to be Cloak Proxy could accept an app’s traffic. On shared networks, set macName or macFingerprint.
  • On Linux, the allowed and blocked apps are kept in ~/.local/share/cloakproxy/sdk-sources.json (readable only by you), and the switch and port in ~/.config/cloakproxy/sdk.json.
  • Requests from the SDK include headers and bodies, which may hold tokens and personal data. Use the SDK only in debug builds, and turn the listener off when you’re done.
Limit Details
URLSession only Requests made with URLSession, and libraries built on it such as Alamofire, are recorded. WKWebView, AVPlayer streaming, Network.framework connections and other networking stacks are not.
Background sessions Their requests run in a system process, so they aren’t seen.
WebSockets WebSocket tasks are skipped.
Upload bodies Bodies set on the request or passed as Data to upload(for:from:) are recorded. Bodies from a file or a stream aren’t, and the request is marked as truncated.
Redirects A redirected request shows once, with the final URL and response.
Compressed responses Response bodies are recorded as the app received them, already decompressed, even when Content-Encoding says gzip or br.
Background on iOS iOS drops the connection when the app goes to the background. The SDK reconnects when it returns and sends what it buffered.
Future OS versions Recording relies on URLSession internals that have been stable for years. If an OS changes them, the SDK records nothing rather than misbehave, and CloakProxyKit.isCapturing is false.
Symptom What to check
No question appears on the Mac Accept traffic from apps using CloakProxyKit is on, the Mac and the device are on the same network, and on iOS the app has both Info.plist keys and local network access is allowed in Settings › Privacy & Security › Local Network.
Bonjour doesn’t find the Mac Some networks block Bonjour. Set host to the Mac’s address and port to the listener’s port.
The app connects to the wrong Mac Set macName or macFingerprint.
You blocked an app by mistake Click Remove next to it under Blocked apps. It’s asked about again within a minute.
You’re asked about the same app again It lost its token, usually because it was reinstalled. Allow it again.
Some requests are missing They may not use URLSession, or they ran in a background session. See Limits.