Thanks, Quinn.
The reason I was considering two executables was purely to minimize the amount of application code that ever runs as root. The long-lived daemon is intended to have a strict invariant that all XPC and storage work occurs only after it has permanently transitioned to the dedicated non-root account.
If using a single executable is the preferred design, I’m happy to simplify it that way. The startup sequence would then be roughly:
launchd starts the daemon as root
setgroups(exactGroupSet)
setgid(targetGID)
setuid(targetUID)
verify real/effective UID/GID and supplementary groups
only then create NSXPCListener(machServiceName:)
No XPC listener, storage connection, network activity, or other privileged resource would be opened before the credential transition.
Is that single-process pattern supported for an SMAppService LaunchDaemon with a declared MachServices entry? In particular, after the process permanently changes its UID/GID/group list but does not exec, can it still check in to its original launchd Mach service using NSXPCListener(machServiceName:)?
If so, that would remove my reason for needing the exec boundary.
I’m away from my development environment for the next few days, so I may not be able to test a suggested approach immediately, but I wanted to clarify the architectural question while the context is fresh.
Thanks again.