# pyobfus: the Python obfuscator
**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](https://claude.com/claude-code), [Cursor](https://cursor.com/), [GitHub Copilot](https://github.com/features/copilot), [Codex](https://openai.com/codex/), [CodeBuddy](https://www.codebuddy.ai/), and any MCP-compatible AI agent help **debug obfuscated stack traces**. A transparent, open-source alternative to PyArmor.
[](https://pypi.org/project/pyobfus/)
[](https://pypi.org/project/pyobfus/)
[](https://pyobfus.readthedocs.io/en/latest/)
[-blue.svg)](https://github.com/zhurong2020/pyobfus/blob/main/LICENSE)
[](https://www.python.org/downloads/)
[](https://www.bestpractices.dev/projects/12788)
[](https://doi.org/10.5281/zenodo.20846053)
[](https://glama.ai/mcp/servers/zhurong2020/pyobfus)
[](https://github.com/psf/black)
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.** Six 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](#-pro-edition) below.
> **๐ What's new in v0.5.29:** Pro artifacts now ship with a redistributable
> runtime. Output built with the Pro fusion passes imports the small, separately
> published `pyobfus-runtime` package instead of the complete `pyobfus_pro`
> builder, so a protected artifact can be delivered legally and run on a clean
> target with no licence key. `pyobfus` declares `pyobfus-runtime>=0.1,<1`, so
> `pip install -U pyobfus` brings it along; `--provenance-manifest` records the
> requirement as `runtime_requirement`; consumed Pro marker imports are stripped
> from deliverables; and a Pro edition that fails to import now says why
> instead of looking unlicensed. Existing 0.5.x artifacts keep working.
> ๐ **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.
## ๐ Companion MCP server: [`pyobfus-mcp`](https://github.com/zhurong2020/pyobfus/tree/main/pyobfus_mcp)
This repository ships **two installable packages**:
| Package | What it is | Install |
|---|---|---|
| [`pyobfus`](https://pypi.org/project/pyobfus/) | The Python obfuscator (CLI + library). | `pip install pyobfus` |
| [`pyobfus-mcp`](https://pypi.org/project/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/`](https://github.com/zhurong2020/pyobfus/tree/main/pyobfus_mcp) and is built on the official [Model Context Protocol Python SDK](https://github.com/modelcontextprotocol/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`](https://github.com/zhurong2020/pyobfus/blob/main/pyobfus_mcp/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`](https://github.com/zhurong2020/pyobfus/blob/main/pyobfus_mcp/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`](https://github.com/zhurong2020/pyobfus/blob/main/pyobfus_mcp/pyobfus_mcp/tools.py) | Auto-detect framework โ write a working `pyobfus.yaml` |
| `unmap_stack_trace` | [`pyobfus_mcp/tools.py`](https://github.com/zhurong2020/pyobfus/blob/main/pyobfus_mcp/pyobfus_mcp/tools.py) | Reverse obfuscated identifiers in a production stack trace |
| `list_presets` | [`pyobfus_mcp/tools.py`](https://github.com/zhurong2020/pyobfus/blob/main/pyobfus_mcp/pyobfus_mcp/tools.py) | Enumerate community / framework / Pro presets |
| `explain_preset` | [`pyobfus_mcp/tools.py`](https://github.com/zhurong2020/pyobfus/blob/main/pyobfus_mcp/pyobfus_mcp/tools.py) | Describe what a named preset changes |
| `recommend_tier` | [`pyobfus_mcp/tools.py`](https://github.com/zhurong2020/pyobfus/blob/main/pyobfus_mcp/pyobfus_mcp/tools.py) | Analyze a project and recommend community vs Pro tier, with reasoning |
| `start_pro_trial` | [`pyobfus_mcp/tools.py`](https://github.com/zhurong2020/pyobfus/blob/main/pyobfus_mcp/pyobfus_mcp/tools.py) | Return structured guidance for starting the 5-day Pro trial |
The server is registered in the **official [MCP Registry](https://registry.modelcontextprotocol.io/)** under `io.github.zhurong2020/pyobfus-mcp`. The transport is stdio. See [`pyobfus_mcp/README.md`](https://github.com/zhurong2020/pyobfus/blob/main/pyobfus_mcp/README.md) for per-client configuration snippets.
### ๐งฉ Claude Code skill / plugin
This repo is also a **Claude Code plugin marketplace**, shipping two skills split by whether they change anything:
| Skill | What it does | Writes? |
|---|---|---|
| `pyobfus-protect` | The full "protect Python before shipping: obfuscate **and** verify it still runs" workflow (MCP-first, CLI fallback) | Yes, produces a build |
| `pyobfus-review` | Answers the question that comes first: is this project safe to obfuscate, what would break, and which artifacts would a build emit. Config-aware `--check` plus `--dry-run`, nothing else | No, read-only |
Both follow the [agentskills.io](https://agentskills.io) `SKILL.md` format, so they also work in GitHub Copilot agent mode, Cursor and Codex CLI, which read skills from `.github/skills/` in your own repository.
```
/plugin marketplace add zhurong2020/pyobfus
/plugin install pyobfus@pyobfus
```
See [`skills/`](https://github.com/zhurong2020/pyobfus/tree/main/skills) for both skills and install details. (This is distinct from [`templates/ai-integration/`](https://github.com/zhurong2020/pyobfus/tree/main/templates/ai-integration), which are copy-in rule files for *your* project.)
### ๐งโ๐ป VS Code extension
pyobfus is also on the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=zhurong2020.pyobfus) and [Open VSX](https://open-vsx.org/extension/zhurong2020/pyobfus) (publisher `zhurong2020`, same version on both; Open VSX covers VSCodium, Gitpod, Eclipse Theia and code-server). It is 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/`](https://github.com/zhurong2020/pyobfus/tree/main/vscode-extension) and [`docs/VSCODE_EXTENSION_PLAN.md`](https://github.com/zhurong2020/pyobfus/blob/main/docs/VSCODE_EXTENSION_PLAN.md).
### ๐ค AI-native features
- **`pyobfus --check src/`** runs a config-aware pre-flight risk scan. It 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`](https://github.com/zhurong2020/pyobfus/blob/main/docs/SARIF_CODE_SCANNING.md)).
- **`uses: zhurong2020/pyobfus-action@v1`** runs the pre-flight scan or an obfuscated build in GitHub Actions, with SARIF wired to Code Scanning and a findings table in the job summary. It separates findings from tool errors, so `fail-on: never` lets a SARIF upload run first without the `|| true` workaround swallowing a mistyped path. Repo: [zhurong2020/pyobfus-action](https://github.com/zhurong2020/pyobfus-action) ยท [Marketplace](https://github.com/marketplace/actions/pyobfus-scan-and-build).
- **`pyobfus --init src/`** is zero-config onboarding: it 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`** reverses 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`** stamps 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 โฆ --no-community-marker`** switches off the versioned `# pyobfus:generated` marker that generated files normally open with. The marker names the tool version, edition, and the project-relative source path, so anyone (or any agent) opening the file knows it is generated output rather than something to edit. It never contains an absolute path, buyer id, licence key or hash. It is transparent attribution (a plain comment you can delete), not a licence check or an anti-piracy measure, and suppressing it is a free-tier feature, not a paid one. Distinct from `--trace-marker`, which is about reversing tracebacks.
- **`pyobfus โฆ --provenance-manifest provenance.json`** writes 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`](https://github.com/zhurong2020/pyobfus/blob/main/docs/PROVENANCE_MANIFEST.md).
- **`pyobfus --verify-provenance-manifest provenance.json --json`** validates the manifest structure, CycloneDX-compatible relationships, and local integrity digest before archiving or shipping it.
- **`pyobfus โฆ --dry-run --json`** previews 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`** is an opt-in post-build check: it 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.
- **`pyobfus โฆ --build-report build-report.json`** writes one deterministic, privacy-safe fact model after a successful build: selection/config, transformation and cache counters, verification evidence, output hashes, artifact roles, marker state, and provenance linkage. See [`docs/VERIFIABLE_BUILD_REPORT.md`](https://github.com/zhurong2020/pyobfus/blob/main/docs/VERIFIABLE_BUILD_REPORT.md).
- **Packages that re-export.** An `__init__.py` that re-exports (`from .core import run`, with `run` in `__all__`) keeps the name its definition was given, so the generated package stays importable and `from pkg import *` still works. Names re-exported from third-party packages are left alone. (v0.5.25)
- **Reproducible builds** (v0.5.25). The same input and configuration produce the same output bytes, so whoever receives a build can re-run it and compare against the digests in `--build-report` rather than trusting them. Scope is deliberate: `--numeric-obfuscation` and AES string encryption draw fresh randomness per build and are expected to differ.
- **Release provenance.** pyobfus and pyobfus-mcp are published through PyPI Trusted Publishing with PEP 740 attestations; see [`docs/RELEASE_PROVENANCE_VERIFICATION.md`](https://github.com/zhurong2020/pyobfus/blob/main/docs/RELEASE_PROVENANCE_VERIFICATION.md) for verification commands and the current snapshot.
- **Framework-aware presets.** `--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.
- **Support matrix.** What is actually verified and by what, in three honest labels (tested / verified once / advisory-only). Cells that cannot point at a CI job or a dated record are marked advisory-only rather than assumed. See [`docs/SUPPORT_MATRIX.md`](https://github.com/zhurong2020/pyobfus/blob/main/docs/SUPPORT_MATRIX.md).
- **Compatibility cookbooks.** Pair pyobfus with real delivery pipelines: import-hook / encrypted-file (SOURCEdefender `.pye`), compiled packaging (Nuitka / Cython), and ML model-serving. `pyobfus --check` also emits `compatibility_advisory` findings for these. See [`docs/IMPORT_HOOK_COOKBOOK.md`](https://github.com/zhurong2020/pyobfus/blob/main/docs/IMPORT_HOOK_COOKBOOK.md), [`docs/COMPILED_PACKAGING_COOKBOOK.md`](https://github.com/zhurong2020/pyobfus/blob/main/docs/COMPILED_PACKAGING_COOKBOOK.md), and [`docs/MODEL_SERVING_COOKBOOK.md`](https://github.com/zhurong2020/pyobfus/blob/main/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`](https://github.com/zhurong2020/pyobfus/blob/main/docs/REMOTE_DEBUG_HARDENING.md).
- **Global `--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.
## Features
### โ
Free Edition
The following features are **fully implemented and available** in the current version:
- Cross-file obfuscation keeps renamed symbols consistent across every file in a project:
- Automatic import statement rewriting
- `__all__` list updates with obfuscated names
- Global symbol table with collision detection
- Two-phase obfuscation pipeline (Scan โ Transform)
- Preview mode with `--dry-run` flag
- Name mangling renames variables, functions, classes, and class attributes to index-based names (I0, I1, I2...).
- Comment removal strips comments and docstrings.
- String encoding wraps string literals in Base64 and injects the decoder for you.
- Numeric / constant obfuscation (`--numeric-obfuscation`) replaces 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`) removes 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 `