Documentation

Guides for protecting production JavaScript

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

Inside The Docs

Practical guides, not placeholder pages.

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.

VM Bytecode Protection

  • UseVMProtection
  • Corporate or Enterprise

VM bytecode protection compiles selected functions to a per-build virtual-machine instruction set and ships a small JS interpreter to execute them at runtime. This is a different protection class from the static transforms in Maximum mode (encrypted constants, flat control flow, polymorphic decoder) — the virtualizer's output has no source-level structure left to recover, only opcodes that an attacker would have to reimplement the VM to follow.

What it changes

Functions you mark with the // @virtualize comment are removed from the output as readable JavaScript. They are replaced with bytecode and a small VM interpreter that loads, decodes, and dispatches those opcodes when the function is called. From the calling site, the function still has the same name and signature; from a static-analysis standpoint, the body is gone.

Functions you do not mark are processed normally by the rest of the Maximum-mode pipeline. You should not virtualize hot paths — see the When to use it guidance below.

API usage

Set the option on the obfuscation request:

{
  "Items": [{ "FileName": "app.js", "FileCode": "..." }],
  "EncryptStrings": true,
  "FlatTransform": true,
  "UseVMProtection": true
}

Mark functions to virtualize with a // @virtualize comment on the line above the function declaration:

// @virtualize
function validateLicense(userId, key) {
    let hash = 0;
    for (let i = 0; i < key.length; i++) {
        hash = (hash * 31 + key.charCodeAt(i)) | 0;
    }
    return hash === userIdToHash(userId);
}

Functions without the marker are not virtualized.

Multi-file projects collapse to a single bundled output. When UseVMProtection=true is honored on the request, the response's Items array contains one file regardless of how many were submitted. The customer's input files are concatenated, the marked functions are virtualized, the VM interpreter is inlined at the top, and the resulting single .js file is returned. If you rely on per-file output (separate scripts per HTML page, route-specific bundles, etc.), keep UseVMProtection=false for those builds — or virtualize only the build that contains the security-critical functions, and ship the rest of your bundles through standard Maximum mode.

When to use it

  • License validation, key derivation, anti-tamper checks — cold paths where the algorithm is the asset. The slowdown is irrelevant because they run rarely.
  • Watermarking and fingerprinting routines — logic an attacker would otherwise lift wholesale.
  • Small functions in the security perimeter — the VM cost is per-instruction; a 50-instruction function adds microseconds, not milliseconds.

When not to use it

  • Rendering loops, animation tick handlers, hot network parsers — VM execution is meaningfully slower than native; a virtualized 60-fps game loop will not stay at 60 fps.
  • Functions called from requestAnimationFrame or tight setTimeout loops — same reason.
  • async / await functions — async functions are skipped with an engine warning rather than virtualized.

Per-build polymorphism

Every build regenerates the VM. The same source compiled twice produces two structurally different bundles — different bytecode bytes, different dispatcher shape, different identifier names. A deobfuscator that solves one build's structure does not get the next for free. This matches the per-build polymorphism principle the rest of Maximum mode already uses.

Honest limits

  • VM protection raises the static reverse-engineering bar substantially. It does not stop runtime observation: a determined attacker can attach a debugger, log every dispatcher invocation, and reconstruct the bytecode-to-source mapping. Pair with anti-debug + runtime monitoring for that threat.
  • VM-aware deobfuscators exist for some commercial obfuscators. Per-build polymorphism is the defense; but assume the technique is not a one-way function.
  • Async/await is not supported. The pipeline detects async functions marked with // @virtualize and skips them with an engine warning rather than failing the build. Generator functions (function*) are skipped for the same reason.
  • var declarations inside virtualized functions are not supported. Use let or const. The pipeline detects var in marked functions and skips virtualization for that function — the function ships through standard Maximum mode instead.
  • Several JavaScript constructs cause a function to be skipped with a warning rather than virtualized: class declarations, new expressions, the this keyword, try/catch/throw, regex literals, template literals, spread / rest parameters, destructuring patterns, for...of / for...in loops, and unsigned right shift (>>>). Plain control flow (if/else, C-style for, while, switch with break, plain break/continue), typeof, and the standard arithmetic / bitwise / comparison / logical operators (except ==/!=) ARE supported.
  • Closures are not supported inside virtualized functions. A virtualized function must reference only its parameters, its own locals, and globals on globalThis (e.g. Math, helper functions defined at script top level). Variables captured from an enclosing function's scope cause a skip-with-warning. The good fits above (license validation, key derivation, anti-tamper) typically don't need closures — if your function does, virtualize the enclosing function instead.
  • Function parameters must be plain identifiers. Default parameters (function fn(x = 1)) and destructured parameters (function fn({x, y})) cause a skip-with-warning.
  • Multi-file projects with UseVMProtection=true return a single bundled .js file in the response (see the API-usage section above). Build-time decision; if you rely on per-file output, keep UseVMProtection=false for those builds.

Performance

VM execution is meaningfully slower than native JavaScript. The exact factor depends on the function: tight inner loops with cheap per-instruction work pay the most; functions dominated by string operations or method calls amortize the dispatcher cost over more native work and pay much less.

Per-call cost is the right number to plan against, not the slowdown ratio. For the recommended use cases (license validation, anti-tamper, key derivation called once or twice per session), the per-call cost is microseconds and entirely invisible at user-experience level. Functions called more than a few thousand times per second per user are the only place the cost becomes visible — do not virtualize those.

Bundle-size impact: a small fixed VM dispatcher shared across all virtualized functions, plus a per-virtualized-function bytecode payload of a few hundred bytes. Negligible against typical web-app bundle sizes.

Tier availability

Available on Corporate ($49/mo) and Enterprise ($99/mo) plans. Free and Basic tier requests with UseVMProtection=true are rejected at validation time with a MissingPlanException identifying the required tier. The Free online tool is unaffected — the option is not exposed in the playground UI.

Choosing this vs alternatives

Three honest cases where another product is the right choice instead of (or alongside) UseVMProtection:

  • You need runtime threat monitoring with live alerts. JSO does not collect runtime telemetry from your protected output. If active attackers are part of your threat model and you want a dashboard of attempted tampering events, pair Maximum mode with a runtime monitoring platform (Jscrambler, Verimatrix, Digital.ai). Their VM and your VM are not mutually exclusive — you can ship both.
  • Your protected asset must survive determined professional reversers spending months on it. If you ship DRM keys for premium video, novel cryptographic constants, or anti-cheat heuristics that adversaries will burn weeks attacking, the heaviest enterprise VM products (WhiteCryption / Verimatrix Code Protection, Digital.ai Application Protection) are deeper than what JSO ships. They are sales-led, often six-figure annual contracts, but the depth is real.
  • You only need free-tier obfuscation. The open-source javascript-obfuscator npm package and its playground (obfuscator.io) cover name mangling, control-flow flattening, and string array encoding for free. They do not include bytecode VM, but if VM was not in your requirements, the free tool is the right baseline.

For the buyer-axis comparison (real opcode interpreter vs CFG flattening, per-build polymorphism, async support, pricing transparency), see VM Protection Across Vendors on the comparison page, and the named-vendor breakdown on the blog.

Mark functions with // @virtualize, set UseVMProtection=true on a Corporate or Enterprise tier request, and the response Items will contain a single bundled .js with the virtualized functions in place. Questions: [email protected].