Documentation

Name Mangling

Reference guides for release workflows, command-line usage, cross-file protections, and the desktop app.

Inside the Docs

Practical guides for real release work.

How-to guides Start with release sequencing and command-line usage, then move into feature-specific references.
Advanced protection Browse cross-file controls like Replace Globals and Protect Members when a build spans multiple scripts.

Name Mangling

  • ReplaceNames
  • Free

Name Mangling is the base obfuscation option. It replaces local identifiers — variables, function parameters, and locally declared functions — with short generated names. Every other transform assumes it is on, so this is the first option to enable and the last one you would turn off.

What it covers

By default this option focuses on local identifiers: anything whose scope is contained inside the file. That is deliberate. Local names can be rewritten with no coordination, because nothing outside the file can refer to them. The public surface of the file — global names, exported members, property keys other code reads by name — is left alone so protected output stays a drop-in replacement.

When you also need the public surface renamed, move up to the cross-file features, which require you to think about the whole build rather than one file:

  • Replace Globals (RenameGlobals) renames identifiers declared at global scope.
  • Protect Members (RenameMembers) renames object property and method names.
  • Variable Exclusion List (VariableExclusion) is the escape hatch that keeps named identifiers verbatim when a rename would break a contract.

Output styles

Two generated naming styles are available, selected with IdentityStyle. The choice is cosmetic for security purposes but does affect output size.

IdentityStyleShapeNotes
v1hex _0x1D6E7, _$_51d7 Long hexadecimal-style names. Visually noisy and the style most readers associate with obfuscated JavaScript.
v2abcd ed, b, k Short alphabetic names. Smaller output, and compresses better over the wire.

Compact hexadecimal-style names (v1hex):

if (_0x1EBFF[_$_51d7[103]](_0x1D60F, _$_51d7[160])) {
    var _0x1D6E7 = _0x1D60F[_$_51d7[222]][_$_51d7[169]];
}

Short alphabetic-style names (v2abcd):

if (ed[_$_3485[103]](b, _$_3485[160])) {
    var k = b[_$_3485[222]][_$_3485[169]];
}

Neither style is harder to rename back than the other. An analyst who wants readable names will generate their own; what mangling removes is the information the original names carried, and that is gone either way. Prefer v2abcd unless you have a reason to want the noisier look.

Configuration

Name Mangling is on in every preset. To set it explicitly in jso.config.json:

{
  "options": {
    "ReplaceNames": true,
    "IdentityStyle": "v2abcd"
  }
}

If you are migrating from the open-source javascript-obfuscator package, its identifierNamesGenerator option maps to IdentityStyle. See npm migration for the full option table.

Per-build polymorphic names

By default the generated names differ from build to build, so two protected copies of the same source do not line up name-for-name. That costs an analyst the ability to diff two releases and read off what changed, and it defeats deobfuscation scripts that hard-code names seen in an earlier build.

The trade-off is that output is not byte-identical between builds, which breaks reproducible-build checks and makes your CI artifact hashes churn. When you need stable output, pass a seed: the same input, options, and seed produce byte-identical output.

npx jso-protector --config jso.config.json --seed 20260803

Use a seed for release artifacts you need to reproduce or attest to, and omit it when you want each build to look different. The full treatment — what determinism covers, what it deliberately costs you, and how to verify it in CI — is in reproducible builds. See configuration presets for where this sits relative to the preset options.

What it does not protect

Name Mangling changes names, not structure. After mangling, the control flow, the string literals, and the shape of every object are still exactly as you wrote them — only the labels are gone. That is enough to stop casual reading and copy-paste reuse, and it is not enough to stop a determined reader with time.

Layer structural transforms on top when the logic itself is the asset:

Debugging mangled output

Once names are generated, every production stack trace names them instead of your functions. Keep the identifier map that a protected build emits and demangle traces on the way in rather than reading them raw — see symbolication and the production debugging walkthrough.

Start here: if you are new to the product, enable Name Mangling alone first and confirm your test suite passes against the protected output. Once that baseline is green, add one transform at a time. A break introduced by three simultaneous changes takes far longer to attribute than three verified steps.

Frequently asked questions

What exactly does Name Mangling rename?

Local identifiers only, by default: variables, function parameters and functions declared inside a scope contained within the file. The boundary is deliberate rather than a limitation. A local name can be rewritten with no coordination at all, because nothing outside the file is able to refer to it, so the rename is always safe. Global names, exported members and property keys that other code reads by name are left alone, which is what allows protected output to drop straight into the place the original file occupied.

Why are globals and property keys excluded by default?

Because renaming them requires a decision about the whole build rather than one file, and getting it wrong breaks a contract silently. Two separate options exist for when you do want that reach: one renames identifiers declared at global scope, and one renames object property and method names. Both require you to reason about every consumer of those names, including code you did not protect, data files that carry the same keys, and anything that looks a name up as a string. The exclusion list is the escape hatch for individual names that must survive verbatim.

Does the naming style change how hard the output is to analyse?

No, and this is worth knowing before choosing on instinct. Two styles are available: long hexadecimal-looking names, and short alphabetic ones. Neither is harder to reverse than the other, because anybody who wants readable names will simply generate their own set. What the transform removes is the information the original names carried, and that information is equally gone under both styles. The choice is cosmetic for security purposes and real for size.

Which style should we pick, then?

The short alphabetic style unless you have a specific reason not to. It produces smaller output and compresses better over the wire, which is a measurable benefit on every request. The hexadecimal style is visually noisier and is what most readers associate with obfuscated JavaScript, so it is sometimes chosen for signalling rather than for protection. That is a legitimate reason, just not a technical one.

What tends to break, and how do we find it?

Anything that depends on an identifier's spelling at run time rather than on the reference itself. Code that reads a function's own name, builds a lookup table from names, or resolves a handler by string will stop matching once the name changes. These failures appear at run time rather than at build time, which is why the recommended sequence is to enable this option alone first and get a green test run against the protected output before adding anything else.

Do we need a paid plan for this?

No. Name Mangling is available at the free tier and is enabled in every preset, because effectively every other transform assumes it is already on. The cross-file renaming features are the ones that carry plan requirements, and they are separate options with separate trade-offs.

Is there ever a reason to turn it off?

Rarely, and mostly for diagnosis rather than for production. Turning it off while investigating a defect makes the output far easier to read against your source, which is useful when you are trying to attribute a break to a specific transform. Shipping with it off gives up the base layer that every other option builds on, so it is the last thing to disable rather than the first.

Try this in the online obfuscator

Paste your own code and see this option applied, or compare plans for larger projects and the desktop app.

Try It Free See Pricing