From ae2204559361e11fe33496c93433f26f5689eb5e Mon Sep 17 00:00:00 2001 From: Rae McKelvey <633012+okdistribute@users.noreply.github.com> Date: Wed, 10 Jun 2026 14:13:37 -0700 Subject: [PATCH] Document local network discovery permissions for iOS and Android Device testing showed iroh's unicast traffic never engages the iOS local-network permission, so the Swift guide explains that no Info.plist keys are needed by default and what changes once mDNS (presetN0WithMdns, iroh-ffi#240) is adopted: the usage-description key plus the multicast entitlement on iOS, and the manifest permission plus a WifiManager multicast lock on Android. Co-Authored-By: Claude Fable 5 --- languages/kotlin.mdx | 31 +++++++++++++++++++++++++++++++ languages/swift.mdx | 21 +++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/languages/kotlin.mdx b/languages/kotlin.mdx index 1eee565..351775e 100644 --- a/languages/kotlin.mdx +++ b/languages/kotlin.mdx @@ -71,6 +71,37 @@ On Android, the OS aggressively suspends background processes — sockets get to For desktop JVM apps these constraints don't apply — keep the endpoint bound for the lifetime of the process and call `shutdown()` on exit. +## Local network discovery on Android + +By default, iroh does not browse your local network. Endpoints learn each other's addresses through iroh's discovery service (or a ticket) and then talk over ordinary one-to-one connections, the same kind of traffic as loading a website. On Android that means the only permission you need is `INTERNET`, which every networked app already declares. There is no prompt and no extra manifest entry, and direct device-to-device connections work even when both peers sit on the same Wi-Fi. + +iroh can optionally discover peers over mDNS, a broadcast technology that lets endpoints find each other on the same Wi-Fi without any outside help (`presetN0WithMdns()`, not yet in an iroh-ffi release). Android handles this differently from iOS: there is no user-facing permission prompt, but phones silently drop broadcast packets at the Wi-Fi chip to save battery, so mDNS responses never reach your app unless you ask the system to let them through. Enabling it takes three steps: + +1. Declare the multicast permission in `AndroidManifest.xml`. It is granted at install time; the user is never prompted: + + ```xml + + ``` + +2. Hold a multicast lock while you want discovery to work. The lock tells the Wi-Fi driver to deliver broadcast packets instead of filtering them, at some battery cost, so release it when you no longer need to find new peers: + + ```kotlin + val wifi = context.getSystemService(Context.WIFI_SERVICE) as WifiManager + val multicastLock = wifi.createMulticastLock("iroh-mdns").apply { acquire() } + // ... bind and use the endpoint ... + multicastLock.release() + ``` + +3. Bind your endpoint with the mDNS preset instead of `presetN0()`: + + ```kotlin + val ep = Endpoint.bind( + EndpointOptions(preset = presetN0WithMdns(), alpns = listOf(ALPN)), + ) + ``` + +Desktop JVM apps need none of this: mDNS works there without any special permissions or locks. + ## Building from source For Android, an unsupported host, or to hack on the bindings themselves, clone iroh-ffi and generate the sources locally: diff --git a/languages/swift.mdx b/languages/swift.mdx index 3922307..8278991 100644 --- a/languages/swift.mdx +++ b/languages/swift.mdx @@ -65,6 +65,27 @@ Your `.entitlements` file should now contain: ``` +## Local network discovery on iOS + +By default, iroh does not browse your local network. Endpoints learn each other's addresses through iroh's discovery service (or a ticket you paste in) and then talk over ordinary one-to-one connections, the same kind of traffic as loading a website. iOS treats that as regular internet traffic even when the two devices sit on the same Wi-Fi, so direct device-to-device connections work with no extra Info.plist keys or permission prompts. We verified this on real hardware: an iPhone and a Mac on the same network held direct connections over both their local (`192.168.x.x`) and public addresses, even with the app's Local Network toggle switched off in **Settings → Privacy & Security**. + +iroh can optionally discover peers over mDNS, the broadcast technology behind Bonjour, which lets endpoints find each other on the same Wi-Fi without any outside help. Because that mode genuinely browses the network, iOS gates it behind the "would like to find and connect to devices on your local network" prompt. mDNS discovery has not shipped in an iroh-ffi release yet; once it does (`presetN0WithMdns`), enabling it takes three steps: + +1. Add the `NSLocalNetworkUsageDescription` key to your Info.plist with a short sentence telling the user why your app looks for nearby devices. iOS shows this text in the permission prompt. +2. Request the `com.apple.developer.networking.multicast` entitlement from Apple ([request form](https://developer.apple.com/contact/request/networking-multicast)) and add it to your app's entitlements once granted. +3. Bind your endpoint with the mDNS preset instead of `presetN0()`: + + ```swift + let ep = try await Endpoint.bind(options: EndpointOptions( + preset: presetN0WithMdns(), + alpns: [Data("hello-iroh/0".utf8)] + )) + ``` + + `presetN0WithMdns()` keeps everything the n0 preset configures (relays and public discovery) and layers local-network discovery on top, so peers on the same Wi-Fi can find each other even when the public lookup path is slow or unreachable. + +Until you opt into mDNS, none of this applies. + ## 5. Disable previews (Xcode 16 workaround) Xcode 16's preview pipeline tries to link `SwiftUICore.framework` directly when a Swift Package is in the graph, which fails with *"product being built is not an allowed client of it"*. Until Apple ships a fix, turn previews off for the app target: