export(
appdir = "my-app",
destdir = "build",
sign = TRUE
)Without code signing, macOS Gatekeeper blocks your app (“unidentified developer”) and Windows SmartScreen warns on every download (“unrecognized app”). This guide covers what you need, what it costs, and how to set it up. For broader Electron security topics, see Security Considerations.
Why Code Signing Matters
Unsigned apps still work once users bypass the warning, but signing matters for:
- User trust: Security warnings scare non-technical users away.
- Enterprise deployment: Many organizations block unsigned software via policy.
- Auto-updates: macOS and Windows require signed builds for electron-updater to verify update integrity.
| macOS | Windows | Linux | |
|---|---|---|---|
| Required? | Strongly recommended | Strongly recommended | Optional |
| Cost | $99/year (Apple Developer) | $200–$700/year (CA cert) | Free (GPG) |
| Certificate | Developer ID Application | OV or EV code signing | GPG key |
| Notarization | Yes (macOS 10.15+) | N/A | N/A |
macOS: Code Signing and Notarization
What You Need
- Apple Developer Program membership ($99/year) at developer.apple.com
- Developer ID Application certificate — this is different from a Mac App Store certificate. Create one in the Apple Developer portal under Certificates, Identifiers & Profiles.
- App-specific password for notarization (generated at appleid.apple.com)
Setup
Export your Developer ID Application certificate as a .p12 file from Keychain Access, then set these environment variables:
# Certificate (path to .p12 file, or base64-encoded content)
export CSC_LINK="/path/to/certificate.p12"
export CSC_KEY_PASSWORD="your-certificate-password"
# Notarization credentials
export APPLE_ID="your@apple.id"
export APPLE_APP_SPECIFIC_PASSWORD="xxxx-xxxx-xxxx-xxxx"
export APPLE_TEAM_ID="XXXXXXXXXX"Then export your app with signing enabled:
electron-builder handles the entire signing and notarization flow automatically when these environment variables are present. You do not need to run codesign or xcrun notarytool yourself.
You can also configure signing behavior in _shinyelectron.yml (credentials should still come from environment variables):
signing:
sign: true
mac:
identity: "Developer ID Application: Your Name (TEAMID)"
team_id: "XXXXXXXXXX"
notarize: trueTo open an unsigned .app on macOS during development, clear the quarantine attribute (xattr -cr /path/to/YourApp.app) or right-click and choose Open.
Windows: Code Signing
What You Need
An OV or EV code signing certificate from a CA (DigiCert, Sectigo, GlobalSign, SSL.com). EV certificates (~$300–$700/year) provide immediate SmartScreen trust. OV certificates (~$200–$500/year) build reputation gradually — users may still see warnings for the first few weeks.
Setup
# Path to your .pfx certificate file (or base64-encoded content)
export CSC_LINK="/path/to/certificate.pfx"
export CSC_KEY_PASSWORD="your-certificate-password"Then build with signing:
export(
appdir = "my-app",
destdir = "build",
platform = "win",
sign = TRUE
)Unsigned Windows builds trigger SmartScreen on every download with no workaround.
Linux: Optional GPG Signing
Linux distributions do not enforce code signing for desktop apps. However, AppImage files can be GPG-signed so users can verify authenticity.
Setup
export GPG_KEY="your-gpg-key-id"signing:
sign: true
linux:
gpg_sign: trueThis is optional and only matters if your users explicitly verify signatures.
The sign Parameter
export(..., sign = TRUE) tells electron-builder to sign using available credentials. It passes signing configuration to package.json, sets the macOS identity, enables notarization (if configured), and includes the Windows certificate path. It does not create certificates or error on missing credentials — it warns, then electron-builder proceeds (and may fail at the signing step).
The function parameter overrides the config file: export(..., sign = TRUE) enables signing even if _shinyelectron.yml says sign: false. You can also enable signing solely via config (signing: sign: true).
Use app_check("my-app", sign = TRUE) to verify your signing setup before building.
Using with GitHub Actions
Store credentials as GitHub Actions secrets (Settings > Secrets and variables > Actions), then reference them in your workflow.
| Secret Name | Value |
|---|---|
MAC_CERTIFICATE |
Base64-encoded .p12 file (base64 -i cert.p12) |
MAC_CERTIFICATE_PASSWORD |
Password for the .p12 file |
APPLE_ID |
Your Apple ID email |
APPLE_APP_SPECIFIC_PASSWORD |
App-specific password from appleid.apple.com |
APPLE_TEAM_ID |
10-character team ID from developer.apple.com |
WIN_CERTIFICATE |
Base64-encoded .pfx file |
WIN_CERTIFICATE_PASSWORD |
Password for the .pfx file |
Reference secrets in your workflow:
- name: Build Electron app
env:
# macOS
CSC_LINK: ${{ secrets.MAC_CERTIFICATE }}
CSC_KEY_PASSWORD: ${{ secrets.MAC_CERTIFICATE_PASSWORD }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# Windows
# CSC_LINK: ${{ secrets.WIN_CERTIFICATE }}
# CSC_KEY_PASSWORD: ${{ secrets.WIN_CERTIFICATE_PASSWORD }}
run: |
Rscript -e "
shinyelectron::export(
appdir = 'app',
destdir = 'build',
sign = TRUE
)
"Important
macOS and Windows use different certificates, so
CSC_LINKandCSC_KEY_PASSWORDmust point to the correct certificate for each platform. In a matrix build, set these per-platform using conditional env blocks or separate build steps.
For a complete workflow template, see GitHub Actions.
Development vs. Production
| Development | Production | |
|---|---|---|
sign |
FALSE (default) |
TRUE |
| Credentials needed? | No | Yes |
| Build speed | Fast | Slower (notarization takes 1–5 minutes) |
| OS warnings? | Yes (use xattr -cr on macOS) |
No |
A typical workflow:
- Develop and test with
sign = FALSE - Set up signing credentials once
- Use
sign = TRUE(orsigning: sign: truein config) for release builds - Automate signed builds with GitHub Actions
Quick Reference
| Environment Variable | Platform | Purpose |
|---|---|---|
CSC_LINK |
macOS, Windows | Path to .p12/.pfx certificate (or base64) |
CSC_KEY_PASSWORD |
macOS, Windows | Certificate password |
APPLE_ID |
macOS | Apple ID for notarization |
APPLE_APP_SPECIFIC_PASSWORD |
macOS | App-specific password for notarization |
APPLE_TEAM_ID |
macOS | 10-character Apple Developer Team ID |
GPG_KEY |
Linux | GPG key ID for AppImage signing |
Next Steps
- GitHub Actions: Automate signed builds in CI/CD
- Auto Updates: Signed builds are required for auto-updates on macOS and Windows
-
Configuration: Full
_shinyelectron.ymlreference including signing options