How To: Control Which Modules Are Protected¶
This guide explains how PyLocket decides which Python modules to protect and how you can override that behavior.
Default Behavior: Automatic Module Classification¶
PyLocket automatically classifies every module in your artifact as either user code (protected) or dependency code (skipped). You do not need to configure anything for typical projects — PyLocket protects your code and leaves everything else untouched.
What Gets Protected by Default¶
- Your application's entry module (the main script)
- All user-written helper modules reachable from the entry module
- Standalone
.pyfiles that are part of your project
What Is Automatically Skipped¶
- Python standard library modules (
os,sys,json,pathlib, etc.) - PyInstaller internals (
pyimod,pyiboot,pyi_rth_*) - Common third-party packages (
requests,flask,django,pydantic,click,rich,tqdm,setuptools,pip,certifi,urllib3, etc.) - C extension modules (
.pyd,.sofiles compiled from C/Cython)
This means PyLocket protects only your proprietary code by default — the parts that contain your business logic and intellectual property.
Why You Should Not Protect Every Module¶
Do not force-include third-party libraries
Protecting third-party dependencies is unnecessary and can cause problems:
- Increased build time and artifact size — encrypting thousands of library functions adds overhead with no security benefit
- Potential runtime errors — some libraries use reflection, dynamic imports, or introspection that may not work correctly when function bodies are replaced with stubs
- No IP value — third-party libraries are already publicly available; encrypting them does not protect your intellectual property
Focus protection on the code that is uniquely yours: your application logic, algorithms, business rules, and proprietary modules.
Overriding Module Selection¶
PyLocket's heuristics cover the common case, but you have the final say. Declare the modules or functions you want protected and PyLocket will protect them even when its classifier would have skipped them.
There are three ways to declare this. They do the same thing; pick whichever fits how you build.
1. A manifest committed in your repo (recommended)¶
Create pylocket.toml at the root of your project:
[protect]
functions = [
"myapp.data.ledger_key.ledger_key",
"myapp.services.billing",
]
exclude = ["myapp.debug_tools"]
The CLI finds it automatically, including when you run from a subdirectory:
This is the recommended route because the declaration is versioned with your code, shows up in a pull-request diff, and needs nobody in a browser at release time.
2. The REST API¶
If you build through the API rather than the CLI, send the same lists when you create the build:
curl -X POST https://api.pylocket.com/v1/apps/$APP_ID/builds \
-H "X-API-Key: $PYLOCKET_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"version": "1.2.1",
"artifact_type": "app",
"platform": "macos-arm64",
"force_include": ["myapp.data.ledger_key.ledger_key"],
"force_exclude": ["myapp.debug_tools"]
}'
3. A one-off flag¶
To override the manifest for a single build:
pylocket protect --app <APP_ID> --artifact dist/myapp.exe \
--protect myapp.data.ledger_key.ledger_key \
--protect myapp.services.billing
Use --no-protect-manifest to ignore pylocket.toml entirely for one run.
What you can name¶
| Form | Example | Effect |
|---|---|---|
| Package | myapp |
Protects every module in the package |
| Module | myapp.services.billing |
Protects that module |
| Function | myapp.data.ledger_key.ledger_key |
Protects the module and verifies that function was protected |
PyLocket protects at module granularity: every eligible function in a selected module is protected. Naming a function additionally asserts it, which is the reason to prefer the function form for anything that matters.
A name that cannot be honoured fails the build¶
If you name a function and it does not end up protected, the build fails and tells you which name and why:
Declared protection targets were not protected:
'myapp.data.ledger_key.ledgr_key' (function was not protected).
This is deliberate. A typo that silently protected nothing would leave you shipping in the belief that you were covered.
A function is skipped when it is a closure (it reads a variable from an enclosing function) or is below the minimum size. Move the value into a module-level function if you hit either.
What you cannot force-include¶
Naming a standard-library, PyInstaller-internal or known third-party package has no effect. Those checks run before your declaration, so an app that happens to be named after a dependency can never claim it. Protecting a dependency's body has no upside and a real downside: it breaks apps at import time.
Precedence¶
--protect beats pylocket.toml, which beats the per-app default. Excludes are
evaluated before includes, so an exclude always wins.
Environment variables (local engine runs only)¶
PYLOCKET_FORCE_INCLUDE and PYLOCKET_FORCE_EXCLUDE are read by the protection
engine itself.
These do not work with the hosted service
The engine runs on PyLocket's build infrastructure, not on your machine, so
setting these in front of pylocket protect has no effect: the CLI only
uploads your artifact. Earlier versions of this page showed them as the way
to override selection, which was wrong. Use pylocket.toml, the REST API
field, or --protect instead.
Classification Priority¶
When PyLocket evaluates a module, it follows this priority order:
- Force-include override → Module is protected
- Force-exclude override → Module is skipped
- Entry module → Protected
- Not reachable from entry module → Skipped
- Standard library module → Skipped
- PyInstaller / packaging tool internal → Skipped
- Known third-party package → Skipped
- Standalone, reachable user module → Protected
Tips¶
- Start with defaults — PyLocket's automatic classification works correctly for the vast majority of projects. Only use overrides if you encounter a specific issue.
- Check the build's protection report —
GET /v1/apps/{app_id}/builds/{build_id}returnsmanifest_data.protection_log(which modules were protected and which were skipped) alongsidefunctions_protected,modules_protected,modules_skippedandskipped_by_reason. Your CI can assert on these without unpacking the artifact. - Separate your code from dependencies — Keeping your project structure clean (your code in a distinct package, dependencies installed via
pip) helps PyLocket classify modules accurately.
See Also¶
- Protection — What gets protected within each module
- How Protection Works — The full protection pipeline
- CI/CD Integration — Automating protection in your pipeline
- Configuration Reference — All environment variables