Manual macOS Signing Instructions
Use this guide when you need direct control over signing, notarization, or debugging a signing failure.
For the higher-level overview, see the Signing Guide.
Prerequisites
- Xcode command-line tools (
xcode-select --install) - Apple Developer account with the required permissions
- A
Developer ID Applicationcertificate installed in Keychain (or provided as a.p12) - Chatons build artifacts already produced locally
Verify Signing Identities
security find-identity -v -p codesigning
1. Build Chatons
npm run build
npx electron-builder --mac dmg --publish never
This produces artifacts under release/:
release/mac/Chatons.app(app bundle)release/Chatons-latest-arm64.dmg(or similar, depending on architecture)
2. Sign the App Bundle
Replace the identity with your actual certificate name:
SIGNING_IDENTITY="Developer ID Application: Your Name (ABCDE12345)"
codesign --deep --force --verify --verbose \
--options runtime \
--entitlements build/entitlements.mac.plist \
--sign "$SIGNING_IDENTITY" \
release/mac/Chatons.app
Notes:
--deepis needed because Electron bundles contain nested binaries and frameworks--options runtimeenables hardened runtime (required for notarization)- The entitlements file must match
build/entitlements.mac.plist
Verify the Signature
codesign --verify --deep --strict release/mac/Chatons.app
spctl -a -t exec -vv release/mac/Chatons.app
3. Recreate the DMG
After signing the app, rebuild the DMG:
# Remove the old DMG if needed
rm -f release/Chatons-*.dmg
# Rebuild
npx electron-builder --mac dmg --publish never \
--config.mac.identity="$SIGNING_IDENTITY"
The default DMG naming pattern is ${productName}-latest-${arch}.${ext} (e.g. Chatons-latest-arm64.dmg). Semver does not appear in the filename by default.
4. Notarize (Optional)
If you need to notarize outside the CI workflow:
Submit for Notarization
xcrun notarytool submit release/Chatons-latest-arm64.dmg \
--apple-id "your@email.com" \
--password "your-app-specific-password" \
--team-id "ABCDE12345" \
--wait
Or using the older altool flow:
xcrun altool --notarize-app \
--primary-bundle-id "com.thibaut.chaton" \
--username "your@email.com" \
--password "your-app-specific-password" \
--file release/Chatons-latest-arm64.dmg
Check Status (altool)
xcrun altool --notarization-info UUID \
-u "your@email.com" \
-p "your-app-specific-password"
Staple the Ticket
xcrun stapler staple release/Chatons-latest-arm64.dmg
The CI workflow includes retry logic for stapling because notarization tickets may not propagate immediately.
5. Final Verification
# Verify the signed DMG
spctl -a -v -t install release/Chatons-latest-arm64.dmg
# Verify the app inside the DMG
# (mount the DMG first, then verify the enclosed .app)
codesign --verify --deep --strict /Volumes/Chatons/Chatons.app
# Verify stapler ticket
xcrun stapler validate release/Chatons-latest-arm64.dmg
Common Failures
code object is not signed at all
One or more nested binaries were not signed. Fix:
- Re-run
codesignwith--deep - Check nested frameworks and helper apps inside the Electron bundle
resource fork, Finder information, or similar detritus not allowed
Clear extended attributes:
xattr -cr release/mac/Chatons.app
Entitlements problems
Verify you are using the correct entitlements file:
codesign -d --entitlements - release/mac/Chatons.app
Compare against build/entitlements.mac.plist.
Notarization failure
Check:
- Apple ID credentials are correct
- Using an app-specific password (not your account password)
- Bundle ID matches
com.thibaut.chaton - App was properly signed before submission
When to Prefer the CI Flow
Manual signing is useful for debugging, but the automated workflow is safer for real releases. It handles:
- Consistent artifact production
- Certificate import into temporary keychain
- Post-build validation
- Notarization checks and stapling retries
- Version stamping
Related
- Signing Guide -- Overview and CI workflow details
- Semantic Versioning -- Version bump policy