PrivilegedHelperTool no longer launches automatically after SMJobBless to SMAppService

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_PRIVILEGED should 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-app that 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!

As it happens, shortly after posting I had a hunch that the com.apple.developer.service-management.managed-by-main-app entitlement may not be needed at all.

That entitlement features on the GitHub repo LLMs are likely training on: https://github.com/malpern/privileged_helper_help/tree/main

The .developer suffix hints that this would only be needed during development, but my best guess is that the fact that my main app uses a provisioning profile in both Debug and Release configuration somehow plays a role. Either way, removing that entitlement from the Helper tool suddenly allowed the connection to be started automatically.

It would be great to know the answers to the other questions, since trial-and-error sometimes leads to half-truths:

  • 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? Would this requirement, if it exists, be true for all versions of macOS where SMAppService is now available (13 > 27)?
  • Is it true that XPC_CONNECTION_MACH_SERVICE_PRIVILEGED should be omitted when creating the client connection, if the helper tool is managed via SMAppService API? My current working version has it, but if the LLM’s claim of its obsolescence is true, is this something that might break in previous/future versions of macOS, for example?
  • 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?

I don't know much about root issues with apps. But this question seems like it might be useful: https://developer.apple.com/forums/thread/792826

I think that the term "privileged helper" historically referred to an older way for apps to temporarily gain root privileges. Apple didn't like that and pushed people towards launch daemons. Lots more information here: https://developer.apple.com/forums/thread/708765

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?

Again, I'm not sure about anything involving root. But I have discovered certain benefits to sandboxing everything. If you have any part that requires (or typically uses) sandboxing, then all parts should be sandboxed. It greatly helps with interoperability between different apps or different components within apps.

If you need to break out of the sandbox, you can do that with an unsandboxed XPCService. But you can only do this with a true XPCService. For other types of XPC communication, then you can't cross the sandbox line.

Don't use the comments feature. They hide your replies.

For better or worse the App Sandbox is not a good option for our software.

That's not what I'm saying. App Sandbox opts you into a lot of modern behaviour and expectations. It's literally the default.

But all you need to do is add an unsandboxed XPC service. The whole OS, and random, unsuspecting APIs, run via XPC. You can't avoid it.

the moment your app starts using the newer APIs, a few behaviors are clearly different.

Certainly. But the old behaviours are deprecated. They may break at any time, especially if root is involved.

The nice part is that launch daemons are the intended application for much of these technologies. The official documentation assumes a launch daemon. But if you're trying to do a launch agent, then you have to cobble together information from various places and try to make it work. I did finally get it to work, but it took me a year. But you could just run through the standard examples and see if that exhibits the same behaviour.

PrivilegedHelperTool no longer launches automatically after SMJobBless to SMAppService
 
 
Q