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).
Quick start
- Create an account and open the dashboard.
- Pick your language, target (e.g. FiveM or Roblox) and a preset (start with
vm). - Paste code or upload a file, then Obfuscate and download. The first line of the output is always the
-- CodeMaskheader.
Prefer the terminal? Use the CLI:
codemask obfuscate script.lua --preset vm --target fivem -o out.luaLanguages & targets
Language is auto-detected from the file extension:
| Language | Extensions | Notes |
|---|---|---|
| Lua | .lua | Register-VM. FiveM · Roblox · Luau · LuaJIT · 5.1–5.4. |
| JavaScript / TS / JSX | .js .mjs .cjs .ts .tsx .jsx | Bytecode VM with transparent fallback to the classic pipeline. |
| CSS / HTML | .css .html .htm | Class/id rename, minify, inline-JS virtualization. |
Lua targets drive the dialect + emitted runtime:
| Target | Lua | Features |
|---|---|---|
fivem | 5.4 | Backtick hash, ?. safe-nav, vectors, getfenv shims, load(). |
roblox | 5.3 (Luau) | bit32, no goto/integers, loadstring sandboxed → no compression. |
luau | 5.3 (Luau) | Types, continue, compound assign, string interpolation. |
luajit | 5.1 | LuaJIT bit.* library, getfenv. |
lua54 | 5.4 | Native bitwise, integers, floor-div, goto. |
lua53 | 5.3 | Native bitwise, integers, floor-div. |
lua52 | 5.2 | goto; self-contained pure-Lua bitops (poly). |
lua51 | 5.1 | getfenv; 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.
vmVMFull 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-VMRename + minify. Fast, light protection.
lowNon-VMRename + string array + minify.
mediumNon-VMRename + string array/encrypt + const-to-expr + opaque predicates.
highNon-VMEverything 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:
enabledMaster switch — compile into the register VM. Off ⇒ classic (non-VM) passes only.
intenseStructureExtra dispatch indirection + decoy handlers + a self-obfuscated bootstrap (the interpreter itself is renamed & control-flow-flattened). No readable ISA remains.
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.
compressionPack bytecode + constants with real LZW and inflate via load() at startup (~25% smaller). Needs load() → off for Roblox.
optimizationLevel (1–3)1 = light, 2 = balanced, 3 = maximum hardening.
staticEnvironmentSnapshot / localize referenced globals — faster lookups and hides the global names.
gcFixesRoot the VM tables strongly so __gc / collection can never drop live state.
disableLineInfoDrop the line map + traceback remap. Faster, smaller — but errors no longer carry source lines.
useDebugLibraryPermit 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.)
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)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"))Same idea for a numeric literal. Returns n; the VM encodes constants regardless.
local port = CM_ENCNUM(31337)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)Hint the strongest virtualization for a critical function. Behaves as identity for correctness.
local hot = CM_JIT(function(n) return n * n end)Hint that fn should be lowered without shared upvalue cells where possible. Identity for behavior.
local f = CM_NO_UPVALUES(function() return 1 end)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() endfunction 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.
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"
}'| Field | Meaning |
|---|---|
| source | The code to obfuscate (required). |
| filename | Used to infer the language and name the output. |
| language | lua · js · web (optional; inferred from filename). |
| preset | vm · vm-low · default · low · medium · high (default: vm). |
| target | fivem · roblox · luau · luajit · lua54…lua51 (Lua only). |
| nest | Lua VM-in-VM depth 1–4 (optional). |
| compress | Lua 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 envcodemask 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]| Flag | Meaning |
|---|---|
| --preset, -p | vm · vm-low · default · low · medium · high (default: vm) |
| --target, -t | fivem · roblox · luau · luajit · lua54…lua51 (default: fivem) |
| --out, -o | Output file or directory. |
| --lang, -l | Force language (lua · js · web) instead of auto-detect. |
| --nest, -n | VM-in-VM depth 1–4 (2 = peel reveals another VM). |
| --compress | Real 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-obfFiveM & 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.