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

┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│  Upload  │───→│ Analyze  │───→│ Protect  │───→│ Package  │
│ Artifact │    │ Bytecode │    │Functions │    │  Output  │
└──────────┘    └──────────┘    └──────────┘    └──────────┘

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

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 (.pylocket_manifest)
  4. The native runtime (_pylocket_rt.pyd / .so)
  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:

def calculate(x, y):
    return _pylocket_rt.invoke("mymodule.calculate:5")

The stub: - Preserves the function signature (name, arguments, decorators) - Replaces only the function body - Calls the native runtime with 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. The bootstrap module loads the native runtime
  2. The runtime verifies the protection manifest's cryptographic signature
  3. The application prompts for license activation (or validates a cached token)
  4. On success, the application runs normally — protected functions are decrypted on demand as they are called

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