macOS Signing & Notarization
Code sign and notarize your Hakobu binaries for macOS distribution.
Overview
macOS Gatekeeper blocks unsigned executables from running without manual user intervention. Hakobu supports three signing tiers for macOS binaries:
| Tier | What Happens | When to Use |
|---|---|---|
| Ad-hoc (default) | codesign --sign - -- runs on the build machine; users must right-click and Open | Local development, CI fixtures |
| Developer ID | Signed with your Apple Developer certificate and hardened runtime | Internal distribution |
| Notarized | Developer ID signed + Apple notary ticket stapled | Public distribution |
Prerequisites
Apple Developer Program membership
You need an Apple Developer Program membership ($99/year). Free accounts cannot notarize.
Developer ID certificate
- Open Keychain Access, then go to Certificate Assistant and select Request a Certificate
- In the Apple Developer portal, create a Developer ID Application certificate
- Download and install it in your Keychain
- Verify the certificate is available:
security find-identity -v -p codesigningYou should see output like:
1) ABCDEF... "Developer ID Application: Your Name (TEAMID)"App-specific password (for notarization)
Apple notarization requires an app-specific password, not your regular Apple ID password:
- Go to appleid.apple.com
- Sign in, then navigate to Security, then App-Specific Passwords, and select Generate
- Save the password -- you will need it for the
HAKOBU_APPLE_PASSWORDenvironment variable
Sign with Developer ID
Pass your signing identity via the --sign-identity flag or the HAKOBU_SIGN_IDENTITY environment variable:
hakobu ./my-app --output ./dist/app \
--sign-identity "Developer ID Application: Your Name (TEAMID)"Or set the environment variable once:
export HAKOBU_SIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)"
hakobu ./my-app --output ./dist/appSign and Notarize
To notarize, set your Apple credentials as environment variables and add the --notarize flag:
export HAKOBU_SIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)"
export HAKOBU_APPLE_ID="you@example.com"
export HAKOBU_APPLE_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export HAKOBU_APPLE_TEAM_ID="ABCDE12345"
hakobu ./my-app --output ./dist/app --notarizeThis performs the full pipeline automatically:
- Packages the executable
- Signs with your Developer ID identity (hardened runtime + secure timestamp)
- Submits to Apple's notary service
- Waits for approval (typically 1--5 minutes)
- Staples the notarization ticket to the executable
App Bundle Mode
When --app-bundle is combined with signing or notarization, Hakobu signs and notarizes the .app bundle rather than the raw executable:
export HAKOBU_SIGN_IDENTITY="Developer ID Application: Your Name (TEAMID)"
export HAKOBU_APPLE_ID="you@example.com"
export HAKOBU_APPLE_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export HAKOBU_APPLE_TEAM_ID="ABCDE12345"
hakobu ./my-app --app-bundle --output ./dist/MyApp.app --notarizeThe signing target changes depending on the mode:
| Step | Raw Executable | .app Bundle |
|---|---|---|
| Signing target | Executable | .app directory (codesign --deep) |
| Notarization zip | Executable zipped | .app directory zipped |
| Stapling target | Executable | .app directory |
Environment Variables
| Variable | Required For | Description |
|---|---|---|
HAKOBU_SIGN_IDENTITY | Developer ID signing | Full identity string from security find-identity |
HAKOBU_APPLE_ID | Notarization | Apple ID email address |
HAKOBU_APPLE_PASSWORD | Notarization | App-specific password |
HAKOBU_APPLE_TEAM_ID | Notarization | 10-character Team ID from Apple Developer portal |
CLI Flags
| Flag | Description |
|---|---|
--sign-identity <id> | Code-signing identity (overrides HAKOBU_SIGN_IDENTITY) |
--notarize | Submit to Apple notarization after signing |
Behavior When Credentials Are Absent
| Scenario | Behavior |
|---|---|
| No signing identity | Ad-hoc signing (default, always works) |
--sign-identity without --notarize | Developer ID signed, not notarized |
--notarize without credentials | Error listing the missing environment variables |
Non-macOS target with --notarize | Flag is ignored (notarization is macOS-only) |
--app-bundle without signing identity | Bundle is ad-hoc signed |
CI/CD Setup
To sign and notarize in GitHub Actions, store your certificate and credentials as repository secrets:
- name: Install signing certificate
env:
P12_BASE64: ${{ secrets.APPLE_DEVELOPER_ID_P12 }}
P12_PASSWORD: ${{ secrets.APPLE_DEVELOPER_ID_PASSWORD }}
run: |
echo "$P12_BASE64" | base64 --decode > cert.p12
security create-keychain -p "" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "" build.keychain
security import cert.p12 -k build.keychain -P "$P12_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain
rm cert.p12
- name: Package and notarize
env:
HAKOBU_SIGN_IDENTITY: "Developer ID Application: Your Name (TEAMID)"
HAKOBU_APPLE_ID: ${{ secrets.APPLE_ID }}
HAKOBU_APPLE_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
HAKOBU_APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
run: |
npx hakobu ./my-app --output ./dist/app --notarizeVerifying a Signed Binary
# Check the code signature
codesign -dv --verbose=4 ./my-app
# Check the notarization ticket
xcrun stapler validate ./my-app
# Check Gatekeeper assessment
spctl --assess --type execute -vv ./my-appTroubleshooting
"The signature of the binary is invalid"
The Mach-O __LINKEDIT segment must be patched correctly before signing. Hakobu does this automatically. If you see this error, the patching may have failed -- file a bug.
"Unable to sign the macOS executable"
Ensure codesign is available (Xcode Command Line Tools). On CI runners without Xcode:
xcode-select --installNotarization rejected
Run the notarytool log command shown in the error output to see Apple's rejection reasons. Common issues include missing hardened runtime (Hakobu adds this automatically) and network issues during submission.
Checking notarization status manually
xcrun notarytool history --apple-id EMAIL --password PASS --team-id TEAM
xcrun notarytool log SUBMISSION_ID --apple-id EMAIL --password PASS --team-id TEAM