I'm working on a multi-process macOS application (based on Chromium/Electron) that uses Mach ports for inter-process communication between the main app and its helper processes.
Background
I have an MAS build working successfully via TestFlight for internal testing. However, public TestFlight testing requires Apple review, and while waiting for that review, I wanted to provide a directly distributable build for external testers. I attempted to create a Developer ID signed build with App Sandbox enabled, expecting it to behave similarly to the MAS build.
The Problem
With App Sandbox enabled (com.apple.security.app-sandbox) and identical entitlements, I observe different behavior depending on the signing certificate:
- Apple Distribution certificate: App launches successfully,
mach-registerandmach-lookupwork - Developer ID certificate: App crashes at launch,
mach-registeris denied by sandbox
The Console shows this sandbox violation for the Developer ID build:
Sandbox: MyApp(13605) deny(1) mach-register XXXXXXXXXX.com.mycompany.myapp.MachPortRendezvousServer.13605
The crash occurs when the app calls bootstrap_check_in() to register a Mach service for child process communication.
What I've tried
-
Adding
com.apple.security.temporary-exception.mach-register.global-namewith wildcard patternXXXXXXXXXX.com.mycompany.myapp.MachPortRendezvousServer.*to the main app's entitlements - this resolved themach-registerdenial. -
However, helper processes then fail with
mach-lookupdenial. Addingcom.apple.security.temporary-exception.mach-lookup.global-namewith the same wildcard pattern to the main app's entitlements (for inheritance) does not work.
Analysis of /System/Library/Sandbox/Profiles/application.sb
I examined macOS's App Sandbox profile and found that mach-register.global-name supports wildcard patterns via select-mach-filter:
(sandbox-array-entitlement
"com.apple.security.temporary-exception.mach-register.global-name"
(lambda (name)
...
(let ((mach-filter
(select-mach-filter name global-name-prefix global-name)))
(allow mach-register mach-filter))))
But mach-lookup.global-name does not - it only accepts exact names:
(sandbox-array-entitlement
"com.apple.security.temporary-exception.mach-lookup.global-name"
(lambda (name) (allow mach-lookup (global-name name))))
Since the Mach service name includes the PID (e.g., ...MachPortRendezvousServer.13605), it's impossible to specify exact names in entitlements.
I also verified that com.apple.security.application-groups grants mach-register and mach-lookup only for service names prefixed with the group ID (e.g., group.com.mycompany.myapp.), which
doesn't match the TEAMID.bundleid. prefix used by Chromium's MachPortRendezvousServer.
My questions
- What mechanism allows Apple Distribution signed apps to use mach-register and mach-lookup for these service names without temporary exceptions? I don't see any certificate-based logic in application.sb.
- Is there a way to achieve the same behavior with Developer ID signing for testing purposes?
Related threads
- https://developer.apple.com/forums/thread/747005
- https://developer.apple.com/forums/thread/685601
- https://developer.apple.com/forums/thread/128714 (confirms temporary-exception can be used freely for Developer ID apps)
Environment
- macOS 15.6 (Sequoia)
- Xcode 16.4
- Both certificates from the same Apple Developer account
The canonically correct way to manage Mach service names [1] in a sandboxed app is to make the name a ‘child’ of an app group ID. That’s what I recommend that you do here.
Temporary exception entitlements are a reasonable option when:
- You’re not targeting the Mac App Store
- No other option is available to you
I talk more about that in The Case for Sandboxing a Directly Distributed App. But in this case you are targeting the Mac App Store and there is a better option available, and hence my recommendation.
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"
[1] This includes XPC named endpoints. I recommend that folks not use Mach directly, although I realise that’s not something under your control here.