Skip to main content

Obfuscation levels

The --level 0–5 flag is Obfy's sophistication dial. Each level adds to the one below it. Whatever the level, the output is always compiled → marshalled → AES-256-GCM encrypted by the loader.

--levelAdds (includes everything below it)
0Nothing — code is compiled, marshalled, and encrypted as-is.
1 (default)Strip docstrings. (Comments are dropped inherently by recompiling from the AST.)
2+ mangle string literals (XOR decoder) + inject dead code.
3+ rename function-local variables and in-function nested defs/classes.
4+ cross-module public-name renaming across the whole tree.
5+ native function compilation: eligible functions are lowered to the Obfy clean-room VM so their CPython bytecode never ships. Unsupported functions fall back to level 4.

Choosing a level

  • Levels 1–2 are safe for virtually any project and add no semantic risk.
  • Level 3 renames only function-local names, so it's low-risk too.
  • Level 4 renames public names across modules. This is aggressive: code that looks names up dynamically (e.g. by string, via getattr, or framework auto-discovery) may break. Use --exclude for files that rely on dynamic lookup.
  • Level 5 additionally compiles eligible functions to a native VM. Coverage is partial and growing; anything unsupported transparently falls back to level 4, so the build still succeeds.
obfy build --src ./app --out ./dist --level 4

Level 5 and the JIT

Level 5 runs on a default interpreter for the compiled functions. An opt-in JIT (set the OBFY_NATIVE_JIT environment variable) emits control-flow-flattened, per-build polymorphic machine code for stronger protection. It is opt-in because native execution has stricter runtime requirements; leave it off unless you've validated your build with it.

Honest posture

Higher levels raise the cost of recovering your code but do not make it impossible — CPython must eventually run real bytecode. Treat Obfy as deterrence, and pair it with legal protection for sensitive IP.