Code Signing Guide
Code signing verifies your app’s authenticity and integrity. It’s required for distribution on macOS (especially for notarization) and recommended on Windows for user trust.
Overview
Forge provides code signing through:
- Automatic signing during
forge bundle(via manifest configuration) - Manual signing with
forge signcommand - Environment variables for CI/CD integration
macOS Code Signing
Prerequisites
- Apple Developer Program membership ($99/year)
- Xcode Command Line Tools:
xcode-select --install - Developer ID Application certificate in Keychain
Setting Up Your Signing Identity
- Create a Developer ID certificate in Apple Developer Portal
- Download and install in Keychain Access
- Verify installation:
security find-identity -v -p codesigningYou should see output like:
1) ABC123DEF "Developer ID Application: Your Name (TEAMID)"Manifest Configuration
Configure signing in your manifest.app.toml:
[bundle.macos]sign = truesigning_identity = "Developer ID Application: Your Name (TEAMID)"team_id = "TEAMID"
# Optional: custom entitlementsentitlements = "entitlements.plist"
# Optional: notarization (requires signing)notarize = true
# App Store categorycategory = "public.app-category.developer-tools"
# Minimum macOS versionminimum_system_version = "12.0"Notarization
Apple notarization is required for apps distributed outside the App Store on macOS 10.15+.
One-time setup - store your credentials:
xcrun notarytool store-credentials forge-notarizeYou’ll be prompted for:
- Apple ID email
- Team ID (from Apple Developer Portal)
- App-specific password (generate at appleid.apple.com)
Enable notarization in manifest:
[bundle.macos]sign = truenotarize = trueteam_id = "TEAMID"signing_identity = "Developer ID Application: Your Name (TEAMID)"Build and Bundle
# Build and bundle with automatic signing + notarizationforge build .forge bundle .Output:
Creating macOS app bundle... Building release binary with embedded assets... Generating Info.plist... Generating icon... Signing app bundle... Creating DMG... Submitting for notarization... Notarization successful Stapled notarization ticket
App bundle: bundle/MyApp.app DMG: bundle/MyApp-1.0.0-macos.dmgManual Signing
Sign an existing artifact:
forge sign ./bundle/MyApp.app --identity "Developer ID Application: Your Name (TEAMID)"With notarization:
export FORGE_TEAM_ID="TEAMID"export FORGE_NOTARIZE=1forge sign ./bundle/MyApp-1.0.0-macos.dmg --identity "Developer ID Application: Your Name (TEAMID)"Ad-hoc Signing (Development)
For local testing without a certificate:
forge sign ./bundle/MyApp.app --identity "-"Ad-hoc signed apps will show security warnings to users and cannot be notarized.
Windows Code Signing
Prerequisites
- Windows SDK with SignTool (install via Visual Studio Installer or standalone)
- Code signing certificate (.pfx file)
- Purchase from a Certificate Authority (DigiCert, Sectigo, etc.)
- Or use a self-signed certificate for testing
SignTool Location
Forge automatically searches for SignTool in:
- System PATH
C:\Program Files (x86)\Windows Kits\10\bin\<version>\x64\signtool.exeC:\Program Files (x86)\Windows Kits\10\bin\<version>\x86\signtool.exe
Creating a Test Certificate
For development/testing only:
# Generate self-signed certificate$cert = New-SelfSignedCertificate ` -Type CodeSigningCert ` -Subject "CN=My Company" ` -KeyExportPolicy Exportable ` -CertStoreLocation Cert:\CurrentUser\My
# Export to .pfx file$pwd = ConvertTo-SecureString -String "your-password" -Force -AsPlainTextExport-PfxCertificate -Cert $cert -FilePath "cert.pfx" -Password $pwdWarning: Self-signed certificates will trigger SmartScreen warnings. Use a trusted CA certificate for production.
Manifest Configuration
[bundle.windows]format = "msix"sign = truecertificate = "cert.pfx"password = "$CERT_PASSWORD" # References environment variablepublisher = "CN=My Company, O=My Company, C=US"min_version = "10.0.17763.0"capabilities = ["internetClient"]Password options:
password = "$ENV_VAR"- Read from environment variable (recommended)password = "literal"- Literal password (avoid in version control)
Build and Bundle
# Set certificate passwordexport CERT_PASSWORD="your-password"
# Build and bundleforge build .forge bundle .Manual Signing
export FORGE_SIGNING_PASSWORD="your-password"forge sign ./bundle/MyApp-1.0.0.msix --identity "cert.pfx"Linux Code Signing
Linux code signing is optional and uses GPG for creating detached signatures.
Prerequisites
- GPG installed and configured
- A GPG key pair
Creating a GPG Key
gpg --gen-keySigning
Linux AppImages are signed automatically if GPG is available:
forge bundle .Or manually:
forge sign ./bundle/MyApp-1.0.0.AppImage --identity "your.email@example.com"This creates a detached signature at MyApp-1.0.0.AppImage.sig.
Verification
Users can verify:
gpg --verify MyApp-1.0.0.AppImage.sig MyApp-1.0.0.AppImageNote: GPG signing is optional. If GPG is not available or signing fails, the bundle process continues without error.
Environment Variables
Use environment variables for CI/CD integration:
| Variable | Platform | Description |
|---|---|---|
FORGE_SIGNING_IDENTITY | All | Signing identity (certificate path or name) |
CODESIGN_IDENTITY | macOS | Fallback for signing identity |
FORGE_SIGNING_PASSWORD | Windows | Certificate password |
FORGE_TEAM_ID | macOS | Apple Developer Team ID |
FORGE_NOTARIZE | macOS | Enable notarization (any value) |
CI/CD Example (GitHub Actions)
jobs: build-macos: runs-on: macos-latest steps: - uses: actions/checkout@v4
- name: Import certificate env: CERTIFICATE_BASE64: ${{ secrets.APPLE_CERTIFICATE }} CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} run: | echo $CERTIFICATE_BASE64 | base64 --decode > certificate.p12 security create-keychain -p "" build.keychain security import certificate.p12 -k build.keychain -P "$CERTIFICATE_PASSWORD" -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple: -s -k "" build.keychain
- name: Setup notarization credentials env: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} APPLE_APP_PASSWORD: ${{ secrets.APPLE_APP_PASSWORD }} run: | xcrun notarytool store-credentials forge-notarize \ --apple-id "$APPLE_ID" \ --team-id "$APPLE_TEAM_ID" \ --password "$APPLE_APP_PASSWORD"
- name: Build and bundle env: FORGE_SIGNING_IDENTITY: "Developer ID Application: Your Name (TEAMID)" FORGE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} FORGE_NOTARIZE: "1" run: | forge build . forge bundle .
build-windows: runs-on: windows-latest steps: - uses: actions/checkout@v4
- name: Import certificate env: CERTIFICATE_BASE64: ${{ secrets.WINDOWS_CERTIFICATE }} run: | $bytes = [Convert]::FromBase64String($env:CERTIFICATE_BASE64) [IO.File]::WriteAllBytes("cert.pfx", $bytes)
- name: Build and bundle env: FORGE_SIGNING_IDENTITY: "cert.pfx" FORGE_SIGNING_PASSWORD: ${{ secrets.WINDOWS_CERTIFICATE_PASSWORD }} run: | forge build . forge bundle .CLI Reference
forge sign
Sign a bundled artifact.
forge sign [OPTIONS] <ARTIFACT>Arguments:
<ARTIFACT>- Path to artifact (.app,.dmg,.msix,.exe,.AppImage)
Options:
--identity <IDENTITY>or-i- Signing identity
Examples:
# macOS bundleforge sign MyApp.app --identity "Developer ID Application: Name (TEAMID)"
# macOS DMG with notarizationFORGE_NOTARIZE=1 FORGE_TEAM_ID=TEAMID forge sign MyApp.dmg --identity "Developer ID..."
# Windows MSIXFORGE_SIGNING_PASSWORD=secret forge sign MyApp.msix --identity cert.pfx
# Linux AppImageforge sign MyApp.AppImage --identity user@example.comTroubleshooting
macOS
“codesign: command not found”
- Install Xcode Command Line Tools:
xcode-select --install
“No identity found”
- Verify certificate is in Keychain:
security find-identity -v -p codesigning - Ensure the full identity string matches exactly
“Notarization failed”
- Verify credentials:
xcrun notarytool store-credentials forge-notarize - Check Apple Developer account status
- Ensure hardened runtime is enabled (automatic with Forge)
“The signature is invalid”
- Re-sign with
--forceflag (automatic with Forge) - Check for modified files after signing
Windows
“SignTool not found”
- Install Windows SDK via Visual Studio Installer
- Add SignTool to PATH or let Forge auto-discover
“Certificate not valid for code signing”
- Ensure certificate has Code Signing EKU
- Check certificate expiration date
SmartScreen warnings
- Use an EV (Extended Validation) certificate for immediate trust
- Standard certificates build reputation over time
Linux
“gpg: command not found”
- Install GPG:
apt install gnupgor equivalent - GPG signing is optional; builds continue without it
“No secret key”
- Ensure GPG key exists:
gpg --list-secret-keys - Generate a key:
gpg --gen-key