Documentation

Docs

Everything you need to obfuscate with CodeMask — languages, targets, presets, the Lua VM panel, the CM_ macros and the command-line tool.

Overview

CodeMask compiles your code into a real, register-based virtual machine emitted as self-contained source (never luac bytecode — safe for FiveM and Roblox), encrypts every constant, and produces a completely different result on every build (polymorphic by default).

Real VM
Register machine + packed, encrypted byte-blob. No readable ISA.
Polymorphic
Every build differs — diffing & signature attacks die.
Multi-layer
VM-in-VM nesting, self-obfuscated bootstrap, anti-tamper.

Quick start

  1. Create an account and open the dashboard.
  2. Pick your language, target (e.g. FiveM or Roblox) and a preset (start with vm).
  3. Paste code or upload a file, then Obfuscate and download. The first line of the output is always the -- CodeMask header.

Prefer the terminal? Use the CLI:

codemask obfuscate script.lua --preset vm --target fivem -o out.lua

Languages & targets

Language is auto-detected from the file extension:

LanguageExtensionsNotes
Lua.luaRegister-VM. FiveM · Roblox · Luau · LuaJIT · 5.1–5.4.
JavaScript / TS / JSX.js .mjs .cjs .ts .tsx .jsxBytecode VM with transparent fallback to the classic pipeline.
CSS / HTML.css .html .htmClass/id rename, minify, inline-JS virtualization.

Lua targets drive the dialect + emitted runtime:

TargetLuaFeatures
fivem5.4Backtick hash, ?. safe-nav, vectors, getfenv shims, load().
roblox5.3 (Luau)bit32, no goto/integers, loadstring sandboxed → no compression.
luau5.3 (Luau)Types, continue, compound assign, string interpolation.
luajit5.1LuaJIT bit.* library, getfenv.
lua545.4Native bitwise, integers, floor-div, goto.
lua535.3Native bitwise, integers, floor-div.
lua525.2goto; self-contained pure-Lua bitops (poly).
lua515.1getfenv; self-contained pure-Lua bitops (poly).

Presets

Presets bundle the VM tier and classic passes. vm and vm-low virtualize; the rest are non-VM transforms of increasing strength.

vmVM

Full virtualization. Intense structure, optimization 3, GC fixes, string encryption, const-to-expr, wrapped + minified. The strongest tier.

vm-lowVM (light)

Single-dispatch VM, optimization 2. Faster runtime, smaller output — for hot code paths.

defaultNon-VM

Rename + minify. Fast, light protection.

lowNon-VM

Rename + string array + minify.

mediumNon-VM

Rename + string array/encrypt + const-to-expr + opaque predicates.

highNon-VM

Everything non-VM: + control-flow flattening, garbage code, anti-tamper, watermark, wrap.

Lua VM panel

When a VM preset is active, these knobs control how hard the VM is to reverse:

Obfuscation Nodeenabled

Master switch — compile into the register VM. Off ⇒ classic (non-VM) passes only.

Intense VM StructureintenseStructure

Extra dispatch indirection + decoy handlers + a self-obfuscated bootstrap (the interpreter itself is renamed & control-flow-flattened). No readable ISA remains.

VM Nesting (VM-in-VM)vmNesting (1–4)

Re-virtualize the emitted VM N times. Peel one layer → you get another VM's bytecode, not source. Costs size + runtime per layer; use 2 for critical/smaller scripts.

VM Compressioncompression

Pack bytecode + constants with real LZW and inflate via load() at startup (~25% smaller). Needs load() → off for Roblox.

Optimization LeveloptimizationLevel (1–3)

1 = light, 2 = balanced, 3 = maximum hardening.

Static EnvironmentstaticEnvironment

Snapshot / localize referenced globals — faster lookups and hides the global names.

GC FixesgcFixes

Root the VM tables strongly so __gc / collection can never drop live state.

Disable Line InfodisableLineInfo

Drop the line map + traceback remap. Faster, smaller — but errors no longer carry source lines.

Use Debug LibraryuseDebugLibrary

Permit debug.* anti-tamper probes (integrity self-hash, anti-hook peel detection). Off ⇒ debug.* is never touched.

CM_ macros

CodeMask macros are ordinary function calls in your source, prefixed CM_. They are resolved at compile time — no macro names survive in the output. Use them to steer the obfuscator per-region. (The legacy IZP_ prefix is still accepted as an alias.)

CM_NO_VIRTUALIZE(fn)

Emit fn as native Lua (outside the VM) — for hot loops / natives that must not pay VM overhead. Falls back to virtualizing if fn captures an enclosing local, or always virtualizes on Roblox (no loadstring).

-- kept as plain, fast Lua:
local tick = CM_NO_VIRTUALIZE(function()
  return GetGameTimer()
end)
CM_ENCSTR(s) / CM_STRENC(s)

Marks a string literal as sensitive. Returns s unchanged — the VM already encrypts every constant, so this is a readability hint for your own code.

print(CM_ENCSTR("license-key-9F2A"))
CM_ENCNUM(n) / CM_NUMENC(n)

Same idea for a numeric literal. Returns n; the VM encodes constants regardless.

local port = CM_ENCNUM(31337)
CM_ENCFUNC(fn) / CM_FUNCENC(fn)

Force-virtualize a function even under a non-VM preset. Unwraps to fn; virtualization is applied by the pipeline.

local check = CM_ENCFUNC(function() return verify() end)
CM_JIT(fn) / CM_JIT_MAX(fn)

Hint the strongest virtualization for a critical function. Behaves as identity for correctness.

local hot = CM_JIT(function(n) return n * n end)
CM_NO_UPVALUES(fn)

Hint that fn should be lowered without shared upvalue cells where possible. Identity for behavior.

local f = CM_NO_UPVALUES(function() return 1 end)
CM_CRASH()

Anti-tamper tripwire marker. Currently a no-op (reserved for the M6 anti-tamper wiring); safe to leave in your source.

if tampered then CM_CRASH() end
Tip: add identity definitions in your own environment (e.g. function CM_NO_VIRTUALIZE(f) return f end) so the un-obfuscated source still runs during development.

API & keys

Every account has a personal API key (prefix cm_). Find it on your dashboard under API Access — reveal, copy, or reset it there. Resetting invalidates the old key immediately, so update any scripts that use it.

Keep your key secret. Anyone with it can obfuscate on your account. Never commit it or ship it in client code — use it server-side or from your own machine.

REST endpoint. POST JSON to /api/v1/obfuscate with your key in the Authorization header. The response is { code, filename, stats }.

curl https://codemask.io/api/v1/obfuscate \
  -H "Authorization: Bearer cm_your_key" \
  -H "Content-Type: application/json" \
  -d '{
    "source": "print(\"hello\")",
    "filename": "script.lua",
    "preset": "vm",
    "target": "fivem"
  }'
FieldMeaning
sourceThe code to obfuscate (required).
filenameUsed to infer the language and name the output.
languagelua · js · web (optional; inferred from filename).
presetvm · vm-low · default · low · medium · high (default: vm).
targetfivem · roblox · luau · luajit · lua54…lua51 (Lua only).
nestLua VM-in-VM depth 1–4 (optional).
compressLua LZW self-loader true/false (optional).

Max input is 2 MB. A missing or wrong key returns 401.

CLI

The codemask command is a thin client for the API — the engine runs on codemask.io, so it needs your API key. Authenticate once, then obfuscate a single file or a whole FiveM resource directory.

# authenticate once (saves to ~/.codemask/config.json)
codemask login cm_your_key
codemask whoami          # verify the account

# or per-command: --api-key cm_...  |  CODEMASK_API_KEY env
codemask obfuscate <file> [-o out] [--preset <p>] [--target <t>] [--lang <l>] [--nest <n>] [--compress]
codemask resource  <dir>  [-o outdir] [--preset <p>] [--target <t>] [--nest <n>] [--compress]
FlagMeaning
--preset, -pvm · vm-low · default · low · medium · high (default: vm)
--target, -tfivem · roblox · luau · luajit · lua54…lua51 (default: fivem)
--out, -oOutput file or directory.
--lang, -lForce language (lua · js · web) instead of auto-detect.
--nest, -nVM-in-VM depth 1–4 (2 = peel reveals another VM).
--compressReal LZW self-loader (needs load(); off for Roblox).

Examples:

# FiveM script, full VM
codemask obfuscate client.lua --preset vm --target fivem -o client.obf.lua

# Roblox module (no compression — loadstring is sandboxed)
codemask obfuscate module.lua --preset vm --target roblox

# TypeScript, strongest non-VM
codemask obfuscate app.ts --preset high

# Whole FiveM resource (manifests copied, .lua files obfuscated)
codemask resource ./my-resource --preset vm -o ./my-resource-obf

FiveM & Roblox

FiveM (CfxLua): select fivem. Backtick hash literals, ?./?[] safe navigation and vector literals are supported; event names, exports and fxmanifest.lua are preserved. Compression and the debug library work.

Roblox / Luau: select roblox. The VM emits bit32-based bitwise ops, avoids goto/integers, and VM Compression is forced off because Roblox sandboxes loadstring. Constant encryption and integrity checks are made bitwise-free so they run on Luau.

Limitations

  • The line map remaps uncaught errors only. Messages from pcall-caught errors still carry the obfuscated line — enable Disable Line Info if that matters.
  • VM Compression needs load(), so it stays off for Roblox.
  • The JavaScript bytecode VM covers a documented subset; out-of-subset code transparently falls back to the classic JS pipeline — it never breaks your code.
  • VM nesting and higher optimization trade output size + runtime for strength — tune per script.

Ready to mask your code?

Get started free