A desktop app ships its JavaScript to the user's own disk, license check included, where it can be read and edited at leisure.
app.asar unpacks, and the license gate is a one-line edit
Installing your app installs your source. app.asar unpacks with one command, and main, preload and renderer are then ordinary readable files on disk. A local-only license gate is usually a single comparison or a single early return, and flipping it is a one-line edit.
That edit does not stay private. A patched file, or a small script that applies the same edit, circulates as a crack and works on every copy of that release.
One seed across main, preload and renderer
@afterpack/electron obfuscates all three programs from one call and shares one seed across them, so main, preload and renderer come out consistent with each other. That consistency is what keeps the two sides of an IPC channel agreeing on the same channel name.
Every build draws a fresh seed, so each release comes out in a different shape. A patch or a script written against one release does not apply to the next one you ship, and whoever wrote it has to locate the check again in a build that no longer resembles the one they worked on.
Wire it into a release build
The Electron page covers installing the plugin and wiring each leg. For a release build, raise the posture for the whole app in one place:
// electron.vite.config.ts
withAfterpack(config, { preset: "hard" });When each leg builds as its own command rather than through a single plugin call, pin AFTERPACK_seed across those commands so the legs still share a shape. One seed, three ways says which toolchains need that.
Keep the Protection Map, backups and source maps out of the packaged app: .afterpack/, *.backup.* and *.map all belong in your packager's exclude globs, because a .map inside app.asar hands back your original source as directly as the unpacked JavaScript would. The Electron page lists what the plugin refuses to build outright.
Check every leg came out protected
Each leg writes its own Protection Map under .afterpack/<leg>/, so you can open main, preload and renderer separately and see that the file holding the license check was covered. Run afterpack verify against the built output before you package it, as the gate that catches a build where protection did not apply.
Where an offline check ends
A fresh shape on every release is real cost for whoever wants to bypass the check: the work is redone from scratch each time you ship, and every update you publish makes the previous release's crack stale.
An offline check still has to run correctly on the user's machine, so given enough time on one release it can be found and patched. Back a client-side check with a call to your own server for anything revenue-critical.
Next
- Electron: install, wiring, the support matrix, and what the plugin refuses to build.
- Directives: marking one region, such as the license check, harder than the rest of the program (Pro).
- Threat model: what someone running your code can reach.