Skip to content

How Protection Works

This topic provides a high-level overview of the PyLocket protection pipeline — from uploading your artifact to running the protected application on an end-user's machine.


Protection Pipeline Overview

flowchart LR
    A["Upload\nArtifact"] --> B["Analyze\nBytecode"]
    B --> C["Protect\nFunctions"]
    C --> D["Package\nOutput"]

Stage 1: Upload

When you run pylocket protect, the CLI:

  1. Validates the artifact type (executable, wheel, zip, etc.)
  2. Uploads the artifact to PyLocket's secure cloud storage (encrypted at rest)
  3. Creates a protection job in the queue

Stage 2: Bytecode Analysis

The protection worker:

  1. Identifies the packaging format (PyInstaller, cx_Freeze, Briefcase, wheel, or ZIP)
  2. Extracts all Python bytecode from the artifact
  3. Enumerates every function, method, and class body in the code

Module Classification

Before encrypting, PyLocket classifies each module as user code (to be protected) or dependency code (to be skipped). Only your application code is protected — standard library modules, packaging tool internals, and common third-party packages are automatically excluded. You can override the classification with the PYLOCKET_FORCE_INCLUDE and PYLOCKET_FORCE_EXCLUDE environment variables. See Module Selection for details.

Stage 3: Function-Level Protection

Each discovered function is individually encrypted using PyLocket's proprietary protection pipeline. The original function body is replaced with a lightweight stub that delegates to the secure native runtime. This process ensures that the original logic is never present in plaintext within the distributed artifact.

Stage 4: Packaging

The protected output is assembled:

  1. The modified artifact (with stub-replaced functions)
  2. The encrypted function data
  3. A signed protection manifest
  4. The native runtime library
  5. Bootstrap code (injected into the application's startup path)

Stub Replacement

Original Python function:

def calculate(x, y):
    result = x ** 2 + y ** 2
    return math.sqrt(result)

After protection, a decompiler sees only a lightweight stub that delegates to the native runtime. The original logic is completely absent.

The stub: - Preserves the function signature (name, arguments, decorators) - Replaces only the function body - Delegates to the native runtime using the function's unique ID - Reveals nothing about the original logic

Each protected build uses randomized stub variations, so no two builds have the same stub fingerprint. Automated tools cannot pattern-match stubs across different builds.


Runtime Execution

When a protected application launches:

  1. Bootstrap runs automatically before any user code
  2. The bootstrap validates the license — either from a cached activation or by contacting the PyLocket activation service. The license key is obtained from the user prompt or the PYLOCKET_LICENSE_KEY environment variable.
  3. On successful validation, the bootstrap initializes the native runtime with the received key material
  4. When a stub function is called, the native runtime decrypts the function on demand, executes it, and securely zeroes the plaintext from memory

The master key is never embedded in the distributed artifact. Key material comes exclusively from the license activation service, ensuring that even full access to the artifact is insufficient for decryption without a valid license.

The native runtime handles all decryption, integrity verification, and security checks transparently. From the application's perspective, protected functions behave identically to their unprotected originals.


What Gets Protected (and What Doesn't)

Protected

  • All Python function bodies (functions, methods, lambdas, comprehensions)
  • Class bodies
  • Module-level code (the __init__ code object)

Not Protected

  • Non-Python files (images, configs, data files, HTML templates)
  • C extension modules (.pyd, .so files compiled from C/Cython)
  • String literals in function signatures and docstrings (these remain in stubs)
  • Module names and directory structure
  • __pycache__ files (these are recreated by the stubs)

Note: If you need to protect non-Python data, encrypt it at the application level and decrypt it in your protected Python code.


See Also