The full upstream README, mirrored here for reference. Install config, tool schemas, adoption signals, and an original overview live on the Pyobfus MCP listing page.
pyobfus (pronounced as "Python obfuscator") is a modern, AST-based python-obfuscator / code-obfuscator for developers who need to obfuscate before shipping while keeping failures diagnosable. Framework-aware presets, reverse stack-trace mapping, and a machine-readable JSON CLI let Claude Code, Cursor, GitHub Copilot, Codex, CodeBuddy, and any MCP-compatible AI agent help debug obfuscated stack traces. A transparent, open-source alternative to PyArmor.
A Python code obfuscator built with AST-based transformations. Supports Python 3.9 through 3.14. Provides reliable name mangling, string encoding, control-flow flattening, AES-256 string encryption, and — unique to pyobfus — a reverse-mapping workflow that lets you (or your AI coding assistant) debug obfuscated stack traces without giving up the protection.
🔒 Pro Edition available — 6 patent-targeted protection mechanisms (Selective Opacity, forensic watermarking, Runtime String Vault, and more) layered on top of the free AST obfuscator, $45 one-time, no subscription. See Pro Edition below.
🔎 What's new in v0.5.22 —
pyobfus --checknow emits a Python 3.14 remote-debug hardening advisory when a build both requests anti-debug protection and targets Python 3.14+: PEP 768 remote debugging can only be disabled at interpreter startup (-X disable_remote_debug/PYTHON_DISABLE_REMOTE_DEBUG=1), and the obfuscator's runtime anti-debug heuristics cannot switch it off. Informational only — detection, severity and exit codes are unchanged.
🔔 Starring this repo doesn't notify you about new releases — GitHub only sends release notifications to people who explicitly Watch it. Click Watch → Custom → Releases (top of this page) to get a heads-up the moment a new version ships, without the noise of every commit/issue.
pyobfus-mcpThis repository ships two installable packages:
| Package | What it is | Install |
|---|---|---|
pyobfus | The Python obfuscator (CLI + library). | pip install pyobfus |
pyobfus-mcp | A Model Context Protocol (MCP) server that exposes pyobfus's tools to AI coding agents. | uvx pyobfus-mcp (zero-install) or pip install pyobfus-mcp |
The MCP server lives in pyobfus_mcp/ and is built on the official Model Context Protocol Python SDK (FastMCP). It registers eight MCP tools so Claude Desktop, Claude Code, Cursor, Windsurf, Zed, and Codex can call pyobfus directly from agent conversations — no shelling out:
| MCP tool | Implementation | Purpose |
|---|---|---|
protect_project | pyobfus_mcp/tools.py | One-call, self-verifying pipeline: scan → preset → obfuscate → byte-compile + import-smoke-test the output → return verified: true/false. The agent reports a green check instead of hoping the transform didn't break anything |
check_obfuscation_risks | pyobfus_mcp/tools.py | Pre-flight risk scan; pass verify_dependencies_online=true to check declared package names against public PyPI. |
generate_pyobfus_config | pyobfus_mcp/tools.py | Auto-detect framework → write a working pyobfus.yaml |
unmap_stack_trace | pyobfus_mcp/tools.py | Reverse obfuscated identifiers in a production stack trace |
list_presets | pyobfus_mcp/tools.py | Enumerate community / framework / Pro presets |
explain_preset | pyobfus_mcp/tools.py | Describe what a named preset changes |
recommend_tier | pyobfus_mcp/tools.py | Analyze a project and recommend community vs Pro tier, with reasoning |
start_pro_trial | pyobfus_mcp/tools.py | Return structured guidance for starting the 5-day Pro trial |
The server is registered in the official MCP Registry under io.github.zhurong2020/pyobfus-mcp. The transport is stdio. See pyobfus_mcp/README.md for per-client configuration snippets.
This repo is also a Claude Code plugin marketplace. The pyobfus-protect skill teaches an agent the full "protect Python before shipping — obfuscate and verify it still runs" workflow (MCP-first, CLI fallback):
See skills/ for the skill and install details. (This is distinct from templates/ai-integration/, which are copy-in rule files for your project.)
pyobfus is also on the VS Code Marketplace and Open VSX (publisher zhurong2020, same version on both — Open VSX covers VSCodium, Gitpod, Eclipse Theia and code-server) — the first obfuscation-focused extension in this category, since no competitor (PyArmor, Nuitka, Sourcedefender) has one. Inline obfuscation-risk diagnostics (pyobfus --check findings rendered via VS Code's native DiagnosticCollection API — squiggles + Problems panel, no separate linter to configure), a "Reverse Stack Trace" command, a status bar item showing your current tier with a one-click menu (Check Workspace / Generate Config / Start Trial / Unlock Pro), a "Generate pyobfus.yaml" command, and right-click "Obfuscate with pyobfus" from the Explorer or editor. Source and design rationale in vscode-extension/ and docs/VSCODE_EXTENSION_PLAN.md.
pyobfus --check src/ — config-aware pre-flight risk scan: detects eval/exec, dynamic attribute access, framework reflection points, and declared dependencies that do not exist on public PyPI before you obfuscate. It honors the same explicit/discovered config and presets as a build; findings from excluded files are reported separately without affecting the primary result. Use --no-config for the legacy unfiltered scan and --offline to skip PyPI lookups. JSON includes effective_config, excluded_findings, and an ai_hint telling your AI assistant what to run next. Add --sarif pyobfus.sarif to also emit a SARIF 2.1.0 report for GitHub Code Scanning (see docs/SARIF_CODE_SCANNING.md).pyobfus --init src/ — zero-config onboarding: scans the project, detects FastAPI/Django/Pydantic/Click/SQLAlchemy, and writes a ready-to-use pyobfus.yaml.pyobfus --unmap --trace error.log --mapping mapping.json — reverse obfuscated identifiers in a production stack trace so you can debug (or hand the trace to an AI assistant) without reversing the obfuscation itself.pyobfus … --save-mapping mapping.json --trace-marker — stamp each obfuscated file with a # pyobfus:obfuscated header (id + mapping filename + the exact --unmap command) so an AI agent that lands in an obfuscated file from a traceback immediately knows it's pyobfus output and how to reverse the names.pyobfus … --provenance-manifest provenance.json — write a local JSON manifest (input/output hashes, config hash, pyobfus version, git commit when available, mapping digest, CycloneDX-compatible component relationships, and a self-consistency integrity digest — not a cryptographic signature) for offline build provenance. See docs/PROVENANCE_MANIFEST.md.pyobfus --verify-provenance-manifest provenance.json --json — validate the manifest structure, CycloneDX-compatible relationships, and local integrity digest before archiving or shipping it.pyobfus … --dry-run --json — preview a versioned plan object before anything is written: the effective configuration, which files are selected or excluded (and why), and the artifacts a build would produce, each tagged ship / retain-internal / optional. Relative labels only (no source, secrets, or absolute paths); it is a preview, not a saved apply file.pyobfus … --verify-syntax — opt-in post-build check: compiles every generated .py in memory (no import, no execution, no __pycache__) and reports syntax_valid in JSON. A failure blocks delivery; it makes no runtime-correctness claim.docs/RELEASE_PROVENANCE_VERIFICATION.md for verification commands and the current snapshot.--preset fastapi | django | flask | pydantic | click | sqlalchemy | ml with built-in exclusions for dispatch methods, decorators, ORM fields, migrations, model-serving wrappers, and dependency-injection parameters..pye), compiled packaging (Nuitka / Cython), and ML model-serving. pyobfus --check also emits compatibility_advisory findings for these. See docs/IMPORT_HOOK_COOKBOOK.md, docs/COMPILED_PACKAGING_COOKBOOK.md, and docs/MODEL_SERVING_COOKBOOK.md. For a hardened Python 3.14+ deployment that uses anti-debug protection, --check also flags PEP 768 remote-debug exposure (which must be disabled at interpreter startup, not by the obfuscator) — see docs/REMOTE_DEBUG_HARDENING.md.--json — every CLI mode (obfuscate, --check, --unmap, --init) emits the same structured schema with an ai_hint field, ready for Claude Code, Cursor, Windsurf, and MCP servers to consume.The following features are fully implemented and available in the current version:
Cross-File Obfuscation: Consistent name obfuscation across multiple files
__all__ list updates with obfuscated names--dry-run flagName Mangling: Rename variables, functions, classes, and class attributes to obfuscated names (I0, I1, I2...)
Comment Removal: Strip comments and docstrings
String Encoding: Base64 encoding for string literals with automatic decoder injection
Numeric / Constant Obfuscation (--numeric-obfuscation): replace integer and float literals with value-preserving opaque expressions (int → XOR/add/sub identities, float → float.fromhex) so the original constants no longer appear in the shipped source
AI Provenance Stripping (--strip-ai-artifacts): remove AI-generation markers (e.g. Generated by Claude, Co-Authored-By: Claude) from docstrings and attribution dunders, so AI-assisted code doesn't ship with "this was AI-generated" fingerprints
Incremental Builds (--incremental): skip a directory rebuild when every input file and the config are unchanged since the last successful build (cache at <output>/.pyobfus-cache/), useful in CI pipelines that cache artifacts
Parameter Preservation: Preserve function parameter names for keyword argument compatibility (--preserve-param-names)
Multi-file Support: Obfuscate entire projects with preserved import relationships
File Filtering: Exclude files using glob patterns (test files, config files, etc.)
Configuration Files: YAML-based configuration for repeatable builds
Selective Obfuscation: Preserve specific names (builtins, magic methods, custom exclusions)
Configuration Presets: --preset safe | balanced | aggressive for quick obfuscation-strength tradeoffs, plus framework-aware presets — --preset fastapi | django | flask | pydantic | click | sqlalchemy | ml — with built-in exclusions for dispatch methods, decorators, ORM fields, migrations, and dependency-injection parameters. --list-presets shows them all
Pre-flight Risk Scanning (--check): detects eval/exec, dynamic attribute access, and framework reflection points before you obfuscate; add --sarif PATH to export findings as SARIF 2.1.0 for GitHub Code Scanning
Reverse Stack-Trace Mapping (--unmap): reverse obfuscated identifiers in a production stack trace, so you (or an AI coding assistant) can debug without un-obfuscating the shipped code
Build Provenance (--provenance-manifest, v0.5.5+): local JSON manifest of an obfuscation run — input/output file hashes, config hash, pyobfus version, git commit when available, mapping digest, and CycloneDX-compatible component relationships — for offline build provenance, no network calls
Provenance Validation (--verify-provenance-manifest): validates manifest shape, CycloneDX-compatible relationships, and the local integrity digest; JSON output is available for CI/agent use
Structured Dry-Run Plan (--dry-run --json, v0.5.19+): versioned plan object — effective config, selected/excluded files with reasons, and artifacts tagged ship / retain-internal / optional; relative labels only, preview-only (not applyable)
Syntax-Only Output Verification (--verify-syntax, v0.5.19+): after a build, compiles generated Python in memory — no import, no execution, no __pycache__ — and reports syntax_valid in JSON; a failure blocks delivery and it makes no runtime-correctness claim
Release Attestations: PyPI Integrity API / PEP 740 runbook for verifying pyobfus and pyobfus-mcp release artifacts
The following advanced features are available with a Pro license:
String Encryption
Anti-Debugging
sys.gettrace() (Python-level tracers/debuggers), TracerPid via /proc/self/status (native debuggers on Linux — gdb, strace), WinAPI IsDebuggerPresent() (native debuggers on Windows), and a timing-skew check (catches single-stepping regardless of platform)--anti-debugControl Flow Flattening
--control-flowDead Code Injection
--dead-codeLicense Embedding
--expire 2025-12-31--bind-machine--max-runs 100Runtime Policy (v0.5.9)
--requires-os Linux,Darwin--requires-python-min 3.10--requires-arch x86_64,arm64Embedded Encrypted Data (v0.5.10)
--bind-data gap--embed-data path/to/resource.binget_embedded_data() accessor that decrypts on call, not at importConfiguration Presets
--preset trial - 30-day time-limited version--preset commercial - Maximum protection with machine binding--preset library - For pip-distributable libraries--preset maximum - Highest security with all protections--list-presets - View all presetsSix mechanisms, available both as the pyobfus_pro API and — as of v0.5.1 —
as opt-in pyobfus build flags (single-file / --no-cross-file mode):
--selective-opacity, --seal-code, --vault, --scrub-traceback,
--fingerprint <buyer-id>, --expire-hard <date>. v0.5.3 adds
--period <N> (run-counter limit), --opacity-config <opacity.toml>
(pattern-driven L3 encryption by original qualname), and --bind-device /
--bind-device-id <id> (device-locked L3 encryption). v0.5.4 extends
--bind-device to Runtime String Vault keys too — previously only the
Selective Opacity L3 layer was device-locked, so vault secrets decrypted on
any machine; now each vault key is independently re-derived at runtime from
the bound device.
__code__ materialization).@seal_code — build-time bytecode integrity hash; runtime in-memory-patch detection.--scrub-traceback — production traceback encryption (RSA-2048 + AES-256-GCM); reverse error IDs with the new pyobfus-unscrub CLI.Requires Python ≥ 3.9 as of v0.5.0 (3.8 dropped, EOL 2024-10).
See CURRENT_PLAN_ZH.md for the current project plan and priorities.
Try all Pro features for 5 days - no registration or credit card required!
What's included in the trial:
--control-flow)--string-encryption)--anti-debug)--dead-code)--expire, --bind-machine, --max-runs)--preset trial/commercial/library/maximum)After your trial, purchase a license to continue using Pro features.
The trial runs on the honor system. It stores its state in an unsigned file in your home directory, and
pyobfus/trial.pyis readable Apache-2.0 source — so it is a convenience control, not a security boundary, and we document it as such rather than claiming protection it cannot deliver. See SECURITY.md. Note that the Community Edition has no file or line limits and needs no trial at all — the trial gates only the Pro mechanisms.
Pro Edition Features:
importlib imports with encrypted import stringsPrice: $45.00 USD (one-time payment)
Payment methods: credit/debit card, Apple Pay, and WeChat Pay (微信支付) for buyers in China, plus the other options Stripe shows for your region at checkout. Alipay (支付宝) is being enabled.
Visit our purchase page: pyobfus.github.io/purchase for detailed information and secure checkout.
Quick purchase: 🚀 Buy Now - Direct checkout link (Instant delivery • 30-day money-back guarantee)
3-Step Purchase Process:
Complete Secure Checkout (Stripe)
Receive License Key
PYOB-XXXX-XXXX-XXXX-XXXXActivate License
Start Using Pro Features
Support: For license activation, billing, or account questions, email zhurong0525@gmail.com with your license key. For bug reports or usage questions, please open a GitHub issue or start a discussion — that way the answer is there for the next person who hits the same thing.
By purchasing pyobfus Professional Edition, you agree to our:
From PyPI (recommended):
From source (for development):
Before obfuscation:
After obfuscation:
Note: Variable names (I0, I1, etc.) may vary slightly depending on code structure, but functionality is preserved.
Generate a configuration template for your project type:
This creates a pyobfus.yaml file with sensible defaults for your project type.
Check your configuration file for errors before use:
The validator checks for:
exclude_pattern -> exclude_patterns)When you run pyobfus without -c, it automatically searches for:
pyobfus.yamlpyobfus.yml.pyobfus.yaml.pyobfus.ymlCreate pyobfus.yaml:
The exclude_names option preserves specified names from being renamed during obfuscation:
Important: exclude_names only affects name obfuscation, not string encoding:
Use cases:
Exclude patterns support glob syntax:
test_*.py - Exclude files starting with "test_"**/tests/** - Exclude all files in "tests" directories**/__init__.py - Exclude all __init__.py filessetup.py - Exclude specific filesSee pyobfus.yaml.example for more configuration examples.
pyobfus uses Python's ast module for syntax-aware transformations:
This approach ensures:
Integration Testing Framework (v0.1.6+): Test pyobfus on real-world code without uploading to PyPI. See INTEGRATION_TESTING.md for details.
Obfuscate sensitive business logic before distributing Python applications.
Demonstrate code protection concepts and obfuscation techniques.
Add an additional layer of protection for commercial Python software.
Keyword Arguments (✅ Resolved in v0.1.6): By default, parameter names are obfuscated, which breaks keyword arguments. Solution: Use the --preserve-param-names flag to preserve parameter names while still obfuscating function bodies.
Example:
When to use --preserve-param-names:
def func(*, kwonly))Trade-off: Parameter names reveal some information about the function's interface, but function bodies and local variables are still fully obfuscated.
Cross-file imports: ✅ Resolved in v0.2.0 with full cross-file obfuscation support
Dynamic code: eval(), exec() with obfuscated code may require adjustments
Debugging: Obfuscated code is harder to debug (by design)
Performance: Some obfuscation techniques may impact runtime performance
--preserve-param-names to maintain keyword argument compatibilitypython3.14t, verified: full test suite + a real seal/scrub-traceback obfuscate→execute→decrypt round trip)Use pyobfus if you:
--verify-syntax is an opt-in post-build check: it compiles generated Python
source in memory, creates no __pycache__, and reports syntax_valid in JSON.
It does not import or execute the project and is not a runtime compatibility
guarantee.
Run pyobfus --check first, build into a separate output directory, and keep
the optional mapping.json outside the customer artifact. Ship the transformed
tree, then run your normal tests or packaging step against that exact output.
The PyInstaller,
compiled-packaging, and
import-hook cookbooks cover common delivery
formats.
Build with --save-mapping mapping.json. When a production traceback arrives,
run pyobfus --unmap --trace error.log --mapping mapping.json; the restored
identifiers can then be read by you, Claude Code, Cursor, Copilot, or another AI
assistant without giving the customer your private mapping file.
Yes. uvx pyobfus-mcp exposes eight local tools for risk scanning, config
generation, project protection, verification, preset guidance, and traceback
mapping. Source paths are validated locally and pyobfus does not upload project
code or require an API key.
pyobfus is designed to preserve program behavior for supported Python syntax and framework patterns, and its compatibility matrix is covered by automated tests. Obfuscation is still a source transformation: run your own test suite and verify the built artifact, especially when the project relies on dynamic imports, reflection, or generated code.
Minimal impact:
Yes! Use our built-in templates:
pyobfus supports Python 3.9 through 3.14. Build and test the obfuscated artifact with the Python version used in production; cross-interpreter portability can depend on syntax, dependencies, and enabled transformations.
| Feature | pyobfus | PyArmor |
|---|---|---|
| Price | $45 (Pro, one-time) | $89 (Pro, one-time) |
| Free tier project size | No file or line limits | Trial caps out around 935-940 lines/file (measured 2026-05-09) |
| Open source | Yes (Core: Apache 2.0, Pro: Proprietary) | No |
| Native dependencies | None (pure Python output) | Requires runtime library |
| Python 3.9-3.14 support | Yes | Yes |
Choose pyobfus if: You want transparent pricing, open-source trust, and simpler deployment without native dependencies.
See our detailed comparison for more information.
Yes — and for many projects this is the most cost-effective approach. Use pyobfus as your always-on default layer (every module gets AST mangling + mapping for AI-debug compatibility), then stack PyArmor Pro's bytecode encryption or Nuitka's native compilation on the small set of modules that genuinely need stronger protection. The comparison now also covers why bytecode encryption should be treated as a stronger speed bump, not as irreversible cryptographic protection for client-side Python. See Layered Deployment Strategy in COMPARISON.md for the full reasoning.
Yes, at a fraction of Nuitka Commercial's cost: obfuscate first, then bundle the obfuscated output with the free PyInstaller. The two tools solve different problems (name mangling vs. bundling a Python interpreter into one file) and compose cleanly — see the PyInstaller Cookbook for a full worked example, including verification that the original identifier names never reach the compiled binary and that pyobfus --unmap still reverses a traceback captured from the bundled exe.
--dry-run to preview changes before writing files--preserve-param-names if you rely on keyword argumentspyobfus.yaml for names that must stay unchangedName mangling removes the original identifiers from the emitted source and raises the cost of analysis, but it is not cryptographically irreversible: a determined analyst may infer names and behavior from context. Keep the optional mapping file private when you need reliable reverse mapping. For stronger protection, use Pro features:
Important: String encryption (AES-256) is designed as a deterrent against casual reverse engineering, not as cryptographic security.
Because obfuscated code must decrypt strings at runtime, the encryption key is necessarily embedded in the output. A determined attacker with access to the obfuscated code can:
This is a fundamental limitation of ALL client-side obfuscators (including PyArmor, Nuitka, etc.) - true cryptographic security would require server-side decryption, which is impractical for most use cases.
What string encryption DOES provide:
strings or grep searches from revealing sensitive textWhat string encryption does NOT provide:
Recommendation: For sensitive credentials (API keys, passwords), use environment variables or external secret management systems rather than embedding them in code.
| Tool | Approach | Output |
|---|---|---|
| pyobfus | AST transformation | .py files (pure Python) |
| Cython | Compile to C | .so/.pyd (platform-specific) |
| Nuitka | Compile to executable | Binary (platform-specific) |
Choose pyobfus if: You need cross-platform .py files without compilation overhead.
LICENSE-NOTICE.md):
If you find pyobfus helpful, consider supporting its development:
Your support helps maintain and improve pyobfus. Thank you!
If you use pyobfus in academic work or want to reference it, please cite the archived release. The concept DOI below always resolves to the latest version:
APA
Zhu, R. (2026). pyobfus: An AST-based Python obfuscator with reverse stack-trace mapping for AI-assisted development. Zenodo. https://doi.org/10.5281/zenodo.20846053
BibTeX
Machine-readable metadata is in CITATION.cff (GitHub's "Cite this repository" widget reads it).