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 Protection Proof Pack

  • Security review and product evaluation
  • UseVMProtection
  • Corporate / Enterprise beta opt-in

This page is the short public evidence pack for JavaScript Obfuscator VM protection. It shows what a protected release can prove without exposing your private source: which functions were selected, what kind of output shape is produced, which code patterns are supported, and what review details your team can keep with the release.

VM protection is meant for a small number of sensitive functions: license checks, paid-feature gates, anti-tamper logic, fingerprinting routines, and proprietary algorithms. Keep the rest of the bundle on Maximum mode so the application stays easy to test and fast to run.

Use this page when a reviewer asks, "What proof do we get that advanced protection ran?" For the API contract and full option reference, read VM Bytecode Protection.

Five-minute proof run

For a real evaluation, do not rely on screenshots. Protect one small marked function, keep the source-free report with the build, and confirm the shipped file no longer contains the original function body.

# 1. Mark one cold sensitive function with @virtualize.
# 2. Enable UseVMProtection in the protection request or config.
# 3. Save both the manifest and the full API report.
npx jso-protector --config jso.config.json \
  --manifest dist-protected/jso-manifest.json \
  --report   dist-protected/jso-report.json

# 4. Verify the protected files still match the manifest.
npx jso-protector --verify-manifest dist-protected/jso-manifest.json \
  --audit-source-maps

# 5. Verify the source-free report proves VM protection ran.
npx jso-protector --verify-vm-proof dist-protected/jso-report.json \
  --min-vm-functions 1

# 6. Generate the reviewer handoff packet.
npx jso-protector --vm-proof-pack dist-protected/jso-report.json \
  --vm-proof-output reports/vm-proof-pack.md \
  --min-vm-functions 1

The pass condition is practical: the application smoke test still passes, --verify-vm-proof passes, the report shows UseVMProtection=true, the virtualized count is above zero, the generated --vm-proof-pack handoff has a passing checklist, a review decision, compatibility guidance, and hot-path guidance, and the protected file does not contain the readable source body of the marked function.

What a reviewer should look for

Selected function

The source contains an @virtualize marker directly above each function that should receive VM protection.

Protected output shape

The readable function body is replaced by bytecode data and a generated interpreter. The call site keeps working.

Release evidence

The build report records the requested option, the count of virtualized functions, and warnings for any skipped functions.

Sample selected function

A good VM candidate is short, valuable, synchronous, and rarely called. This is the kind of function a reviewer should expect to see marked:

// @virtualize
function validateLicense(userId, licenseKey) {
    let hash = 2166136261;

    for (let i = 0; i < licenseKey.length; i++) {
        hash ^= licenseKey.charCodeAt(i);
        hash = Math.imul(hash, 16777619);
    }

    return (hash >> 0) === expectedLicenseHash(userId);
}

The function does not use async, closures, this, classes, destructured parameters, template literals, or var. That keeps the VM pass focused on the part of the application where stronger protection is worth the runtime cost.

Representative protected output shape

The exact output changes by build. A reviewer should not expect this literal text; they should expect the same shape: generated VM bootstrap, encoded bytecode, and a replacement function that dispatches through the VM.

var __jsoVm=(function(){
  var seed=492817, tape="61,08,4d,3f,...";
  var ops=decodeTape(tape, seed);
  function run(slot,args){
    var pc=0, stack=[], locals=args.slice(0);
    while(pc<ops.length){
      switch(nextOpcode(ops,pc++)){
        case 17: stack.push(locals[nextOperand(ops,pc++)]); break;
        case 42: stack.push(Math.imul(stack.pop(), nextOperand(ops,pc++))); break;
        case 91: return stack.pop();
      }
    }
  }
  return { call: run };
})();

function validateLicense(userId, licenseKey) {
  return __jsoVm.call(0, arguments);
}

This is the useful review signal: the original source-level body is no longer present in the shipped file. An attacker can still observe runtime behavior, but static recovery is a different and harder job.

Compatibility table

Pattern Status Reviewer note
Plain synchronous functions with identifier parameters, let/const locals, arithmetic, comparisons, if, while, C-style for, and switch Supported Best fit for license, watermark, checksum, and anti-tamper logic.
async functions, generator functions, default/destructured parameters, closures, this, class, new, try/catch, regex literals, template literals, spread/rest syntax, for...of, for...in, and var Skipped with warning The build should continue through Maximum mode, and the release report should record a warning explaining why the marked function was not virtualized.
Hot render loops, animation ticks, tight parsers, and functions called thousands of times per user session Avoid Use Maximum mode instead. VM protection is for small high-value code paths, not whole-app acceleration-sensitive logic.
Multi-file projects that must remain as separate returned files Plan carefully When VM protection is honored, the response returns one bundled JavaScript output. Keep per-file builds on standard Maximum mode when separate files are required.

Performance planning

Function type Recommendation Reason
Runs once at startup, login, checkout, activation, or license validation Good candidate The user experience is dominated by network, UI, and application work. VM overhead is usually not visible at this frequency.
Runs occasionally after a user action or protected API response Usually acceptable Measure in your app, but this is normally the intended operating range for sensitive client-side checks.
Runs continuously, in a scroll/render loop, or thousands of times per second Do not virtualize The VM interpreter adds work per instruction. Keep these paths native and protect them with Maximum mode instead.

The generated --vm-proof-pack Markdown and JSON output repeats this hot-path guidance and the compatibility limits above, then adds a review decision such as ready-for-manual-review when the required VM proof checks passed but the release owner still needs to confirm function scope and smoke-test results. It also includes a source-free VM Proof Review Assistant for BYO AI or internal reviewers, with prompts for failed proof checks, VM warnings, cold sensitive function scope, hot-path exclusion, build identity, and protected-build smoke evidence. The assistant boundary is explicit: do not include source code, protected output, VM bytecode, source maps, provider keys, customer data, or secrets. Reviewers should ask for an application smoke test, not a synthetic slowdown ratio. The right question is whether the selected function is rare enough that the extra protection is worth the added runtime work.

Release evidence example

JSO release reports are source-free. They can be stored with build artifacts to show which protections were requested and what the engine actually applied:

{
  "BuildID": "2026-06-06-main-8f43b7c",
  "EnabledOptions": ["EncryptStrings", "FlatTransform", "UseVMProtection"],
  "UseVMProtection": true,
  "VMProtectionApplied": true,
  "VMProtectionVirtualizedCount": 1,
  "VMProtectionWarnings": [],
  "CompatibilitySummary": {
    "Risk": "Low",
    "Warnings": []
  },
  "RuntimeDefenseSummary": {
    "BeaconCallback": true,
    "SelfDefending": true
  },
  "RecoverySymbolPackJson": "{ ... source-free symbolication metadata ... }"
}

If the count is zero, reviewers should check VMProtectionWarnings, confirm the account is enabled for the beta, and confirm the marker is directly above a supported function declaration. Accepted marker forms are // @virtualize, /* @virtualize */, and /** @virtualize */.

Evidence checklist

Evidence item Pass condition Action if missing
Marked source function The function has @virtualize directly above a supported synchronous function declaration. Move the marker to the exact line above the function and remove unsupported syntax from the selected function.
Protection request UseVMProtection=true appears in the request/config and the account is eligible for VM beta access. Confirm the account tier and beta opt-in before treating the build as a VM-protected release.
API report The report shows VMProtectionApplied=true, a non-zero virtualized count, and no unresolved warnings for the selected function. Read VMProtectionWarnings first; the most common causes are unsupported syntax, missing opt-in, or a marker that is not in the strict format.
Protected output The shipped file contains generated VM bootstrap/bytecode shape and does not contain the readable body of the marked function. Do not approve the release as VM-protected until the output and report agree.
Runtime smoke test The affected login, activation, checkout, or license flow still passes on the protected build. Move VM protection to a smaller cold function or keep that path on Maximum mode if the flow depends on unsupported runtime behavior.

Security review handoff

For an internal review, keep this evidence together with the protected release:

  • The source-free release report for the protected build.
  • The jso-protector --vm-proof-pack Markdown or JSON output for that report.
  • The VM Proof Review Assistant section from that packet when a BYO AI key or internal reviewer will help turn proof gaps into owner actions.
  • The jso-protector --verify-vm-proof output when CI needs a compact pass/fail gate.
  • The list of functions selected for VM protection and why each one is sensitive.
  • Any skip warnings and the decision made for those functions.
  • Protected-build smoke-test results for login, checkout, license validation, and other affected flows.
  • Runtime-defense beacon configuration when tamper events need to reach the dashboard, Slack, Discord, Splunk, Elasticsearch, or a signed webhook.

Boundary statement

VM protection is a stronger static-analysis barrier, not a promise that client-side code becomes secret forever. Determined attackers can still run the code, observe behavior, instrument the browser, and spend time reconstructing logic. The practical value is that the shipped file no longer exposes the original function body, and repeated releases do not need to expose the same VM shape.

Next: read the full VM option docs, compare vendors on the VM protection comparison, or review all shipped artifacts in the tooling matrix.