HakobuHakobu

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:

TierWhat HappensWhen to Use
Ad-hoc (default)codesign --sign - -- runs on the build machine; users must right-click and OpenLocal development, CI fixtures
Developer IDSigned with your Apple Developer certificate and hardened runtimeInternal distribution
NotarizedDeveloper ID signed + Apple notary ticket stapledPublic distribution

Prerequisites

Apple Developer Program membership

You need an Apple Developer Program membership ($99/year). Free accounts cannot notarize.

Developer ID certificate

  1. Open Keychain Access, then go to Certificate Assistant and select Request a Certificate
  2. In the Apple Developer portal, create a Developer ID Application certificate
  3. Download and install it in your Keychain
  4. Verify the certificate is available:
security find-identity -v -p codesigning

You 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:

  1. Go to appleid.apple.com
  2. Sign in, then navigate to Security, then App-Specific Passwords, and select Generate
  3. Save the password -- you will need it for the HAKOBU_APPLE_PASSWORD environment 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/app

Sign 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 --notarize

This performs the full pipeline automatically:

  1. Packages the executable
  2. Signs with your Developer ID identity (hardened runtime + secure timestamp)
  3. Submits to Apple's notary service
  4. Waits for approval (typically 1--5 minutes)
  5. 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 --notarize

The signing target changes depending on the mode:

StepRaw Executable.app Bundle
Signing targetExecutable.app directory (codesign --deep)
Notarization zipExecutable zipped.app directory zipped
Stapling targetExecutable.app directory

Environment Variables

VariableRequired ForDescription
HAKOBU_SIGN_IDENTITYDeveloper ID signingFull identity string from security find-identity
HAKOBU_APPLE_IDNotarizationApple ID email address
HAKOBU_APPLE_PASSWORDNotarizationApp-specific password
HAKOBU_APPLE_TEAM_IDNotarization10-character Team ID from Apple Developer portal

CLI Flags

FlagDescription
--sign-identity <id>Code-signing identity (overrides HAKOBU_SIGN_IDENTITY)
--notarizeSubmit to Apple notarization after signing

Behavior When Credentials Are Absent

ScenarioBehavior
No signing identityAd-hoc signing (default, always works)
--sign-identity without --notarizeDeveloper ID signed, not notarized
--notarize without credentialsError listing the missing environment variables
Non-macOS target with --notarizeFlag is ignored (notarization is macOS-only)
--app-bundle without signing identityBundle is ad-hoc signed

CI/CD Setup

To sign and notarize in GitHub Actions, store your certificate and credentials as repository secrets:

.github/workflows/build.yml
- 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 --notarize

Verifying 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-app

Troubleshooting

"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 --install

Notarization 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

On this page