A userscript, a community patch or an extension modifies your app by matching text in the JavaScript you ship and rewriting it. Change what there is to match, on every release, and the patch stops applying.
What a patch anchors on
A patcher needs a stable anchor: a function name, a literal string, a recognisable shape it can find again in the next file it downloads. Minification leaves all three in place. A minified bundle still has function names, shortened but consistent from build to build, the same string literals, and the same overall structure, so a patch written against one release keeps matching the next one.
That is why a good patch survives for months. Its author tests it against your current build, ships it to their users, and it keeps working through every release that leaves its anchors alone.
Structural change removes the shape a patch matches
Renaming alone would only move the anchor. AfterPack rewrites the program itself: control flow is flattened and reordered, values are recomputed at each use instead of stored, and code that was independent comes out entangled. What ships is a functional equivalent of what you wrote, a different program that behaves the same way, chosen by that build's seed.
So a patch that used to splice a line into checkAccess has to first locate the code doing what checkAccess did. It is no longer a function with that name, and no longer a function with that shape. How AfterPack works covers the mechanism.
The strings a patcher greps for stop existing
strings.encode is on at any positive complexity target, so the string literals in your JavaScript are not text in the artifact. The feature name, the message type, the API path, the entitlement string, the copy shown when a gate closes: a grep of the shipped file returns none of them, and neither does a find-and-replace written against one of them.
Those literals are the cheapest anchor a patch can use, because they survive minification unchanged and read as documentation of what the code around them does. Encoding is the part of the build that removes them.
Every release moves the anchors again
The seed is new on every build unless you pin one, so the names, the encodings and the shape all differ from your last release. A patch is then a recurring cost for whoever maintains it: the work is redone against each build you ship, and a script already distributed to users stops working the moment you deploy.
Raise protection where a patcher works
medium raises protection across the whole build:
{
"preset": "medium"
}For the code a patch actually targets, such as an entitlement check, step up to hard:
$ npx afterpack@latest dist/ --preset=hardLeave production on a random seed. A constant seed is what lets one patch keep matching release after release; Seed strategy covers the cases where pinning one is safe. If you ship rarely, rebuild on a schedule so the shape still rotates; see Rotate on a schedule.
Confirm the anchors are gone
Search the artifact you are about to deploy for the names and strings a patch would target. Nothing should come back.
Open the Protection Map, written when protectionMap.enabled is on, and confirm the code a patch would go after is covered at the complexity you expect. Then run verify as the last step before deploying: a build writes a protection receipt into its output, and verify re-hashes every file the receipt names and fails when one no longer matches.
What this does not stop
Patching continues. Someone who reads the new build can write a new patch against it, and nothing in a build stops an extension from modifying your page in a browser its owner controls.
A userscript that anchors on the DOM rather than on your JavaScript, matching CSS classes, element structure or visible text, is unaffected by what AfterPack does to the bundle. What changes is every patch that has to find code: it costs its author the work again on each release, and their users run a broken script until they get an update.
Next
- Build & CI: rebuilding on a schedule and keeping a random seed in production.
- Best practices: the full seed strategy, including when pinning one is safe.
- Protect a paywall check: the same mechanics applied to an entitlement gate.