"How to" for dext distribution

I have a DriverKit system extension (dext) that uses PCIDriverKit. I would like to get the build environment straightened out to successfully distribute the dext and associated software to end users.

There are three types of software involved:

  1. The Dext-hosting application - this is the application that must be installed to /Applications/, and will perform the registration of the dext. The dext is deployed "within" this application, and can be found in the /Contents/Library/SystemExtensions folder of the app bundle.
  2. The dext itself - this is the actual binary system extension, which will be registered by its owning application, and will operate in its own application space independent of the hosting application.
  3. Additional applications that communicate with the dext - these are applications which will connect to the dext through user clients, but these applications do not contain the dext themselves.

There are multiple locations where settings need to be exactly correct for each type of software to be signed, provisioned, and notarized properly in order to be distributed to users:

  1. developer.apple.com - where "identifiers" and "provisioning profiles" are managed. Note that there are differences in access between "Team Agent", "Admin", and "Developer" at this site.
  2. Xcode project's Target "Signing & Capabilities" tab - this is where "automatically manage signing" can be selected, as well as team selection, provisioning profile selection, and capabilities can be modified.
  3. Xcode project's Target "Build Settings" tab - this is where code signing identity, code signing development team, code signing entitlements file selection, Info.plist options and file selection, and provisioning profile selection.
  4. Xcode's Organizer window, which is where you manage archives and select for distribution. In this case, I am interested in "Developer ID" Direct Distribution - I want the software signed with our company's credentials (Team Developer ID) so that users know they can trust the software.

Choosing "automatically manage signing" does not work for deployment. The debug versions of software include DriverKit (development) capability (under App ID configuration at developer.apple.com), and this apparently must not be present in distributable provisioning. I believe this means that different provisioning needs to occur between debug and release builds?

I have tried many iterations of selections at all the locations, for all three types of binaries, and rather than post everything that does not work, I am asking, "what is supposed to work?"

Answered by DTS Engineer in 811868022

Stepping back for a moment, I think it's important to understand what's actually going on here. The starting point here is that there are two sets of data at work here:

  • (1) Your Xcode project has a set of data about your app, particularly the entitlement list, which are embedded in the codes signature of your app. That data can actually be viewed with the command:
codesign -dvvv --ent :- <path>
  • (2) The developer portal has a set of data about apps bundle ID, particularly the entitlement list, which is used to generate a provisioning profile which will be embedded inside your apps bundle. That data can actually be viewed with one of the following commands*:

*iOS and macOS use slightly different names and locations, a fact which is annoying when you jump back and forth between platforms. I wrote a script years ago which simply tried all 4 possibilities and am now working hard to not remember which is which.

security cms -D -i <path>/embedded.mobileprovision
security cms -D -i <path>/embedded.provisionprofile
security cms -D -i <path>/Contents/embedded.mobileprovision
security cms -D -i <path>/Contents/embedded.provisionprofile

All of the issues here boil down to "the data in those two files doesn't match". That leads to the difference between "Automatic" and "Manual":

  • Automatic-> Xcode will interact with the dev portal, adjusting the dev portal configuration (if it can) and generating/downloading provisioning profiles as needed.

  • Manual-> Xcode will (try) use whatever provisioning profiles you tell it to.

What's changed over time is Xcode's involvement with "adjusting the dev portal configuration". Originally, it didn't really do ANY adjustment. You'd configure the data on the Dev Portal and Xcode would use that data. Between Xcode v14 -> v16, Xcode expanded it's role, with the goal being that Xcode would be able to handle "all" of this configuration. That evolution is also what made Xcode 15 so problematic for DEXT. Xcode 15 added support for configuring managed capabilities (which is what DEXTs rely on) but it DIDN'T properly handle the particular configuration DEXTs use.

The key point here is that manual shouldn't actually "different" than automatic in an fundamental way, as all automatic should be generating exactly the same profile manual would.

That leads to the errors you're seeing. First off, this one:

  1. There is a problem with the request entity - You already have a current Developer ID Application Managed (with Kext) certificate or a pending certificate request.

I don't know what's causing that, but I don't believe it's directly related to this issue. I suspect it's a side effect of how your archive was originally signed and will self correct once other issues are resolved.

That leads to the other two errors, which are actually the same underlying error. First the manual error:

Profile doesn't match the entitlements file's value for the com.apple.developer.driverkit.userclient-access entitlement.

In other words, you pointed Xcode at a provisioning profile ("2") which didn't match apps signing ("1") configuration. You were actually on the right track here;

there is no place to declare that in the developer portal when making the provisioning profile.

Historically, the portal added a mechanism that could artificially "inject" a single "chunk" of entitlement data into the profile being generated. This was a poor solution that didn't scale which is why we replaced it with the right approach, which is to manage all capabilities in a single place (the App ID).

That leads to this error:

  1. No profiles for 'com.company.AppName' were found - Xcode couldn't find any Developer ID provisioning profiles matching 'com.company.AppName'.

This could be phrased better, but the issue here is the same as the manual case. That is, the configuration on the portal doesn't match the configuration your app was signed with, so Xcode can't/won't use that profile.

Somewhat ironically, the easiest way to fix these issues is the same in Xcode 15 and 16. Turn on automatic codesign'ing and configure the entitlements you need in the Capability interface. If you're using Xcode 15 that interaction may look pretty broken, as some of the capabilities you add will end up adding two entries in the capability section when there should only be one. That behavior is the visible symptom of Xcode 15s issue with DEXT. It's correctly enabling the entitlement on the portal, but it doesn't understand that the entitlements "collide", so it ends up mishandling profile generation.

The key point is that you shouldn't try to fix archive signing issues at the archive level, you need to move "back" to the earlier archive and fix in there.

Once the portal is corrected:

  • Xcode 16 should handle everything with automatic signing.

  • In Xcode 15, you need to manually generate provisioning profiles which you'd then select during export.

One tip I've found helpful on the manual provisioning side. On a machine that's been used for a long time, I've seen profiles "build up" to the point where it becomes very difficult to pick the right profile. These profile files are actually located in

~/Library/Developer/Xcode/UserData/Provisioning Profiles/

...and you can move/delete them to clear out the "cruft" so you can be sure it's doing the right thing.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

One undocumented requirement is that the name of the dext must match the dext's bundle identifier. This is most likely a bug, and I have submitted a Feedback Report for it (FB15590713), but in the meantime please make sure that your dext's "PRODUCT_NAME" matches "$(PRODUCT_BUNDLE_IDENTIFIER)" in the dext target's "Build Settings" tab.

This was noticed by the following developer forum thread as well: https://developer.apple.com/forums/thread/133499?answerId=422125022#422125022

It is documented that the bundle identifier of the dext must be just an extension beyond the bundle identifier of the dext-hosting application.

For example, if the Dext-hosting application's bundle ID is "com.company.DextHostingApp" then the dext's bundle ID must be "com.company.DextHostingApp.<extra>" where <extra> can be whatever you want, but the leading section must match the owning application's bundle ID.

I can tell you my experience with a Camera extension, and with a USBDriverKit extension.

I believe the requirement that the kext's bundle ID begin with the bundleID of the hosting app is for iOS/iPadOS only. There isn't such a restriction for the dexts that I know about (but networking or security extensions might have different rules). That said, I see that I actually obey that rule with my projects, and it doesn't do any harm.

Your dext has a managed entitlement for PCI. Your Account Owner has to make a profile for that on the developer portal - an Admin can't (but won't see a helpful error message, last time I tried as an Admin).

You can leave all the Xcode settings that are working for development as they are. Try not to fiddle with Info.plist files or Build Settings directly if there is a way to set the same settings using the Info tab for your target, or the General tab, or the Signing and Capabilities tab, because those feed into the Build Settings.

in my main app and dext targets, the Provisioning Profile is "Xcode managed profile" and the Signing Certificate is a Development one. That's fine, it is all going to re-signed for distribution.

When you want to build for distribution from Xcode,

  • archive your build.
  • choose Distribute
  • choose the Custom distribution method
  • select Direct Distribution
  • select the Export destination
  • use the Manually Manage Signing option

at this point, you'll be able to choose a Developer ID Application certificate, and the appropriate profiles for you app and its extension.

If you're doing all this from a script, you have to figure out what the right commands are which correspond to the steps in the dialogs.

Good Luck!

I can't take you any further with this, because I gladly ceded signing and notarizing tasks to our DevOps team, so I don't have the signing certificates with the private keys on my Mac.

AFAIR, I selected "None" for my app's Profile, it doesn't need anything special, while the driver has managed entitlements and I do need to set its profile explicitly.

After a successful signing step here, you can use notarytool to notarize the app, and attach a ticket to it.

There's a post from Kevin Elliot here about this https://forums.developer.apple.com/forums/thread/751490?answerId=787624022.

So, let me start here:

The debug versions of software include DriverKit (development) capability (under App ID configuration at developer.apple.com), and this apparently must not be present in distributable provisioning. I believe this means that different provisioning needs to occur between debug and release builds?

Not exactly. What the "debug" entitlement variants actually provide is a broad entitlement configuration (basically, they'll match "anything") that's ONLY available in the development provisioning profile. This did cause issue in Xcode 15, however:

  • The issues were specifically tied to the family entitlement variations, which are customized for each developer, not the "generic" entitlement (like the basic DriverKit entitlement).

  • The PCI family has never had a "development" variant, which means this issue didn't really effect PCI "only" drivers.

  • These issues were basically resolved by iOS 16.

Commenting on my own comments here:

There's a post from Kevin Elliot here about this https://forums.developer.apple.com/forums/thread/751490?answerId=787624022.

The post above describes how to workaround Xcode 15's issues, however, Xcode 16 has basically resolved "all" of these issues. That is, using Xcode 16:

  • It correctly shows the configuration options available on your account and correctly applies them when you configure in Xcode "Signing & Capabilities" tab. Assuming the account has the necessary permission, it's possible to fully a configure a DEXT without using the developer portal.

  • Automatic code-signing works as expected.

  • The export process is able to correctly export all build types.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you both for the responses.

We have accomplished the Team Agent request for entitlements for PCI DriverKit - it looks like I am able to perform all other tasks necessary at my "admin" level on the developer portal.

Following ssmith_c's advice, I was able to build, sign, notarize, and deploy a dext within a hosting application, the added export and manual selection seemed to be the difference I needed.

However, I'm still encountering some issues with Xcode 15.2 when trying to make a distributable application that works with the dext (#3 of the software types listed above). One worked, and one seemed to not work. The "not working" one is crashing based on:

Exception Type:  EXC_CRASH (SIGKILL (Code Signature Invalid))
Exception Codes: 0x0000000000000000, 0x0000000000000000
Termination Reason: CODESIGNING 1 Taskgated Invalid Signature

Using codesign to inspect the signature, it seems ok, but I likely am just not seeing the issue:

daniek3@MacBook-Pro Project % codesign -dv --verbose=3 AppName.app
Executable=/Users/daniek3/repository/Project/AppName.app/Contents/MacOS/AppName
Identifier=com.company.AppName
Format=app bundle with Mach-O universal (x86_64 arm64)
CodeDirectory v=20500 size=319557 flags=0x10000(runtime) hashes=9975+7 location=embedded
Hash type=sha256 size=32
CandidateCDHash sha256=94ec693fa2c5c43167f1e8005a93d6405361072f
CandidateCDHashFull sha256=94ec693fa2c5c43167f1e8005a93d6405361072fa22fd9750416d50890c41f4e
Hash choices=sha256
CMSDigest=94ec693fa2c5c43167f1e8005a93d6405361072fa22fd9750416d50890c41f4e
CMSDigestType=2
CDHash=94ec693fa2c5c43167f1e8005a93d6405361072f
Signature size=8980
Authority=Developer ID Application: Company (<Team ID>)
Authority=Developer ID Certification Authority
Authority=Apple Root CA
Timestamp=Oct 29, 2024 at 2:23:36 PM
Info.plist entries=22
TeamIdentifier=<Team ID>
Runtime Version=14.2.0
Sealed Resources version=2 rules=13 files=3
Internal requirements count=1 size=220

As noted, however, this was in Xcode 15.2, so I'll move to Xcode 16 and give it another try.

Trying the same app that is having difficulty in the last post (above) in Xcode 16.1.

From the Organizer, choosing to distribute app as "Custom->Direct Distribution->Export->Development Team (selected)->Automatically Manage Signing" gives the following two errors:

  1. There is a problem with the request entity - You already have a current Developer ID Application Managed (with Kext) certificate or a pending certificate request.
  2. No profiles for 'com.company.AppName' were found - Xcode couldn't find any Developer ID provisioning profiles matching 'com.company.AppName'.

Having previously developed kexts for our hardware, yes, our developer certificate has the "kext" qualifier. This should be normal for most developers moving from kext to dext development. There absolutely is a provisioning profile for com.company.AppName - as will be shown in the next step.

If instead I use the choices: From the Organizer, choosing to distribute app as "Custom->Direct Distribution->Export->Development Team (selected)->Manually Manage Signing" and select "Distribution Certificate: Default (..)" and "AppName.app: <provisioning profile>" I get a new error that reads:

  • Profile doesn't match the entitlements file's value for the com.apple.developer.driverkit.userclient-access entitlement.

This error seems poorly articulated as well, since the entitlements file requires I enter the Bundle ID of the dext that the application will communicate with, but there is no place to declare that in the developer portal when making the provisioning profile.

Should I open a technical support incident to get this more closely looked at? This app works when built in debug, but I am not getting the settings correct to be able to distribute it.

I encountered the same problem as you and the same error message. I started an Apple DTS to ask for a solution, but so far I have only received a reply from the Apple robot. I use Xcode16, and Xcode16.1 but there is no difference.

Like you, I previously encountered a similar error in Xcode 16.0 beta - entered as FB15239007 (this pertains to the kext-related developer certificate error).

I've additionally submitted FB15634854 to describe the "Profile doesn't match the entitlements file's value for the com.apple.developer.driverkit.userclient-access entitlement." error to Apple.

FWIW, I completely rebuilt the project, thinking perhaps that I had gotten into a bad customization spot with the previous one, but I am still unable to get a combination that works in Xcode 16.1 for this.

Stepping back for a moment, I think it's important to understand what's actually going on here. The starting point here is that there are two sets of data at work here:

  • (1) Your Xcode project has a set of data about your app, particularly the entitlement list, which are embedded in the codes signature of your app. That data can actually be viewed with the command:
codesign -dvvv --ent :- <path>
  • (2) The developer portal has a set of data about apps bundle ID, particularly the entitlement list, which is used to generate a provisioning profile which will be embedded inside your apps bundle. That data can actually be viewed with one of the following commands*:

*iOS and macOS use slightly different names and locations, a fact which is annoying when you jump back and forth between platforms. I wrote a script years ago which simply tried all 4 possibilities and am now working hard to not remember which is which.

security cms -D -i <path>/embedded.mobileprovision
security cms -D -i <path>/embedded.provisionprofile
security cms -D -i <path>/Contents/embedded.mobileprovision
security cms -D -i <path>/Contents/embedded.provisionprofile

All of the issues here boil down to "the data in those two files doesn't match". That leads to the difference between "Automatic" and "Manual":

  • Automatic-> Xcode will interact with the dev portal, adjusting the dev portal configuration (if it can) and generating/downloading provisioning profiles as needed.

  • Manual-> Xcode will (try) use whatever provisioning profiles you tell it to.

What's changed over time is Xcode's involvement with "adjusting the dev portal configuration". Originally, it didn't really do ANY adjustment. You'd configure the data on the Dev Portal and Xcode would use that data. Between Xcode v14 -> v16, Xcode expanded it's role, with the goal being that Xcode would be able to handle "all" of this configuration. That evolution is also what made Xcode 15 so problematic for DEXT. Xcode 15 added support for configuring managed capabilities (which is what DEXTs rely on) but it DIDN'T properly handle the particular configuration DEXTs use.

The key point here is that manual shouldn't actually "different" than automatic in an fundamental way, as all automatic should be generating exactly the same profile manual would.

That leads to the errors you're seeing. First off, this one:

  1. There is a problem with the request entity - You already have a current Developer ID Application Managed (with Kext) certificate or a pending certificate request.

I don't know what's causing that, but I don't believe it's directly related to this issue. I suspect it's a side effect of how your archive was originally signed and will self correct once other issues are resolved.

That leads to the other two errors, which are actually the same underlying error. First the manual error:

Profile doesn't match the entitlements file's value for the com.apple.developer.driverkit.userclient-access entitlement.

In other words, you pointed Xcode at a provisioning profile ("2") which didn't match apps signing ("1") configuration. You were actually on the right track here;

there is no place to declare that in the developer portal when making the provisioning profile.

Historically, the portal added a mechanism that could artificially "inject" a single "chunk" of entitlement data into the profile being generated. This was a poor solution that didn't scale which is why we replaced it with the right approach, which is to manage all capabilities in a single place (the App ID).

That leads to this error:

  1. No profiles for 'com.company.AppName' were found - Xcode couldn't find any Developer ID provisioning profiles matching 'com.company.AppName'.

This could be phrased better, but the issue here is the same as the manual case. That is, the configuration on the portal doesn't match the configuration your app was signed with, so Xcode can't/won't use that profile.

Somewhat ironically, the easiest way to fix these issues is the same in Xcode 15 and 16. Turn on automatic codesign'ing and configure the entitlements you need in the Capability interface. If you're using Xcode 15 that interaction may look pretty broken, as some of the capabilities you add will end up adding two entries in the capability section when there should only be one. That behavior is the visible symptom of Xcode 15s issue with DEXT. It's correctly enabling the entitlement on the portal, but it doesn't understand that the entitlements "collide", so it ends up mishandling profile generation.

The key point is that you shouldn't try to fix archive signing issues at the archive level, you need to move "back" to the earlier archive and fix in there.

Once the portal is corrected:

  • Xcode 16 should handle everything with automatic signing.

  • In Xcode 15, you need to manually generate provisioning profiles which you'd then select during export.

One tip I've found helpful on the manual provisioning side. On a machine that's been used for a long time, I've seen profiles "build up" to the point where it becomes very difficult to pick the right profile. These profile files are actually located in

~/Library/Developer/Xcode/UserData/Provisioning Profiles/

...and you can move/delete them to clear out the "cruft" so you can be sure it's doing the right thing.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

I thought an example of the command line output might be useful, so here is the output of my specific test app:

Command:

codesign -dvvv --ent :- HIDKeyboardApp.app

Entitlement block at end of output:

....
<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>com.apple.application-identifier</key><string>VKPFXJZWAV.com.appledts.kevine.KeyboardApp</string><key>com.apple.developer.system-extension.install</key><true/><key>com.apple.developer.team-identifier</key><string>VKPFXJZWAV</string><key>com.apple.security.app-sandbox</key><true/><key>com.apple.security.files.user-selected.read-only</key><true/></dict></plist>

Command:

security cms -D -i HIDKeyboardApp.app/Contents/embedded.provisionprofile

Entitlement block at end of output:

....
	<key>Entitlements</key>
	<dict>
				
				<key>com.apple.developer.system-extension.install</key>
		<true/>
				
				<key>com.apple.application-identifier</key>
		<string>VKPFXJZWAV.com.appledts.kevine.KeyboardApp</string>
				
				<key>keychain-access-groups</key>
		<array>
				<string>VKPFXJZWAV.*</string>
		</array>
				
				<key>com.apple.developer.team-identifier</key>
		<string>VKPFXJZWAV</string>

	</dict>
	<key>ExpirationDate</key>
	<date>2042-10-07T18:15:36Z</date>
	<key>Name</key>
	<string>Mac Team Direct Provisioning Profile: com.appledts.kevine.KeyboardApp</string>
	<key>ProvisionsAllDevices</key>
	<true/>
	<key>TeamIdentifier</key>
	<array>
		<string>VKPFXJZWAV</string>
	</array>
	<key>TeamName</key>
	<string>Kevin Elliott</string>
	<key>TimeToLive</key>
	<integer>6570</integer>
	<key>UUID</key>
	<string>339032ce-3f1c-4b56-888f-ffcb9ae14caf</string>
	<key>Version</key>
	<integer>1</integer>
</dict>
</plist> 

Command:

codesign -dvvv --ent :- HIDKeyboardApp.app/Contents/Library/SystemExtensions/com.appledts.kevine.KeyboardApp.dext.dext

Entitlement block at end of output:

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "https://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict><key>com.apple.application-identifier</key><string>VKPFXJZWAV.com.appledts.kevine.KeyboardApp.dext</string><key>com.apple.developer.driverkit</key><true/><key>com.apple.developer.driverkit.family.hid.eventservice</key><true/><key>com.apple.developer.driverkit.transport.hid</key><true/><key>com.apple.developer.driverkit.transport.usb</key><array><dict><key>idProduct</key><integer>0</integer></dict></array><key>com.apple.developer.team-identifier</key><string>VKPFXJZWAV</string></dict></plist>

Command:

security cms -D -i HIDKeyboardApp.app/Contents/Library/SystemExtensions/com.appledts.kevine.KeyboardApp.dext.dext

Entitlement block at end of output:

...											
	<key>Entitlements</key>
	<dict>
				
				<key>com.apple.developer.driverkit</key>
		<true/>
				
				<key>com.apple.developer.driverkit.transport.hid</key>
		<true/>
				
				<key>com.apple.developer.driverkit.family.hid.eventservice</key>
		<true/>
				
				<key>com.apple.application-identifier</key>
		<string>VKPFXJZWAV.com.appledts.kevine.KeyboardApp.dext</string>
				
				<key>keychain-access-groups</key>
		<array>
				<string>VKPFXJZWAV.*</string>
		</array>
				
				<key>com.apple.developer.team-identifier</key>
		<string>VKPFXJZWAV</string>
				
				<key>com.apple.developer.driverkit.transport.usb</key>
		<array>
						<dict>
				
				<key>idProduct</key>
		<integer>0</integer>
						</dict>
		</array>

	</dict>
	<key>ExpirationDate</key>
	<date>2042-10-26T22:32:17Z</date>
	<key>Name</key>
	<string>Mac Team Direct Provisioning Profile: com.appledts.kevine.KeyboardApp.dext</string>
	<key>ProvisionsAllDevices</key>
	<true/>
	<key>TeamIdentifier</key>
	<array>
		<string>VKPFXJZWAV</string>
	</array>
	<key>TeamName</key>
	<string>Kevin Elliott</string>
	<key>TimeToLive</key>
	<integer>6570</integer>
	<key>UUID</key>
	<string>9d763743-3526-42da-8913-6583d57a14ec</string>
	<key>Version</key>
	<integer>1</integer>
</dict>
</plist> 

Footnotes:

  • Yes, I apparently ended my dext bundle id with "dext", making the file name "*.dext.dext". Yes, I wasn't really aware of that till now. Yes, this is dumb and I'd encourage you to do better.

  • The astute reader will notice that DEXT and apps have different profile paths and may assume that I, hypocritically, remembered which one was which and ran the correct command. I asure you that was not the case. My script ran all 4 possibilities and I counted back to figure out which one worked. This is a detail I've strongly committed to not remembering.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Thank you Kevin for the explanations and the tools to distinguish which element contributed to which piece. It's taken a few days, but I've figured out a few things and can hopefully get this all settled.

First, I had changed the bundle ID of my dext to what it "should be", after learning that the bundle ID ought to be an extension of the "owning" application's bundle ID.

It turns out you can't do that from an Admin role. I kept looking at the output of the security command and seeing the older bundle ID showing up for com.apple.developer.driverkit.userclient-access., which was not the updated bundle ID I was developing now.

Backing up to this point of the thread:

That leads to the other two errors, which are actually the same underlying error. First the manual error:

Profile doesn't match the entitlements file's value for the com.apple.developer.driverkit.userclient-access entitlement.

In other words, you pointed Xcode at a provisioning profile ("2") which didn't match apps signing ("1") configuration. You were actually on the right track here;

there is no place to declare that in the developer portal [what the dext bundle ID is] when making the provisioning profile.

Your Team Agent must request DriverKit entitlements and know in advance the bundle ID of the dext you intend to create. That's how the provisioning profile (2) from the developer portal knows the bundle ID of the dext that your application wants to create a user client for. Having this more clearly documented would be helpful.

Next post I'll explain the settings that I am currently using - there are still a few lingering issues which most likely need new forum threads, so I won't initiate those conversations on this thread, but I do want to wrap this up with a clear description so that others don't hit the same walls that I did.

Accepted Answer

For this explanation, I am using Xcode 15.x. The hosting application will use com.company.HostingApp as its bundle ID and com.company.HostingApp.Driver as the dext's bundle ID, and then a second app with bundle ID com.company.ClientOnlyApp which communicates with the dext but is not the hosting application.

As noted above, the driver bundle ID of com.company.HostingApp.Driver needed to be requested from the Team Agent at the developer portal. Once that's completed, you still need to create the Identifier to be used, by going to Identifiers and creating one that matches.

Dext Identifier settings:

  • DriverKit Allow Third-Party User Clients ("Capabilities" tab - I need this, perhaps you do not)
  • DriverKit ("Additional Capabilities" tab)
  • DriverKit Allow Any UserClient Access ("Additional Capabilities" tab)
  • DriverKit PCI (Primary Match) ("Additional Capabilities" tab) (I'm making a PCI driver, so I need this)

Hosting App Identifier settings:

  • DriverKit Communicates with Drivers ("Capabilities" tab)
  • System Extension ("Capabilities" tab)
  • DriverKit ("Additional Capabilities" tab)
  • DriverKit UserClient Access ("Additional Capabilities" tab)

Client Only Identifier settings:

  • DriverKit Communicates with Drivers ("Capabilities" tab)
  • DriverKit ("Additional Capabilities" tab)
  • DriverKit UserClient Access ("Additional Capabilities" tab)

Once these are built, you can move to Profiles and create the provisioning profiles necessary. Since the settings are covered in the Identifiers, creating a Provisioning file only required selecting Distribution - Developer ID, then selecting the App ID (aka Identifier), then under Certificate choose your Developer Team's Certificate, and finally under Additional Entitlements there is a drop-box to select DriverKit and System Extension Template for <TeamID> Mac Dev ID profile / Mac Direct Dist Profile (yours may not be named this, but mine is).

And in the Xcode Project -> Target -> Signing and Capabilities, I have the following selections:

Dext Target Signing and Capabilities

  • Automatically manage signing checked
  • Team <Developer Team selected>
  • Bundle Identifier: com.company.HostingApp.Driver
  • DriverKit (x2)*
  • DriverKit (Allow Any UserClient Access)
  • DriverKit (PCI Primary Match) (x2)*
  • (*=these are due to Xcode 15's duplication of capabilities)

Hosting App Target Signing and Capabilities

  • Automatically manage signing checked
  • Team <Developer Team selected>
  • Bundle Identifier: com.company.HostingApp
  • App Sandbox
  • Hardened Runtime
  • System Extension

Client Only App Target Signing and Capabilities

  • Automatically manage signing checked
  • Team <Developer Team selected>
  • Bundle Identifier: com.company.ClientOnlyApp
  • App Sandbox
  • Hardened Runtime
  • System Extension

Once all of those settings are done, I can produce distributable dext software and applications that work with the dext.

Order of steps to produce distributable software:

  1. Hosting application's Xcode's Product -> Archive menu option
  2. Xcode's Window -> Organizer, select the hosting application archive that was just built, and press the Distribute App button
  3. Choose Custom
  4. Choose Developer ID - distribute directly to customers
  5. Choose Export - sign and export without notarizing
  6. Choose Manually manage signing
  7. Select Distribution certificate for your Developer Team, Hosting App provisioning file for the hosting app, and Dext provisioning file for the dext.
  8. Click Export
  9. Use notarytool and staple to complete the notarization process.

Repeat these steps for the Client Only application as well.

Notes & Questions:

  • I know that I do not have DriverKit UserClient Access selected in the Xcode Project -> Target -> Signing and Capabilities selection for either the hosting application or the client only application. This seems to be in keeping with Kevin Elliot's example, and the current situation is working for me, perhaps because the dext is set to "allow any UserClient Access"?
  • System Extension Redistributable is an entitlement that means your dext (or other system extension) is allowed to be distributed by another Developer Team. This is not a feature that I needed, but perhaps you might.
  • It is unclear what a Developer Team needs to do when the team must deploy multiple dexts - it seems like the system is built for a single dext authorization (see above post where the Team Agent must register the dext bundle ID) - how would a team select between different dexts?
  • Hosting applications must be placed in /Applications/, and directly there, not in a sub-directory. Given that requirement, it seems that an installer package is still the best option for distributing the host application, so that it is installed properly on the user's hard drive?

It turns out you can't do that from an Admin role. I kept looking at the output of the security command and seeing the older bundle ID showing up for com.apple.developer.driverkit.userclient-access., which was not the updated bundle ID I was developing now.

FYI, this is one of the pitfalls of manual codesigning, as automatic codesigning will not allow that. That's actually the biggest issue with manual codesigning- it allows you to force configuration that won't actually work, so unless you understand EXACTLY why automatic is failing, you can easily end up replacing an error at signing with a different error somewhere "else".

First, I had changed the bundle ID of my dext to what it "should be", after learning that the bundle ID ought to be an extension of the "owning" application's bundle ID.

This is common practice and what Xcode does by default, but I don't believe the system actually requires it, as it needlessly restricts/complicates what's possible without any real benefit. It's been awhile since I've specifically tested this, but it's definitely possible to create an app that doesn't follow this requirement and I believe it will work fine.

Your Team Agent must request DriverKit entitlements and know in advance the bundle ID of the dext you intend to create.

That is not correct, at least not for DEXT creation itself. I think there were a few early approvals that were tied to specific bundle IDs and there may be special cases where we still do this, but it's NOT the normal practice. The standard approach is that your team is granted the entitlement and can add that entitlement to any bundle ID it wants to.

What IS true:

That's how the provisioning profile (2) from the developer portal knows the bundle ID of the dext that your application wants to create a user client for. Having this more clearly documented would be helpful.

...is that the user client entitlement IS tied to a specific bundle ID.

Moving to your next post:

Once that's completed, you still need to create the Identifier to be used, by going to Identifiers and creating one that matches.

Xcode 15 & 16 will do all of this through it's capabilities section, both creating the bundle ID and enabling the entitlement.

...finally under Additional Entitlements there is a drop-box to select DriverKit and System Extension Template for <TeamID> Mac Dev ID profile / Mac Direct Dist Profile (yours may not be named this, but mine is).

This step is not necessary and will not exist for many developers. The UI above is the original process used to "attach" managed entitlements to specific bundle IDs and was actually replaced by the "Additional Capabilities" tab you used above. You do not need to do so and it's effect will either:

  • Neutral, as the system should be smart enough to compare the existing entitlement set (configured through "Identifiers-> Additional Capabilities") and ignore any duplicates.

  • Harmful, as it will allow you to add entitlements to the profile that aren't configured on the identifier. That will then cause problems later, particularly with automatic codesigning.

Order of steps to produce distributable software: ... Choose Manually manage signing

Again, just to be clear, manual codesigning is ONLY required here due to bugs in Xcode 15. Beyond that, in Xcode 16 is is possible to complete this ENTIRE process without accessing the portal at ALL. Xcode 16's capability interface will create and configure the app record on the developer portal and automatic codesigning will sign thing properly.

Use notarytool and staple to complete the notarization process

Xcode 16 (and I think 15) will also handle notarization.

I know that I do not have DriverKit UserClient Access selected in the Xcode Project -> Target -> Signing and Capabilities selection for either the hosting application or the client only application. This seems to be in keeping with Kevin Elliot's example, and the current situation is working for me, perhaps because the dext is set to "allow any UserClient Access"?

This part is complicated as there are multiple, different, mechanisms at work. Starting with the DEXT side:

  1. By default, the initial connection system only allows connection requests from processes that are signed by the same Team ID as the DEXT. The "allow any UserClient Access" entitlement overrides that behavior allowing the "request" from any process. If "your DEXT" only talks to "your app/processes", then you don't need any entitlement to allow.

  2. I used the term "request" because the DEXT itself has complete, dynamic control (at runtime) of what it actually accepts connections from. Note that I really do mean "complete and dynamic". If you want to create a DEXT UserClient that only accepts every other request on Tuesdays, that is entirely possible. This is a complicated topic on it's own so if you want to discuss that, lets start a new thread on that subject.

Moving to the app/process side, the system ALSO restricts the ability for user processes to connect to DEXT/KEXTS.

  • "com.apple.developer.driverkit.userclient-access" is the direct entitlement that restricts this. It says "the app with this entitlement can connect to this DEXT bundle ID".

  • This restriction system is part of app sandbox system, so a non-sandboxed app can attempt to connect to "anything".

  • Again, this ONLY controls what the user space process can "attempt". An app can be allowed to connect (either of the two factors above) but still fail to actually create a working user client (because of 1 or 2 above).

This seems to be in keeping with Kevin Elliot's example, and the current situation is working for me

I suspect your app isn't sandboxed.

It is unclear what a Developer Team needs to do when the team must deploy multiple dexts - it seems like the system is built for a single dext authorization (see above post where the Team Agent must register the dext bundle ID) - how would a team select between different dexts

This is a complicated topic as well, so kick off another thread if you really want to dig into it. However, in brief:

  1. DEXT (just like KEXTs) can basically be "arbitrarily" complex objects. In many cases, the simplest option is to simply ship a single DEXT that supports multiple devices/configuration.

  2. The "com.apple.developer.driverkit.userclient-access" entitlement is an array, so it can contain multiple bundle IDs when the same app needs to talk to multiple DEXTs.

it seems that an installer package is still the best option for distributing the host application

You can do that, but my own preference would be for the app to:

  • Attempt the installation.

  • If it fails, check it's location and tell the user if they need to move the app.

Attempting first covers issues like policy changes on our side or more complex configurations that were created or overlooked.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

Hi Kevin,

First, thank you for your patience on all of this.

It turns out you can't do that from an Admin role. I kept looking at the output of the security command and seeing the older bundle ID showing up for com.apple.developer.driverkit.userclient-access., which was not the updated bundle ID I was developing now.

FYI, this is one of the pitfalls of manual codesigning, as automatic codesigning will not allow that. That's actually the biggest issue with manual codesigning- it allows you to force configuration that won't actually work, so unless you understand EXACTLY why automatic is failing, you can easily end up replacing an error at signing with a different error somewhere "else".

That's the issue, though. I don't know EXACTLY why automatic codesigning is failing - at least with manual code signing I can look at what settings I am using and try something else. If I just press "automatic" and it doesn't work (it hasn't yet), there does not seem to be anything further I can do to affect the problem - I am just stuck.

Next:

First, I had changed the bundle ID of my dext to what it "should be", after learning that the bundle ID ought to be an extension of the "owning" application's bundle ID.

This is common practice and what Xcode does by default, but I don't believe the system actually requires it, as it needlessly restricts/complicates what's possible without any real benefit. It's been awhile since I've specifically tested this, but it's definitely possible to create an app that doesn't follow this requirement and I believe it will work fine.

Ok, that's helpful information.

Next:

What IS true:

That's how the provisioning profile (2) from the developer portal knows the bundle ID of the dext that your application wants to create a user client for. Having this more clearly documented would be helpful.

...is that the user client entitlement IS tied to a specific bundle ID.

Got it. That failed in the automatic process, though - it kept bringing in the old dext bundle ID, and it was only AFTER involving the Team Agent and registering a new dext bundle ID that I got the correct results (via manual code signing and ignoring automatic).

This next piece might be the a-ha moment we've been looking for:

...finally under Additional Entitlements there is a drop-box to select DriverKit and System Extension Template for <TeamID> Mac Dev ID profile / Mac Direct Dist Profile (yours may not be named this, but mine is).

This step is not necessary and will not exist for many developers. The UI above is the original process used to "attach" managed entitlements to specific bundle IDs and was actually replaced by the "Additional Capabilities" tab you used above. You do not need to do so and it's effect will either:

  • Neutral, as the system should be smart enough to compare the existing entitlement set (configured through "Identifiers-> Additional Capabilities") and ignore any duplicates.
  • Harmful, as it will allow you to add entitlements to the profile that aren't configured on the identifier. That will then cause problems later, particularly with automatic codesigning.

OH. Ok, since it was presented, and I only had that option or "default" (which implied to not provide ANY entitlement), I chose the option which looked like it would do something. Thank you for clarifying, and glad I was detailing every step for us to find this.

As for whether or not your own app needs an entitlement to communicate to your own dext:

This part is complicated as there are multiple, different, mechanisms at work. Starting with the DEXT side:

  • By default, the initial connection system only allows connection requests from processes that are signed by the same Team ID as the DEXT. The "allow any UserClient Access" entitlement overrides that behavior allowing the "request" from any process. If "your DEXT" only talks to "your app/processes", then you don't need any entitlement to allow.

I know I have particular use cases that others will not, and for the moment I'm just trying to establish the correct rules to develop and deploy, outside of the App Store, a dext, it's owning application, and supporting applications.
For this particular dext, I need to allow "any userclient" to connect. It looks like the reason my apps can connect without defining the "userclient access" entitlement is because they share the same TeamID as the dext.

  • I used the term "request" because the DEXT itself has complete, dynamic control (at runtime) of what it actually accepts connections from. Note that I really do mean "complete and dynamic". If you want to create a DEXT UserClient that only accepts every other request on Tuesdays, that is entirely possible. This is a complicated topic on it's own so if you want to discuss that, lets start a new thread on that subject.

No, I don't need a follow-up thread. This I understand, I have two kinds of user clients and can dynamically control whether a connection is established or not, I have that working already. This thread is just for what codesign/entitlements/notarization is needed to actually deploy a dext outside of the App Store.

I suspect your app isn't sandboxed.

It is sandboxed - I'm in fact having trouble with accessing a user preference file thanks to the sandboxing (and spaces in the directory names), but the connection to the dext has no issues. I think it's due to the above bullet point about the TeamIDs matching, then.

Re: multiple dexts - understood, glad to hear that "com.apple.developer.driverkit.userclient-access" entitlement is an array. Should I be creating that entry in App.entitlements as an array with a single string entry? I was creating it as a string with the bundleID of the dext when I was trying it.

Re: creating an installer package:

  • Attempt the installation.
  • If it fails, check its location and tell the user if they need to move the app.

Thank you - this is a cleaner solution that is already functioning, and avoids concerns about installer codesigning/notarization breaking the application's/dext's settings.

I understand the repeated statements that Xcode 16 automatic codesigning, archiving, notarization, and deployment should work - but they have not done so for me to date. I will give it yet another try, now that I know what steps are necessary to get a manual configuration working properly.

That failed in the automatic process, though - it kept bringing in the old dext bundle ID, and it was only AFTER involving the Team Agent and registering a new dext bundle ID that I got the correct results

So, the issue here is that automatic works by having Xcode modify the portal, so if you're account type can't modify the portal configuration, Xcode can't fix it. You can fix this in two ways:

  1. You can have the Team Agent log in to Xcode and configure the project, which will then cause Xcode to modify the portal.

  2. You can modify the portal configuration "yourself" so that it's configuration matches what it "should".

This is ultimately a business decision, but my personal experience has been that out development "flow" works MUCH better with admin accounts. With a large company (which obviously has concerns about "legitimate" app distribution), my own inclination would be to have two different accounts- a "development account" where all developers could be admins but which never actually shipped software and a "company account" where access was tightly restricted and which was actually used to ship software. This makes it very easy for developers to test/experiment/configure products. The company account only includes the products you're actually shipping and you add new products by changing the bundle ID of the development build and copying the bundle ID configuration (in the portal) of the development build over the portal of the company account.

On the manual codesign side of this:

OH. Ok, since it was presented, and I only had that option or "default" (which implied to not provide ANY entitlement), I chose the option which looked like it would do something.

If you take a look at our portal roles documentation, it sounds like you may not have been granted the "right" level of access for this to work smoothly. The general idea here is that the "higher level" users should be configuring the project correctly, which will then propagate to the lower level users. Manual codesiging won't really get you around that.

__
Kevin Elliott
DTS Engineer, CoreOS/Hardware

"How to" for dext distribution
 
 
Q