ABHIJAT
← Back to Writing

Developer Tools

Packaging a desktop app without losing a week to code signing

Electron's build tooling is genuinely good. Code signing on both platforms is where the schedule actually goes.

Abhijat2026-087 min read5 views

Last updated September 17, 2026

The actual app-building part of shipping an Electron app is usually the easy part — electron-builder handles packaging, auto-update manifests, and installers for all three platforms with fairly little configuration. Code signing is where a release schedule actually slips, because it's not one problem, it's a different problem on each platform, and every one of them is invisible until you try to distribute to a user who isn't you.

Why it works on your machine and nowhere else

An unsigned build runs fine on the machine that built it, because the OS trusts binaries that originated locally more than ones downloaded from the internet. The moment that same build goes to another user — downloaded from a website, sent over Slack, whatever — both macOS and Windows treat it as untrusted, and the failure isn't a helpful error message, it's Gatekeeper or SmartScreen quietly blocking the app from opening, with wording that makes it sound like the app itself is broken.

macOS: two separate steps, not one

Code signing on macOS proves the binary hasn't been tampered with since a developer built it. Notarization is a separate step: Apple's servers scan the signed binary for known malware patterns and issue a ticket that Gatekeeper checks at launch. Both are required — a signed-but-not-notarized app still gets blocked, just with a different warning than a completely unsigned one, which is a confusing failure mode the first time you hit it because the app is signed, just not notarized.

"mac": {
  "hardenedRuntime": true,
  "notarize": {
    "teamId": "YOUR_TEAM_ID"
  }
}

electron-builder handles both steps if given an Apple Developer certificate and app-specific password, but it needs the hardened runtime entitlement enabled first, which is easy to miss and produces a notarization rejection with an error message that doesn't obviously point back to that setting.

Windows: reputation, not just validity

A validly signed Windows binary can still trigger SmartScreen's "unknown publisher" warning, because SmartScreen scores based on the certificate's accumulated reputation — how many users have run binaries signed with it before — not just whether the signature is cryptographically valid. A brand-new code-signing certificate starts with no reputation, meaning early releases can still get flagged even though everything about the signature is correct. This resolves itself over time as more users run signed builds, but it's worth knowing about ahead of a first release so it doesn't look like a broken pipeline.

Doing it once, in CI

The version of this that actually holds up long-term is doing signing in a CI pipeline with certificates stored as secrets, not on a developer's laptop by hand before each release — a manual process gets skipped under deadline pressure exactly when it matters most (a rushed release), and a CI step doesn't have that failure mode. The certificates themselves (an Apple Developer ID and a Windows code-signing certificate) are the one-time setup cost; wiring them into electron-builder's CI config is comparatively little work once they exist, and it means every release is signed the same way, not "however the person doing the release remembered to do it."

Tags

ElectronDesktopDeveloper Tools