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!