-
-
Notifications
You must be signed in to change notification settings - Fork 5
Maintainer Macos Signing
Signed + notarized macOS builds are required for:
-
Gatekeeper — users can open the
.dmgwithout right-click → Open workarounds -
Auto-update — packaged macOS installs only enable
electron-updaterwhen the running app carries a Developer ID Application signature (seeelectron/updater.ts)
Windows releases can stay unsigned for now; this guide is macOS-only.
npm run signing:checkExit 0 = ready to run npm run package:mac with signing + notarization.
Exit 1 = something is missing (certificate and/or Apple notarization credentials).
You need a Developer ID Application cert in your login keychain (not just an Apple ID sign-in).
Option A — Xcode (fastest if you already use Xcode)
- Xcode → Settings → Accounts → select your team
- Manage Certificates… → + → Developer ID Application
- Re-run
npm run signing:check
Option B — Apple Developer portal + CSR
npm run signing:setupThis writes a CSR to build/macos-signing/developer_id.csr (gitignored). Then:
- Certificates → +
- Developer ID Application → upload the CSR
- Download the
.cerand double-click to install npm run signing:check
Copy the template and fill in your values:
cp .env.signing.example .env.signingOption A — Apple ID + app-specific password (simple for local releases)
| Variable | Value |
|---|---|
APPLE_ID |
Your Apple ID email |
APPLE_APP_SPECIFIC_PASSWORD |
From appleid.apple.com → Sign-In and Security → App-Specific Passwords |
APPLE_TEAM_ID |
10-character team id (Developer portal → Membership, or Xcode → Accounts) |
Option B — App Store Connect API key (better for CI)
Set APPLE_API_KEY, APPLE_API_KEY_ID, and APPLE_API_KEY_ISSUER in .env.signing instead.
.env.signing is gitignored — never commit it.
npm run signing:checkYou should see ✓ for the certificate, .env.signing, and notarization credentials.
npm run package:macscripts/electron-builder-run.mjs loads .env.signing, picks the first Developer ID Application identity in your keychain (override with CSC_NAME — use the name only, e.g. Grim Media (TEAMID), not the Developer ID Application: prefix), signs with hardened runtime + entitlements in build/entitlements.mac.plist, and notarizes via scripts/macos-notarize-after-sign.mjs when credentials are present (retries transient S3 upload failures).
Output lands in release/pkg/:
| Artifact | Role |
|---|---|
Minnow-<version>-<arch>.dmg |
User download |
Minnow-<version>-<arch>.zip |
Auto-update payload |
latest-mac.yml |
macOS update feed (attach to GitHub Releases with the zip) |
MINNOW_SKIP_SIGNING=1 npm run package:macGatekeeper will block first open; auto-update stays disabled in Settings.
The shipped macOS app is ~1 GB on disk but compresses to ~350 MB for Apple’s upload (similar to Minnow-*-arm64.zip in release/pkg/). Expect several minutes to zip locally, ~5–15 minutes to upload on a typical home connection, then more time while Apple scans. That has been true since 0.0.2 — it is not a new 0.0.3 bundle blow-up.
If a run feels much longer than your last release, the usual causes are: upload timeouts (deadlineExceeded / abortedUpload) followed by automatic retries (each retry re-uploads the whole zip), or Apple/notarytool slowness — not a dramatically larger app.
notarytool defaults to S3 Transfer Acceleration; for large Minnow builds, direct S3 (--no-s3-acceleration) is the default in our afterSign hook. Opt into acceleration with MINNOW_NOTARIZE_S3_ACCELERATION=1 only if you know it helps on your network.
To skip re-zipping when you already have an archive (e.g. release/pkg/Minnow-notarize-submit.zip):
MINNOW_NOTARIZE_ZIP=release/pkg/Minnow-notarize-submit.zip npm run signing:notarize -- release/pkg/mac-arm64/Minnow.appWhen .env.signing is configured, package:mac signs and notarizes. Notarization uploads a ~350 MB zip to Apple; uploads can fail with HTTPClientError.deadlineExceeded / abortedUpload even on good Wi‑Fi because of Apple’s client timeouts, not because Minnow grew 10× since the last release.
MINNOW_SKIP_NOTARIZATION=1 npm run package:macYou get a Developer ID–signed .dmg / .zip without waiting on Apple's notary service. For release builds, retry on a stable connection or submit manually with xcrun notarytool submit after packaging.
[ ] Bumped version in package.json
[ ] npm run signing:check passes
[ ] npm run package:mac succeeded
[ ] release/pkg/ has .dmg, .zip, latest-mac.yml (+ .blockmap if present)
[ ] GitHub release tagged v<version>
[ ] Attach macOS artifacts + latest-mac.yml
[ ] Release notes written for users
[ ] Pre-release flag set correctly (Beta vs Stable)
See also updating.md for the shared release flow.
The beta nightly workflow signs and notarizes on a GitHub macOS runner. Upload the five repository secrets it needs from a Mac where npm run signing:check passes:
npm run signing:upload-secretsscripts/upload-macos-signing-secrets.sh exports only the Developer ID Application identity for APPLE_TEAM_ID from the login keychain (macOS asks you to allow the export), repackages it as a .p12 with a random password, and sets MACOS_CERTIFICATE_P12_BASE64, MACOS_CERTIFICATE_PASSWORD, APPLE_ID, APPLE_APP_SPECIFIC_PASSWORD and APPLE_TEAM_ID via gh secret set. No values are printed or written to the repo. Re-run it after renewing the certificate or rotating the app-specific password.
| File | Used for |
|---|---|
build/entitlements.mac.plist |
Main app — JIT, native modules (node-pty, better-sqlite3), mic/camera for browser preview |
build/entitlements.mac.inherit.plist |
Helper / child processes |
If notarization fails with an entitlement error, adjust these plists and rebuild.
| Problem | What to try |
|---|---|
0 valid identities found |
Create/install Developer ID Application (not “Apple Development”) |
Please remove prefix "Developer ID Application:" |
Fixed automatically by macos-signing-env.mjs; if you set CSC_NAME manually, omit the prefix |
| Notarization fails immediately | Check .env.signing; team id must match the signing cert |
deadlineExceeded / abortedUpload during notarize |
Apple's notarytool upload to S3 timed out (Minnow's .app is large — often ~1GB). package:mac now notarizes via an afterSign hook with retries and --no-s3-acceleration on later attempts. Retry npm run package:mac, or notarize an existing signed app: npm run signing:notarize -- release/pkg/mac-arm64/Minnow.app. If uploads still fail, raise the client timeout (Xcode 16+): defaults write com.apple.gke.notary.tool nt-upload-connection-timeout 900 then verify with xcrun notarytool submit --verbose … (look for Setting S3 timeout to …). Or MINNOW_SKIP_NOTARIZATION=1 for a signed local build. |
| App opens but updater disabled | Build was unsigned or ad-hoc — use a signed package:mac build |
node-pty / native module crash on launch |
Ensure com.apple.security.cs.disable-library-validation is in both entitlements files |
Nightly macOS job fails with Missing MACOS_CERTIFICATE_P12_BASE64
|
Run npm run signing:upload-secrets (see CI signing secrets) |
Generated from documentation/. Do not edit generated pages directly.
Extensions
Developer reference
Contributing
- Accessibility and keyboard-first audit
- Minnow apps
- Architecture overview
- Command reference
- LAN companion
- Orchestrator V2 board testing
- Contributing to Minnow
- Setup from source
Design system
- CSS file map
- Layout shell
- UI primitives
- Minnow design system (current state)
- Minnow Shell
- Themes
- Design tokens
Guides
- Accessibility and keyboard-first audit
- Minnow apps
- Architecture overview
- Command reference
- Configuration & storage
- Keyboard shortcuts
- LAN companion
- Model bench: Flip Match v1
- Orchestrate board testing
- Minnow guides
- Release E2E testing guide
- Setup guide
- Troubleshooting
- Minnow wiki
Maintainers
- macOS release signing & notarization
- Prompt ownership matrix (MIN-379)
- Releasing Minnow
- Settings reference
- GitHub Wiki publishing
Apps
Chat
Core concepts
Extend Minnow
Get started
Orchestrate
Overview
Reference