macOS Signing Guide
How Chatons builds, signs, and notarizes macOS artifacts.
See also: Manual Signing Instructions
Build System
macOS artifacts are built with Electron Builder, configured in package.json > build.
Key build settings:
| Setting | Value |
|---|---|
appId | com.thibaut.chaton |
productName | Chatons |
mac.hardenedRuntime | true |
mac.gatekeeperAssess | false |
mac.entitlements | build/entitlements.mac.plist |
mac.entitlementsInherit | build/entitlements.mac.plist |
mac.type | distribution |
mac.notarize | false (overridden by CI when secrets are available) |
mac.target | dmg |
dmg.artifactName | ${productName}-latest-${arch}.${ext} |
Output directory: release/
CI Workflow (.github/workflows/build-all-platforms.yml)
The CI workflow handles the complete signing and notarization pipeline.
Steps
- Check whether signing secrets are available
- Check whether notarization secrets are also available
- Import the signing certificate into a temporary keychain
- Build renderer and Electron main process
- Run Electron Builder for macOS DMG
- Validate produced DMGs and enclosed app bundles
- Retry stapling when notarization propagation is slow
Secrets
| Secret | Purpose |
|---|---|
MAC_CERTIFICATE or MACOS_CERTIFICATE_P12_BASE64 | Signing certificate (base64-encoded P12) |
MAC_CERTIFICATE_PWD or MACOS_CERTIFICATE_P12_PASSWORD | Certificate password |
MACOS_KEYCHAIN_PASSWORD | Temporary keychain password |
MAC_DEVELOPER_ID | Developer ID certificate name |
APPLE_ID | Apple ID for notarization |
APPLE_APP_SPECIFIC_PASSWORD | App-specific password for notarization |
APPLE_TEAM_ID | Apple team identifier |
Signing Modes
| Certificate secrets | Notarization secrets | Result |
|---|---|---|
| Missing | -- | Unsigned build |
| Present | Missing | Signed-only build |
| Present | Present | Signed and notarized build |
Entitlements
build/entitlements.mac.plist currently grants:
| Entitlement | Why |
|---|---|
com.apple.security.cs.allow-jit | JIT compilation for V8/Electron |
com.apple.security.cs.allow-unsigned-executable-memory | Electron runtime requirement |
com.apple.security.cs.allow-dyld-environment-variables | Dynamic library loading |
com.apple.security.cs.disable-executable-page-protection | Electron runtime requirement |
com.apple.security.network.client | Outbound network access (API calls) |
com.apple.security.network.server | Local server (OAuth callbacks on port 1455) |
Local Helper Scripts
scripts/build-signed.sh
Shell script for local signed builds. Expects environment variables:
| Variable | Required | Description |
|---|---|---|
APPLE_TEAM_ID | Yes | Team ID |
APPLE_SIGNING_IDENTITY | Yes | Certificate name |
CSC_LINK | No | Path to .p12 certificate file |
APPLE_ID | No | For notarization |
APPLE_ID_PASSWORD | No | For notarization |
export APPLE_TEAM_ID="ABCDE12345"
export APPLE_SIGNING_IDENTITY="Developer ID Application: Your Name (ABCDE12345)"
bash scripts/build-signed.sh
scripts/build-signed.js
Node helper that reads from build/config.js and optional .env, then runs Electron Builder.
Important Note
These local scripts are convenience tools. The CI workflow is the authoritative release path. It handles more edge cases, validation, and retry logic.
Verification
After a signed build:
# Verify app signature
codesign --verify --deep --strict release/mac/Chatons.app
# Check Gatekeeper assessment
spctl -a -t exec -vv release/mac/Chatons.app
# Verify DMG (if notarized)
spctl -a -v -t install release/Chatons-latest-arm64.dmg
# Check stapler ticket
xcrun stapler validate release/Chatons-latest-arm64.dmg
Artifact Naming
The DMG artifact name is:
Chatons-latest-arm64.dmg
Based on the pattern ${productName}-latest-${arch}.${ext}. The semver version number does not appear in the filename by default.
Recommended Approach
| Scenario | Use |
|---|---|
| Real releases | GitHub Actions workflow |
| Preparation / debugging | Local helper scripts |
| Direct low-level control | Manual Signing Instructions |
Related
- Manual Signing Instructions -- Step-by-step manual signing
- Semantic Versioning -- Version bump policy