From cbae9682f7282bd603b991aa73e4c8edeeb19ee0 Mon Sep 17 00:00:00 2001 From: Michael Neale Date: Thu, 23 Oct 2025 10:12:21 +1100 Subject: [PATCH] signing builds and notarizing --- ui/desktop/.env.signing.example | 14 +++ ui/desktop/.gitignore | 2 + ui/desktop/SIGNING.md | 187 ++++++++++++++++++++++++++++++++ ui/desktop/forge.config.ts | 14 +++ 4 files changed, 217 insertions(+) create mode 100644 ui/desktop/.env.signing.example create mode 100644 ui/desktop/SIGNING.md diff --git a/ui/desktop/.env.signing.example b/ui/desktop/.env.signing.example new file mode 100644 index 0000000000..01c62ca9bc --- /dev/null +++ b/ui/desktop/.env.signing.example @@ -0,0 +1,14 @@ +# Example signing configuration for macOS +# Copy this file to .env.signing and fill in your details + +# Find your identity with: security find-identity -v -p codesigning +APPLE_IDENTITY="Developer ID Application: Your Name (TEAM_ID)" + +# Your Apple ID email +APPLE_ID="your.email@example.com" + +# Generate at: https://appleid.apple.com (Security → App-Specific Passwords) +APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx" + +# Your 10-character Team ID (found in the identity string above) +APPLE_TEAM_ID="TEAM_ID" diff --git a/ui/desktop/.gitignore b/ui/desktop/.gitignore index c3482b5af5..2e53ae74cb 100644 --- a/ui/desktop/.gitignore +++ b/ui/desktop/.gitignore @@ -9,3 +9,5 @@ src/bin/goose-npm/ /test-results/ /src/bin/temporal-service src/bin/temporal.db +# Signing credentials +.env.signing diff --git a/ui/desktop/SIGNING.md b/ui/desktop/SIGNING.md new file mode 100644 index 0000000000..021448cd01 --- /dev/null +++ b/ui/desktop/SIGNING.md @@ -0,0 +1,187 @@ +# macOS App Signing and Notarization + +This guide explains how to sign and notarize the Goose Electron app for distribution. + +## Prerequisites + +1. **Apple Developer Account** (paid, $99/year) +2. **Developer ID Application Certificate** installed in Keychain +3. **App-specific password** for notarization + +## Getting a Developer ID Certificate + +**Note:** You currently have an "Apple Development" certificate, which is for App Store development. For ad-hoc distribution outside the App Store, you need a "Developer ID Application" certificate. + +### Get Developer ID Certificate + +1. Go to +2. Click the "+" button to create a new certificate +3. Select "Developer ID Application" (under "Software") +4. Follow the instructions to create a CSR (Certificate Signing Request): + - Open Keychain Access + - Menu: Keychain Access → Certificate Assistant → Request a Certificate from a Certificate Authority + - Enter your email and name + - Select "Saved to disk" +5. Upload the CSR file +6. Download the certificate and double-click to install it in Keychain + +After installation, verify it appears: +```bash +security find-identity -v -p codesigning +``` + +You should see a line with "Developer ID Application". + +## Setup + +### 1. Find Your Certificate Identity + +```bash +security find-identity -v -p codesigning +``` + +Look for a line like: +``` +1) XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "Developer ID Application: Your Name (TEAM_ID)" +``` + +### 2. Create App-Specific Password + +1. Go to +2. Sign in with your Apple ID +3. Navigate to "Security" → "App-Specific Passwords" +4. Click "Generate password..." +5. Name it "Goose Notarization" (or similar) +6. Save the generated password (it looks like `xxxx-xxxx-xxxx-xxxx`) + +### 3. Set Environment Variables + +Create a `.env.signing` file in `ui/desktop/`: + +```bash +# Your Developer ID Application certificate name (full string from step 1) +APPLE_IDENTITY="Developer ID Application: Your Name (TEAM_ID)" + +# Your Apple ID email +APPLE_ID="your.email@example.com" + +# App-specific password from step 2 +APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx" + +# Your Team ID (the 10-character code in parentheses from step 1) +APPLE_TEAM_ID="TEAM_ID" +``` + +**Security Note:** Add `.env.signing` to `.gitignore` to keep credentials private! + +## Building with Signing + +### Option 1: Without Notarization (Faster, for testing) + +Just set `APPLE_IDENTITY`: + +```bash +cd ui/desktop +export APPLE_IDENTITY="Developer ID Application: Your Name (TEAM_ID)" +npm run bundle:default +``` + +The app will be signed but not notarized. Recipients need to right-click → Open first time. + +### Option 2: Full Signing + Notarization (Recommended for distribution) + +Set all environment variables: + +```bash +cd ui/desktop +source .env.signing # or manually export the variables +npm run bundle:default +``` + +This will: +1. Build the app +2. Sign it with your certificate +3. Submit it to Apple for notarization (takes 2-10 minutes) +4. Staple the notarization ticket to the app + +### Option 3: Using `just make-ui` + +From the project root: + +```bash +# Export your signing credentials first +export APPLE_IDENTITY="Developer ID Application: Your Name (TEAM_ID)" +export APPLE_ID="your.email@example.com" +export APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx" +export APPLE_TEAM_ID="TEAM_ID" + +# Then build +just make-ui +``` + +## Verification + +### Check if app is signed: + +```bash +codesign -dv --verbose=4 out/Goose-darwin-arm64/Goose.app +``` + +Should show your identity and entitlements. + +### Check if app is notarized: + +```bash +spctl -a -vvv -t install out/Goose-darwin-arm64/Goose.app +``` + +Should say: `accepted` and `source=Notarized Developer ID` + +## Troubleshooting + +### "No identity found" error + +- Make sure your Developer ID certificate is installed in Keychain +- Run `security find-identity -v -p codesigning` to verify + +### Notarization fails + +- Check your Apple ID and app-specific password are correct +- Ensure you have an active Apple Developer Program membership +- Check notarization logs: the build will output an ID you can use with: + ```bash + xcrun notarytool log --apple-id your.email@example.com --password xxxx-xxxx-xxxx-xxxx --team-id TEAM_ID + ``` + +### "App is damaged" error on recipient's machine + +- The app wasn't properly notarized +- Recipient can bypass with: `xattr -cr /path/to/Goose.app` (but this defeats the purpose) + +## Distribution + +After successful signing and notarization, the app is in: +``` +out/Goose-darwin-arm64/Goose.app +``` + +The zip file: +``` +out/Goose-darwin-arm64/Goose.zip +``` + +Recipients can: +1. Download and unzip +2. Drag to Applications folder +3. Double-click to run (no warnings!) + +## Ad-Hoc Distribution (No Notarization) + +If you just want to sign for yourself or trusted testers: + +1. Only set `APPLE_IDENTITY` +2. Build the app +3. Recipients run: `xattr -cr /path/to/Goose.app` once +4. Or right-click → Open first time + +This is faster but less polished for end users. diff --git a/ui/desktop/forge.config.ts b/ui/desktop/forge.config.ts index 34c91dfb82..64e9bda982 100644 --- a/ui/desktop/forge.config.ts +++ b/ui/desktop/forge.config.ts @@ -6,6 +6,20 @@ let cfg = { asar: true, extraResource: ['src/bin', 'src/images'], icon: 'src/images/icon', + // macOS signing configuration + osxSign: process.env.APPLE_IDENTITY ? { + identity: process.env.APPLE_IDENTITY, + 'hardened-runtime': true, + entitlements: 'entitlements.plist', + 'entitlements-inherit': 'entitlements.plist', + 'signature-flags': 'library' + } : undefined, + // macOS notarization configuration + osxNotarize: process.env.APPLE_ID && (process.env.APPLE_APP_SPECIFIC_PASSWORD || process.env.APPLE_ID_PASSWORD) ? { + appleId: process.env.APPLE_ID, + appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD || process.env.APPLE_ID_PASSWORD, + teamId: process.env.APPLE_TEAM_ID, + } : undefined, // Windows specific configuration win32: { icon: 'src/images/icon.ico',