Thanks for being a part of WWDC25!

How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here

iOS not launching my app network extension, it seemingly isn't crashing it either

My personal project is a bit further along however after not being able to get this to work in my app I fell back to a much simpler/proven implementation out there. There is this project on GitHub with a guide that implements a barebones app extension with packet tunneling. I figure this can give us common ground.

After changing the bundle and group identifiers to all end with -Caleb and or match up I tried running the app. The app extension does not work whatsoever and seemingly for reasons that are similar to my personal project.

If I pull up the console and filter for the subsystem (com.github.kean.vpn-client-caleb.vpn-tunnel) I see the following.

First you see installd installing it

0x16ba5f000 -[MIUninstaller _uninstallBundleWithIdentity:linkedToChildren:waitForDeletion:uninstallReason:temporaryReference:deleteDataContainers:wasLastReference:error:]: Destroying container com.github.kean.vpn-client-caleb.vpn-tunnel with persona 54D15361-A614-4E0D-931A-0953CDB50CE8 at /private/var/mobile/Containers/Data/PluginKitPlugin/2D0AE485-BB56-4E3E-B59E-48424CD4FD65

And then installd says this (No idea what it means)

0x16b9d3000 -[MIInstallationJournalEntry _refreshUUIDForContainer:withError:]: Data container for com.github.kean.vpn-client-caleb.vpn-tunnel is now at /private/var/mobile/Containers/Data/PluginKitPlugin/2D0AE485-BB56-4E3E-B59E-48424CD4FD65

Concerningly runningboardd seems to immediately try and stop it?

Executing termination request for: <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {(
    "com.github.kean.vpn-client-caleb",
    "com.github.kean.vpn-client-caleb.vpn-tunnel"
)}>>

[app<com.github.kean.vpn-client-caleb(54D15361-A614-4E0D-931A-0953CDB50CE8)>:1054] Terminating with context: <RBSTerminateContext| explanation:installcoordinationd app:[com.github.kean.vpn-client-caleb/54D15361-A614-4E0D-931A-0953CDB50CE8] uuid:963149FA-F712-460B-9B5C-5CE1C309B2FC isPlaceholder:Y reportType:None maxTerminationResistance:Absolute attrs:[
	<RBSPreventLaunchLimitation| <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {(
    "com.github.kean.vpn-client-caleb",
    "com.github.kean.vpn-client-caleb.vpn-tunnel"
)}>> allow:(null)>
	]>

Then runningboardd leaves a cryptic message

Acquiring assertion targeting system from originator [osservice<com.apple.installcoordinationd>:244] with description <RBSAssertionDescriptor| "installcoordinationd app:[com.github.kean.vpn-client-caleb/54D15361-A614-4E0D-931A-0953CDB50CE8] uuid:963149FA-F712-460B-9B5C-5CE1C309B2FC isPlaceholder:Y" ID:33-244-5222 target:system attributes:[
	<RBSPreventLaunchLimitation| <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {(
    "com.github.kean.vpn-client-caleb",
    "com.github.kean.vpn-client-caleb.vpn-tunnel"
)}>> allow:(null)>
	]>

And that seems to be all I have to go off of.... If I widen my search a bit I can see backboardd saying things like

Connection removed: IOHIDEventSystemConnection uuid:57E97E5D-8CDE-467B-81CA-36A93C7684AD pid:1054 process:vpn-client type:Passive entitlements:0x0 caller:BackBoardServices: <redacted> + 280 attributes:{
    HighFrequency = 1;
    bundleID = "com.github.kean.vpn-client-caleb";
    pid = 1054;
} state:0x1 events:119 mask:0x800 dropped:0 dropStatus:0 droppedMask:0x0 lastDroppedTime:NONE

Or

Removing client connection <BKHIDClientConnection: 0xbf9828cd0; IOHIDEventSystemConnectionRef: 0xbf96d9600; vpid: 1054(vAF7); taskPort: 0x5D777; bundleID: com.github.kean.vpn-client-caleb> for client: IOHIDEventSystemConnection uuid:57E97E5D-8CDE-467B-81CA-36A93C7684AD pid:1054 process:vpn-client type:Passive entitlements:0x0 caller:BackBoardServices: <redacted> + 280 attributes:{
    HighFrequency = 1;
    bundleID = "com.github.kean.vpn-client-caleb";
    pid = 1054;
} state:0x1 events:119 mask:0x800 dropped:0 dropStatus:0 droppedMask:0x0 lastDroppedTime:NONE source:HID

There's really nothing in the sysdiagnose either. No crash no nothing.

I am stumped. Any idea what might be going wrong for me here? Has something about the way app extensions or sandbox rules work changed in later OSes?

Answered by DTS Engineer in 835850022

After changing the bundle and group identifiers to all end with -Caleb and or match up I tried running the app. The app extension does not work whatsoever and seemingly for reasons that are similar to my personal project.

I go into the details of your data below, but the basic answer here is that everything you've described here looks like the normal install process. That all happens before you extension would activate, so it doesn't really tell us anything about what's actually going wrong.

In terms of what IS going on, my understanding is that VPN activation involves two things:

  1. The VPN configuration itself needs to be created, either through a configuration profile or the programmatic configuration process. Note that some configuration on iOS can ONLY be done through configuration profiles.

  2. The configuration(s) you create in #1 are what then show up in the "VPN" section of Settings.app. Enabling one of those are what would then cause your extension to run.

If you're seeing profiles, then you need to monitor the log at the point you touch "enable" to see "what happened". If you don't see configurations, then you need to look at what happened when your app loads/saves those configuration. Note that (I think) NETunnelProviderManager.save will trigger system "Install VPN Profile". If you're not (or haven't) seeing that screen, then that what you need to sort out first.

Shifting to the log data you sent:

And then installd says this (No idea what it means) _refreshUUIDForContainer:withError

The system uses UUID as the installation "target" for basically "everything" it installs so that it doesn't have to worry about issue like having two apps/components named "MyApp.app" on the same system. Part of that process is that the UUIDs change when an app is updated/installed, which is what _refreshUUIDForContainer actually does.

Concerningly runningboardd seems to immediately try and stop it?

The system doesn't update apps while they're running, so it has to kill that if it's running. That part of what runningboardd is avoid all the different parts of the system tracking/determining exactly what is or is not running and then sending requests based on that state. What actually happens is that installd basically says "I don't what this app running" and runningboardd will then terminate it if it HAPPENS to be running.

The other thing runningboardd takes care of is here:

	<RBSPreventLaunchLimitation| <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {(
    "com.github.kean.vpn-client-caleb",
    "com.github.kean.vpn-client-caleb.vpn-tunnel"

In other words, runningboardd isn't just terminating the app (if it happens to be running), it's also make sure nothing else launches it. This is what prevents you from launching apps in the middle of an update.

However, the critical thing to understand here is around this:

Concerningly runningboardd seems to immediately try and stop it?

runningboardd doesn't really "do" anything. That is, it's job is to do whatever it's told, not to decide what's supposed to happen. That's what this is about:

Then runningboardd leaves a cryptic message

An "assertion" is the basic structure runningboardd uses for all of the commands it receives. All of those different commands types use this same log format, which is basically:

Who is telling me to do "something" (in this case, "installcoordinationd"):

Acquiring assertion targeting system from originator [osservice<com.apple.installcoordinationd>:244] with description <RBSAssertionDescriptor| "installcoordinationd 

What I'm being told to do:

app:[com.github.kean.vpn-client-caleb/54D15361-A614-4E0D-931A-0953CDB50CE8] uuid:963149FA-F712-460B-9B5C-5CE1C309B2FC isPlaceholder:Y" ID:33-244-5222 target:system attributes:[
	<RBSPreventLaunchLimitation| <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {(
    "com.github.kean.vpn-client-caleb",
    "com.github.kean.vpn-client-caleb.vpn-tunnel"
)}>> allow:(null)>
	]>

Note that understanding this structure can be REALLY helpful when trying to quickly navigate your way through our logs. In this case, I'd actually be looking for the last command that came from any kind of "install" component, as any kind of extension activation isn't going to start until after installation has finished.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

After changing the bundle and group identifiers to all end with -Caleb and or match up I tried running the app. The app extension does not work whatsoever and seemingly for reasons that are similar to my personal project.

I go into the details of your data below, but the basic answer here is that everything you've described here looks like the normal install process. That all happens before you extension would activate, so it doesn't really tell us anything about what's actually going wrong.

In terms of what IS going on, my understanding is that VPN activation involves two things:

  1. The VPN configuration itself needs to be created, either through a configuration profile or the programmatic configuration process. Note that some configuration on iOS can ONLY be done through configuration profiles.

  2. The configuration(s) you create in #1 are what then show up in the "VPN" section of Settings.app. Enabling one of those are what would then cause your extension to run.

If you're seeing profiles, then you need to monitor the log at the point you touch "enable" to see "what happened". If you don't see configurations, then you need to look at what happened when your app loads/saves those configuration. Note that (I think) NETunnelProviderManager.save will trigger system "Install VPN Profile". If you're not (or haven't) seeing that screen, then that what you need to sort out first.

Shifting to the log data you sent:

And then installd says this (No idea what it means) _refreshUUIDForContainer:withError

The system uses UUID as the installation "target" for basically "everything" it installs so that it doesn't have to worry about issue like having two apps/components named "MyApp.app" on the same system. Part of that process is that the UUIDs change when an app is updated/installed, which is what _refreshUUIDForContainer actually does.

Concerningly runningboardd seems to immediately try and stop it?

The system doesn't update apps while they're running, so it has to kill that if it's running. That part of what runningboardd is avoid all the different parts of the system tracking/determining exactly what is or is not running and then sending requests based on that state. What actually happens is that installd basically says "I don't what this app running" and runningboardd will then terminate it if it HAPPENS to be running.

The other thing runningboardd takes care of is here:

	<RBSPreventLaunchLimitation| <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {(
    "com.github.kean.vpn-client-caleb",
    "com.github.kean.vpn-client-caleb.vpn-tunnel"

In other words, runningboardd isn't just terminating the app (if it happens to be running), it's also make sure nothing else launches it. This is what prevents you from launching apps in the middle of an update.

However, the critical thing to understand here is around this:

Concerningly runningboardd seems to immediately try and stop it?

runningboardd doesn't really "do" anything. That is, it's job is to do whatever it's told, not to decide what's supposed to happen. That's what this is about:

Then runningboardd leaves a cryptic message

An "assertion" is the basic structure runningboardd uses for all of the commands it receives. All of those different commands types use this same log format, which is basically:

Who is telling me to do "something" (in this case, "installcoordinationd"):

Acquiring assertion targeting system from originator [osservice<com.apple.installcoordinationd>:244] with description <RBSAssertionDescriptor| "installcoordinationd 

What I'm being told to do:

app:[com.github.kean.vpn-client-caleb/54D15361-A614-4E0D-931A-0953CDB50CE8] uuid:963149FA-F712-460B-9B5C-5CE1C309B2FC isPlaceholder:Y" ID:33-244-5222 target:system attributes:[
	<RBSPreventLaunchLimitation| <RBSProcessPredicate <RBSProcessBundleIdentifiersPredicate| {(
    "com.github.kean.vpn-client-caleb",
    "com.github.kean.vpn-client-caleb.vpn-tunnel"
)}>> allow:(null)>
	]>

Note that understanding this structure can be REALLY helpful when trying to quickly navigate your way through our logs. In this case, I'd actually be looking for the last command that came from any kind of "install" component, as any kind of extension activation isn't going to start until after installation has finished.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

iOS not launching my app network extension, it seemingly isn't crashing it either
 
 
Q