In transitioning an existing privileged helper tool from SMJobBless to the new-ish SMAppService APIs, I ran into a problem.
Registration via [SMAppService daemonServiceWithPlistName:...]; works and I get the green light via SMAppServiceStatusEnabled. Presumably that means my app’s bundle structure is correct, except that when my app creates a connection to the named mach service advertised by the helper tool, the helper tool process no longer launches on-demand.
The client side (main app) uses:
xpc_connection_create_mach_service("com.fxfactory.FxFactory.helper", queue, XPC_CONNECTION_MACH_SERVICE_PRIVILEGED);
The listener / helper tool uses:
xpc_connection_create_mach_service("com.fxfactory.FxFactory.helper", dispatch_get_main_queue(), XPC_CONNECTION_MACH_SERVICE_LISTENER);
When installed via SMJobBless, the privileged helper tool would automatically launch when a connection attempt is made by the app. This no longer works. The app sits indefinitely, never receiving a reply on its otherwise "live" xpc_connection. The only useful hints on the Console seemed to be the following:
taskgated-helper Checking profile: FxFactory Provisioning Profile 2026-1-15
taskgated-helper com.fxfactory.FxFactory.helper: Unsatisfied entitlements: com.apple.developer.service-management.managed-by-main-app
taskgated-helper Disallowing: com.fxfactory.FxFactory.helper
...and:
/Applications/FxFactory.app/Contents/MacOS/com.fxfactory.FxFactory.helper not valid: Error Domain=AppleMobileFileIntegrityError Code=-413 "No matching profile found" UserInfo={NSURL=file:///Applications/FxFactory.app/Contents/MacOS/com.fxfactory.FxFactory.helper, unsatisfiedEntitlements=<CFArray 0x7b94c33a40 [0x200d1aab0]>{type = immutable, count = 1, values = (
0 : <CFString 0x7b950305a0 [0x200d1aab0]>{contents = "com.apple.developer.service-management.managed-by-main-app"}
)}, NSLocalizedDescription=No matching profile found}
I'm testing this on macOS 27 Beta, not sure if that would/should make a difference. LLMs give a ton of contradicting advice on this topic. I would be great to clear some things out:
- In addition to having the launchd plist that describes the helper tool copied to
/Contents/Library/LaunchDaemons, should the same plist also be embedded by the helper tool binary via-sectcreate __TEXT __launchd_plist? - Is it true that
XPC_CONNECTION_MACH_SERVICE_PRIVILEGEDshould be omitted from the client, when using the new SMAppService API? (the LLM surely insisted on this point, but passing 0 didn't fix anything.) - What are the unsatisfied requirements of the
com.apple.developer.service-management.managed-by-main-appthat taskgated is referring to? Again LLMs insist that there are no additional requirements beyond code-signing by the same team, but this must be false. Could it be that helper tool needs to use the same provisioning profile as the main app? Could it be that it needs its own, tied to its own bundle ID?
Here are the entitlements on the helper tool sitting in the /Contents/MacOS/ directory of the app bundle, presumably the result of the build process injecting them into their own __TEXT section, similarly to how one would inject __launchd_plist:
[Dict]
[Key] com.apple.developer.service-management.managed-by-main-app
[Value]
[Bool] true
[Key] com.apple.security.app-sandbox
[Value]
[Bool] false
[Key] com.apple.security.get-task-allow
[Value]
[Bool] true
Assuming that my privileged helper tool is not launching simply because my bundle is violating the requirements for the com.apple.developer.service-management.managed-by-main-app entitlement, what exactly are these requirements?
As a side question: if one needs these LaunchDaemons to perform some actions with root privileges, what exactly would enabling the App Sandbox (com.apple.security.app-sandbox = true) on the privileged helper tool accomplish? Is there any point in confining a process with root privileges inside a container?
Thank you!
what exactly are these requirements?
That question makes no sense because com.apple.developer.service-management.managed-by-main-app isn’t a real entitlement, in the sense defined by Determining if an entitlement is real.
The [com.apple.developer. prefix] hints that this would only be needed during development
No, that’s not how to read this. Rather, the com.apple.developer. prefix means that:
- This entitlement is available to be used by third-party developers [1].
- But it’s restricted, that is, it must be authorised by a provisioning profile (the unrestricted entitlements have a
com.apple.security.prefix). TN3125 Inside Code Signing: Provisioning Profiles talks about these concepts in more detail.
[1] To be clear, not all entitlements available to third-party developers have this prefix. Some entitlements were created before this convention was established.
should the same plist also be embedded by the helper tool binary via -sectcreate __TEXT __launchd_plist?
No. The system creates the launchd job using the property list file as a template. The __TEXT / __launchd_plist is an SMJobBless thing. It’s not relevant to SMAppService.
Now, it might make sense to embed an Info.plist section, but more on that below.
Is it true that XPC_CONNECTION_MACH_SERVICE_PRIVILEGED should be omitted … ?
No. If you’re talking to a privileged helper, you should always set the privileged flag. XPC Resources has a link to a post where I explain this.
If one needs … to perform some actions with root privileges, what exactly would enabling the App Sandbox … accomplish?
It could potentially allow you to deploy on the Mac App Store (-:
The App Sandbox is compatible with programs running as root, with the restrictions imposed by the sandbox still applying. This is an obscure combination, but it does work and there are circumstances where you need it. For example, a Network Extension provider that’s packaged as a system extension runs as root, but NE requires that you enable the App Sandbox.
In theory such a provider should be deployable via the Mac App Store, but I don’t know if a lot of folks actually use that in practice.
When it comes to SMAppService, if the container app is sandboxed then (starting in macOS 14.2) the daemon must also be sandboxed.
In theory such a daemon would be compatible with the Mac App Store. Honestly, I’ve no idea what App Review would make of that.
If your daemon is sandboxed then it will need an Info.plist because the bundle ID in that tells the system how to set up its app container. You can give it an Info.plist either by putting it in the __TEXT / __info_plist section or by embedded the daemon in an app-like wrapper. See Signing a daemon with a restricted entitlement for advice on how to do the latter.
Generally I prefer that second option, not least because it then lets your daemon use restricted entitlements.
LLMs give a ton of contradicting advice on this topic.
Now if only your LLMs were trained to respond with “Search the forums and do what Quinn said!” (-:
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"