App Sandbox denies mach-register for Developer ID signed app but allows it for Apple Distribution signed app

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-register and mach-lookup work
  • Developer ID certificate: App crashes at launch, mach-register is 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

  1. Adding com.apple.security.temporary-exception.mach-register.global-name with wildcard pattern XXXXXXXXXX.com.mycompany.myapp.MachPortRendezvousServer.* to the main app's entitlements - this resolved the mach-register denial.

  2. However, helper processes then fail with mach-lookup denial. Adding com.apple.security.temporary-exception.mach-lookup.global-name with 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

  1. 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.
  2. Is there a way to achieve the same behavior with Developer ID signing for testing purposes?

Related threads

Environment

  • macOS 15.6 (Sequoia)
  • Xcode 16.4
  • Both certificates from the same Apple Developer account
Answered by DTS Engineer in 867972022

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.

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.

Thank you, Quinn. Your suggestion about using app group IDs worked.

After investigation, I found that adding my Team ID-prefixed app group (XXXXXXXXXX.com.mycompany.myapp) to com.apple.security.application-groups in my entitlements resolves both mach-register and mach-lookup without needing any temporary exceptions.

Looking at /System/Library/Sandbox/Profiles/application.sb, I can see why this works:

(sandbox-array-entitlement
"com.apple.security.application-groups"
(lambda (suite)
  ...
  (allow mach-lookup
         mach-register
         (global-name-prefix (string-append suite ".")))
  ...))

With XXXXXXXXXX.com.mycompany.myapp as the app group, the sandbox allows any Mach service name starting with XXXXXXXXXX.com.mycompany.myapp., which matches Chromium's naming pattern: XXXXXXXXXX.com.mycompany.myapp.MachPortRendezvousServer.<pid>.

My Developer ID provisioning profile already included XXXXXXXXXX.* in its application-groups, which authorizes any Team ID-prefixed app group. I just needed to explicitly claim it in my entitlements.

For Electron developers, here are the specific changes I made to migrate a MAS build to direct distribution with App Sandbox:

  1. Set preAutoEntitlements: false in @electron/osx-sign options - The default preAutoEntitlements behavior automatically adds com.apple.developer.team-identifier to entitlements. Even though my Developer ID provisioning profile contains this entitlement, AMFI rejects it:
AMFI: Code has restricted entitlements, but the validation of its code signature failed.
Unsatisfied Entitlements: com.apple.developer.team-identifier

This appears to be another certificate-based restriction - com.apple.developer.team-identifier is only honored for Apple Distribution certificates, not Developer ID, regardless of what the provisioning profile authorizes.

  1. Add Team ID-prefixed app group to entitlements:
<key>com.apple.security.application-groups</key>
<array>
    <string>group.com.mycompany.myapp</string>
    <string>XXXXXXXXXX.com.mycompany.myapp</string>  <!-- Added -->
</array>
  1. Patch @electron/osx-sign to bypass provisioning profile platform check - By default, osx-sign rejects provisioning profiles that don't match the build platform. I patched util-provisioning-profiles.js to remove this check, allowing my Developer ID provisioning profile to be used with the MAS build.

  2. Download Developer ID provisioning profile - When using fastlane sigh, add the --developer_id flag:

sigh --platform macos --readonly -a com.mycompany.myapp --developer_id
App Sandbox denies mach-register for Developer ID signed app but allows it for Apple Distribution signed app
 
 
Q