Electron Forge Packaging

SkillAI & models

Electron Forge build toolchain -- makers, publishers, code signing, fuses, hooks, CI/CD packaging

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Electron Forge Packaging skill

What this skill tells your AI

The instructions your AI receives, as published by agents-inc/skills in src/skills/desktop-packaging-electron-forge/SKILL.md and read by ahel’s review.

Quick Guide: Electron Forge v7 is the official Electron build toolchain. Configure via forge.config.ts with typed imports from @electron-forge/shared-types. Use makers to produce platform-specific installers (Squirrel for Windows, DMG/ZIP for macOS, deb/rpm for Linux). Use publishers to upload artifacts (GitHub Releases, S3, Snapcraft). Always code-sign production builds -- macOS requires both signing and notarization. Enable Electron Fuses to harden the binary at package time. Use hooks (prePackage, postMake) for custom build logic. Electron itself MUST be a devDependency -- Forge bundles only dependencies.


<critical_requirements>

CRITICAL: Before Using This Skill

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST place electron in devDependencies -- Forge provides the Electron binary during packaging; placing it in dependencies bloats the app by ~200MB)

(You MUST code-sign macOS builds with osxSign and osxNotarize in packagerConfig -- unsigned apps are blocked by Gatekeeper on macOS 10.15+)

(You MUST enable asar: true in packagerConfig -- without ASAR, your source code ships as plain-text files readable by any user)

(You MUST store signing credentials in environment variables -- never hardcode secrets in forge.config.ts)

(You MUST enable Fuses (FuseV1Options.RunAsNode: false, OnlyLoadAppFromAsar: true) for production security -- without them, attackers can bypass ASAR integrity and run arbitrary Node.js code)

</critical_requirements>


Auto-detection: Electron Forge, electron-forge, forge.config.ts, forge.config.js, @electron-forge, maker-squirrel, maker-dmg, maker-deb, maker-rpm, maker-zip, maker-flatpak, maker-snap, maker-appx, maker-wix, maker-pkg, maker-msix, publisher-github, publisher-s3, publisher-snapcraft, plugin-vite, plugin-webpack, plugin-fuses, FusesPlugin, osxSign, osxNotarize, electron-forge make, electron-forge publish, electron-forge package

When to use:

  • Configuring forge.config.ts for packaging and distribution
  • Choosing and configuring makers for target platforms
  • Setting up publishers for automated release distribution
  • Code signing macOS (notarization) or Windows (Authenticode) builds
  • Enabling Electron Fuses for binary hardening
  • Adding build hooks for custom pre/post-packaging logic
  • Setting up CI/CD pipelines for cross-platform builds
  • Deciding between Electron Forge and electron-builder

When NOT to use:

  • Electron app architecture (main/renderer process, IPC, preload) -- use the Electron framework skill
  • Choosing or configuring a bundler for renderer code in isolation
  • Auto-update implementation (that is an Electron framework concern, not a Forge concern)
  • UI framework selection for renderers

Key patterns covered:

  • forge.config.ts structure with typed configuration
  • Platform-specific maker selection and configuration
  • macOS code signing + notarization setup
  • Windows Authenticode signing (traditional + Azure Trusted Signing)
  • Fuses plugin for binary hardening
  • Publisher configuration (GitHub, S3, Snapcraft)
  • Build hooks and lifecycle
  • CI/CD cross-platform build matrix
  • Forge vs electron-builder decision framework

Philosophy

Electron Forge is a unified build pipeline that composes first-party Electron tools (@electron/packager, @electron/rebuild, @electron/osx-sign, @electron/notarize, @electron/fuses) into a single workflow. Rather than reimplementing build logic, Forge orchestrates existing tools through three steps:

  1. Package -- @electron/packager creates the platform-specific app bundle (.app, .exe)
  2. Make -- Makers transform the bundle into distributable installers (.dmg, .msi, .deb)
  3. Publish -- Publishers upload make artifacts to distribution targets (GitHub, S3)

Why Forge over alternatives:

  • First-party: maintained by the Electron team, receives new features (ASAR integrity, universal macOS builds) as soon as they ship
  • Composable: makers, publishers, and plugins are independent npm packages
  • TypeScript-native: forge.config.ts with full type inference since v7

Key constraint: Forge runs makers only for the current host OS by default. Cross-platform builds require CI/CD with per-platform runners (macOS for .dmg, Windows for .exe, Linux for .deb).


Core Patterns

Pattern 1: forge.config.ts Structure

The configuration file defines packaging options, makers, publishers, plugins, and hooks. All fields are optional.

import type { ForgeConfig } from "@electron-forge/shared-types";
import { FusesPlugin } from "@electron-forge/plugin-fuses";
import { FuseV1Options, FuseVersion } from "@electron/fuses";

const config: ForgeConfig = {
  packagerConfig: {
    asar: true,
    icon: "./assets/icon", // omit extension -- Forge picks .icns/.ico/.png
    name: "MyApp",
    executableName: "my-app",
    appBundleId: "com.example.myapp",
  },
  makers: [
    /* see Pattern 2 */
  ],
  publishers: [
    /* see Pattern 5 */
  ],
  plugins: [
    /* see Pattern 4 */
  ],
  hooks: {
    /* see Pattern 6 */
  },
};

export default config;

Key constraint: You cannot override dir, arch, platform, out, or electronVersion in packagerConfig -- Forge sets these internally.

See examples/core.md for full configuration with makers, signing, and fuses.


Pattern 2: Maker Selection by Platform

Each maker produces a specific installer format for a target OS. Install only the makers you need.

MakerPackagePlatformOutput
Squirrel.Windows@electron-forge/maker-squirrelWindows.exe (auto-updating)
WiX MSI@electron-forge/maker-wixWindows.msi
MSIX@electron-forge/maker-msixWindows.msix
AppX@electron-forge/maker-appxWindows.appx (Microsoft Store)
DMG@electron-forge/maker-dmgmacOS.dmg
PKG@electron-forge/maker-pkgmacOS.pkg (Mac App Store)
ZIP@electron-forge/maker-zipmacOS, Linux.zip
deb@electron-forge/maker-debLinux.deb (Debian/Ubuntu)
RPM@electron-forge/maker-rpmLinux.rpm (Fedora/RHEL)
Flatpak@electron-forge/maker-flatpakLinux.flatpak
Snap@electron-forge/maker-snapLinux.snap

Recommended starter set: Squirrel (Windows) + DMG + ZIP (macOS) + deb (Linux).

See examples/core.md for maker configuration examples.


Pattern 3: Code Signing

macOS and Windows both require code signing for distribution. Without it, OS security warnings block or discourage installation.

macOS (Sign + Notarize)
packagerConfig: {
  osxSign: {},  // empty object activates defaults -- signs with first valid identity
  osxNotarize: {
    appleId: process.env.APPLE_ID,
    appleIdPassword: process.env.APPLE_PASSWORD,  // app-specific password, NOT Apple ID password
    teamId: process.env.APPLE_TEAM_ID,
  },
},

Requirements: Apple Developer Program membership, "Developer ID Application" certificate in Keychain, hardenedRuntime: true (required for notarization).

Windows (Authenticode)
// Squirrel maker with traditional certificate
{
  name: "@electron-forge/maker-squirrel",
  config: {
    certificateFile: process.env.WIN_CSC_LINK,
    certificatePassword: process.env.WIN_CSC_KEY_PASSWORD,
  },
},

Key point: Since June 2023, private keys for code signing certificates must be stored on FIPS 140 Level 2 hardware. Azure Trusted Signing is the modern alternative for Windows -- see examples/signing.md.

See examples/signing.md for full signing configuration, notarization strategies, and Azure Trusted Signing setup.


Pattern 4: Fuses Plugin (Binary Hardening)

Fuses are bits in the Electron binary flipped at package time to enable/disable features permanently.

import { FusesPlugin } from "@electron-forge/plugin-fuses";
import { FuseV1Options, FuseVersion } from "@electron/fuses";

plugins: [
  new FusesPlugin({
    version: FuseVersion.V1,
    [FuseV1Options.RunAsNode]: false,
    [FuseV1Options.EnableCookieEncryption]: true,
    [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
    [FuseV1Options.EnableNodeCliInspectArguments]: false,
    [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
    [FuseV1Options.OnlyLoadAppFromAsar]: true,
    [FuseV1Options.GrantFileProtocolExtraPrivileges]: false,
  }),
],

Why critical: Without RunAsNode: false, attackers can set ELECTRON_RUN_AS_NODE=1 and run arbitrary code. Without OnlyLoadAppFromAsar: true, ASAR integrity validation can be bypassed by placing files alongside the archive.

Verification: npx @electron/fuses read --app /path/to/packaged/app

See examples/core.md for the full fuses configuration with explanations.


Pattern 5: Publishers

Publishers upload make artifacts to distribution targets.

publishers: [
  {
    name: "@electron-forge/publisher-github",
    config: {
      repository: { owner: "my-org", name: "my-app" },
      prerelease: true,
    },
  },
],
PublisherPackageTarget
GitHub@electron-forge/publisher-githubGitHub Releases
S3@electron-forge/publisher-s3Amazon S3 bucket
Snapcraft@electron-forge/publisher-snapcraftSnap Store
GCS@electron-forge/publisher-gcsGoogle Cloud Storage

Authentication: Use GITHUB_TOKEN env var for GitHub publisher. Use AWS credentials (env vars or shared credentials file) for S3.

See examples/publishers.md for publisher configuration with CI/CD integration.


Pattern 6: Build Hooks

Hooks insert custom logic at specific points in the build lifecycle.

hooks: {
  prePackage: async (config, platform, arch) => {
    // Run before @electron/packager -- generate assets, validate config
  },
  postMake: async (config, makeResults) => {
    // Run after all makers -- rename artifacts, upload to CDN, notify
    // Return modified makeResults array to affect subsequent steps
    return makeResults;
  },
},
HookWhenCan Mutate?
generateAssetsBefore start or packageNo
prePackageBefore @electron/packagerNo
packageAfterCopyAfter packager copies build dirNo
packageAfterPruneAfter devDependencies prunedNo
postPackageAfter package completesNo
preMakeBefore makers runNo
postMakeAfter makers completeYes -- return modified MakeResult[]
readPackageJsonEvery package.json readYes -- return modified package.json

See examples/hooks.md for hook implementation examples.


Pattern 7: Bundler Plugins (Vite / Webpack)

Forge plugins integrate bundlers for compiling main and renderer process code with HMR.

import { VitePlugin } from "@electron-forge/plugin-vite";

plugins: [
  new VitePlugin({
    build: [
      { entry: "src/main.ts", config: "vite.main.config.mts" },
      { entry: "src/preload.ts", config: "vite.preload.config.mts" },
    ],
    renderer: [
      { name: "main_window", config: "vite.renderer.config.mts" },
    ],
  }),
],

Status: The Vite plugin is marked experimental as of v7.5.0 -- minor versions may include breaking changes.

Key detail: The plugin injects global variables (MAIN_WINDOW_VITE_DEV_SERVER_URL, MAIN_WINDOW_VITE_NAME) for loading the renderer in dev vs production. Declare these in a .d.ts file for TypeScript.

See examples/core.md for Vite plugin setup and global variable declarations.


<decision_framework>

Decision Framework

Forge vs electron-builder

Choosing a build tool?
+-- Want first-party Electron support (ASAR integrity, universal macOS)?
|   +-- YES --> Electron Forge (receives features same-day as Electron)
+-- Need YAML-based config, NSIS installer, or broad community support?
|   +-- YES --> electron-builder (more installer targets, larger community)
+-- Need maximum customization for enterprise?
|   +-- YES --> electron-builder (more config options, NSIS scripting)
+-- Starting a new project?
    +-- YES --> Electron Forge (official recommendation, TypeScript config)
FactorElectron Forgeelectron-builder
MaintainerElectron teamCommunity
Config formatTypeScript / JavaScriptYAML / JSON / JS
New Electron featuresSame-dayDelayed
Plugin ecosystemMakers, publishers, pluginsBuilt-in monolith
Windows installersSquirrel, WiX, MSIX, AppXNSIS, Squirrel, MSI, AppX
macOS installersDMG, ZIP, PKGDMG, ZIP, PKG, MAS
npm downloads~50K/week~1.4M/week
ArchitectureComposable packagesMonolithic

Maker Selection

Which maker for your platform?
+-- Windows?
|   +-- Auto-updating desktop app --> Squirrel.Windows
|   +-- Enterprise/IT deployment --> WiX MSI
|   +-- Microsoft Store --> AppX or MSIX
+-- macOS?
|   +-- Direct distribution --> DMG (drag-to-install) + ZIP (for auto-updater)
|   +-- Mac App Store --> PKG
+-- Linux?
|   +-- Debian/Ubuntu --> deb
|   +-- Fedora/RHEL --> RPM
|   +-- Universal sandboxed --> Flatpak or Snap

</decision_framework>


Detailed resources:

  • examples/core.md -- forge.config.ts setup, makers, Vite plugin, fuses, dependency management
  • examples/signing.md -- macOS notarization, Windows Authenticode, Azure Trusted Signing, entitlements
  • examples/publishers.md -- GitHub, S3, Snapcraft publishers with CI/CD patterns
  • examples/hooks.md -- Build lifecycle hooks, custom makers, extending Forge
  • reference.md -- Maker/publisher quick-reference tables, fuse options, CLI commands, Forge vs builder comparison

<red_flags>

RED FLAGS

Critical Issues:

  • Placing electron in dependencies instead of devDependencies -- bloats the packaged app by ~200MB because Forge already provides the binary
  • Shipping without code signing -- macOS Gatekeeper blocks unsigned apps entirely; Windows SmartScreen shows scary warnings
  • Hardcoding signing credentials in forge.config.ts -- secrets end up in version control; always use process.env
  • Not enabling ASAR (asar: false) -- ships your source code as readable plain-text files
  • Not setting RunAsNode: false fuse -- allows ELECTRON_RUN_AS_NODE=1 to execute arbitrary code with your app's permissions

Architecture Issues:

  • Running electron-forge make on macOS expecting Windows .exe output -- makers run only on the target OS (use CI/CD with per-platform runners)
  • Placing native modules (better-sqlite3, sharp) inside ASAR without asarUnpack -- native addons cannot load from inside an ASAR archive
  • Not running @electron/rebuild for native modules -- modules compiled for system Node.js crash in Electron's Node.js runtime (Forge runs rebuild automatically during package, but manual installs need it)
  • Using electron-forge package for distribution -- this produces an uninstallable app bundle; use make for distributable installers

Configuration Mistakes:

  • Setting asar: true without asarUnpack for native modules -- the app will crash at runtime trying to load the native addon
  • Forgetting the platforms array on makers -- maker runs on all platforms and fails on unsupported ones
  • Using osxNotarize without osxSign -- notarization requires a signed binary; Apple rejects unsigned submissions
  • Using your Apple ID password instead of an app-specific password for osxNotarize -- regular passwords are rejected when 2FA is enabled

Gotchas & Edge Cases:

  • electron-forge start in dev mode does not run makers -- dev mode uses unpackaged source; always test with make before release
  • Notarization takes 2-10 minutes per build -- factor this into CI/CD timeout settings
  • Squirrel.Windows handles first-run events (shortcuts, desktop icons) -- your main process must handle Squirrel startup events or the app opens multiple times during install
  • __dirname resolves to virtual ASAR paths in packaged builds -- use app.isPackaged + process.resourcesPath for resource file paths
  • The Vite plugin is experimental since v7.5.0 -- minor version bumps may include breaking changes to its config shape
  • Azure Trusted Signing paths must not contain spaces -- signing fails silently if any path has spaces
  • Forge hooks run in parallel, not sequentially -- do not rely on execution order between hooks of the same type

</red_flags>


<critical_reminders>

CRITICAL REMINDERS

All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

(You MUST place electron in devDependencies -- Forge provides the Electron binary during packaging; placing it in dependencies bloats the app by ~200MB)

(You MUST code-sign macOS builds with osxSign and osxNotarize in packagerConfig -- unsigned apps are blocked by Gatekeeper on macOS 10.15+)

(You MUST enable asar: true in packagerConfig -- without ASAR, your source code ships as plain-text files readable by any user)

(You MUST store signing credentials in environment variables -- never hardcode secrets in forge.config.ts)

(You MUST enable Fuses (FuseV1Options.RunAsNode: false, OnlyLoadAppFromAsar: true) for production security -- without them, attackers can bypass ASAR integrity and run arbitrary Node.js code)

Failure to follow these rules will produce insecure, bloated, or unsigned builds that OS security mechanisms will block or warn users about.

</critical_reminders>

Signals

GitHub stars
24
Forks
8
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
desktop-packaging-electron-forge
Source
github.com/agents-inc/skills