Skip to content

Signing and Notarizing

Karthikeyan Marappan edited this page Mar 16, 2026 · 1 revision

Signing & Notarizing for Release

This page covers everything you need to do to produce a signed, notarized, and Gatekeeper-accepted release build of AxM Jamf Sync.


Prerequisites

  • An active Apple Developer Program membership (individual or organisation)
  • Xcode 15 or later
  • A Developer ID Application certificate in your keychain
  • xcrun notarytool credentials (App Store Connect API key or Apple ID + app-specific password)

Step 1 — Configure signing in Xcode

  1. Open AxMJamfSync.xcodeproj
  2. Select the AxMJamfSync target → Signing & Capabilities
  3. Set Team to your Apple Developer team
  4. Set Bundle Identifier — keep com.karthikmac.axmjamfsync or change it to match your Developer ID
  5. Set Signing Certificate to Developer ID Application (not Development or Distribution)
  6. Confirm the entitlements file path is set: AxMJamfSync/AxMJamfSync.entitlements

Step 2 — Build an Archive

xcodebuild archive \
  -project AxMJamfSync.xcodeproj \
  -scheme AxMJamfSync \
  -configuration Release \
  -archivePath build/AxMJamfSync.xcarchive \
  CODE_SIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)"

Or use Product → Archive in Xcode.


Step 3 — Export the app

In the Organizer (Xcode → Window → Organizer), select your archive and click Distribute App:

  1. Choose Developer ID
  2. Choose Export
  3. Set signing to Developer ID Application
  4. Enable Hardened Runtime ✓ (required for notarization)
  5. Export to a folder — you'll get an .app bundle

Or from the command line:

xcodebuild -exportArchive \
  -archivePath build/AxMJamfSync.xcarchive \
  -exportPath build/export \
  -exportOptionsPlist ExportOptions.plist

Where ExportOptions.plist contains:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "...">
<plist version="1.0">
<dict>
    <key>method</key>
    <string>developer-id</string>
    <key>signingStyle</key>
    <string>manual</string>
    <key>signingCertificate</key>
    <string>Developer ID Application</string>
    <key>teamID</key>
    <string>YOUR_TEAM_ID</string>
</dict>
</plist>

Step 4 — Verify the signature

codesign --verify --deep --strict --verbose=2 build/export/AxMJamfSync.app

# Should print: AxMJamfSync.app: valid on disk
#               AxMJamfSync.app: satisfies its Designated Requirement

Also check the entitlements were embedded:

codesign --display --entitlements - build/export/AxMJamfSync.app

You should see com.apple.security.app-sandbox, com.apple.security.network.client, and com.apple.security.files.user-selected.read-write.


Step 5 — Submit for notarization

Using App Store Connect API key (recommended)

xcrun notarytool submit build/export/AxMJamfSync.app \
  --key /path/to/AuthKey_XXXXXXXXXX.p8 \
  --key-id XXXXXXXXXX \
  --issuer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
  --wait \
  --output-format plist

Using Apple ID

xcrun notarytool submit build/export/AxMJamfSync.app \
  --apple-id your@email.com \
  --password xxxx-xxxx-xxxx-xxxx \
  --team-id YOUR_TEAM_ID \
  --wait

The --wait flag blocks until notarization completes (usually 1–5 minutes). Remove it and use --no-wait + xcrun notarytool log <submission-id> to poll manually.


Step 6 — Staple the notarization ticket

xcrun stapler staple build/export/AxMJamfSync.app

Stapling embeds the notarization ticket into the app bundle so Gatekeeper can verify it even without an internet connection.

Verify:

xcrun stapler validate build/export/AxMJamfSync.app
# Should print: The validate action worked!

Step 7 — Create a DMG for distribution

# Create a writable DMG
hdiutil create -size 50m -volname "AxM Jamf Sync" -srcfolder build/export/AxMJamfSync.app \
  -ov -format UDZO build/AxMJamfSync.dmg

Sign the DMG too:

codesign --sign "Developer ID Application: Your Name (TEAMID)" build/AxMJamfSync.dmg

Notarize the DMG (Gatekeeper checks the DMG itself before mounting):

xcrun notarytool submit build/AxMJamfSync.dmg \
  --key /path/to/AuthKey_XXXXXXXXXX.p8 \
  --key-id XXXXXXXXXX \
  --issuer xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
  --wait

xcrun stapler staple build/AxMJamfSync.dmg

Final check — Gatekeeper

On another Mac that has never seen this app:

spctl --assess --type exec --verbose build/export/AxMJamfSync.app
# Should print: AxMJamfSync.app: accepted
#               source=Notarized Developer ID

Entitlements reference

The app's AxMJamfSync.entitlements contains exactly:

Entitlement Purpose
com.apple.security.app-sandbox Required for Mac App Store and Gatekeeper trust
com.apple.security.network.client Outbound connections to Apple APIs and Jamf Pro
com.apple.security.files.user-selected.read-write Private key file picker + CSV export save dialog
keychain-access-groups Read/write credentials in the app's Keychain group

Hardened Runtime flags

The app does not require any Hardened Runtime exceptions — no JIT, no unsigned memory execution, no resource access beyond what's in the entitlements. This makes notarization straightforward.


Privacy manifest

PrivacyInfo.xcprivacy is included in the bundle (already in project.pbxproj). It declares:

  • NSPrivacyTracking = false
  • NSPrivacyCollectedDataTypes = [] (no data collected)
  • Required Reasons APIs: UserDefaults (CA92.1) and FileTimestamp (C617.1)

Apple's notarization pipeline scans for this file starting with Xcode 15. Without it, submission may be rejected with a privacy manifest warning.

Clone this wiki locally