Protecting Python Applications Using Script Envelope

You can protect Python applications using the Script Envelope command-line tool (scriptenv.exe). This tool uses automated processes to apply advanced Sentinel LDK Envelope protection to a Python application.

NOTE   Script Envelope supports model file protection for Pytorch and TensorFlow models. For details, see Protecting Model Files for Python.

NOTE   Project files (.sprjx) are human-editable YAML files. While the legacy JSON format is still supported for backward compatibility, YAML is the preferred and recommended format.

You can perform the protection process under Windows (x64) or Linux (Intel). The protected application can run under Windows (32-bit or x64) or Linux (Intel 64 or ARM 64). Script Envelope automatically places the required runtime library under output_root\sntlruntime. For information on using Script Envelope under Linux, see Sentinel LDK Envelope for Linux.

Using Script Envelope consists of the following steps:

1. Prepare a project file that contains all the required information for protecting your Python application, including the location of the source files and the target location for the protected files.

2.Run Script Envelope, passing the project file path as a parameter to the tool.

NOTE   You must connect a Sentinel Vendor key to the machine that you use to run Script Envelope.

Prepare the Project File

Perform the following steps on a machine where Sentinel LDK Vendor Tools are installed.

1.Create or select a project folder to contain the project file for the Python application to be protected.

2.Copy a project file from any sample under %ProgramFiles(x86)%\Thales\Sentinel LDK\Samples\Envelope\Python\ to the project folder. For starting with Script Envelope, consider using simple.sprjx (standard encryption) or simple_native.sprjx (native compilation) from the simple_sample directory. For details, see Sample Projects. Rename the new project file to a relevant name for your application.

3.Open your project file in a text editor.

4.Modify the parameters in your project file as described in the table below.

Property Description
"file_entries" Section of the Project File
input_root

Absolute path of the directory that contains the Python application to protect.

output_root

Absolute path of the directory where Script Envelope writes the protected Python application. You execute the application directly, with no additional manual steps.

entry_scripts_glob

(Optional) Parameter used to specify one or more entry scripts for your project.

The following terms are used in this section:

>Stub: A stub is a small piece of code generated to load the runtime libraries necessary to execute protected scripts. When a script file is protected, the stub is created as a separate file with the same name as the protected file.

>Prologue: When the script file is not protected, the stub code is inserted within the original script file itself and is called a prologue.

Script Envelope generates stub code to ensure that the runtime libraries load correctly. The behavior of stub generation depends on whether you specify the entry_scripts_glob parameter.

When entry_scripts_glob is specified:

>The tool generates runtime-loading stubs only for the listed entry scripts.

>The stub appears either as a prologue (if the entry script is not protected) or as a separate stub file (if the entry script is protected) with the same name as the entry script.

>If you run the protected application with a script that is not included in the entry_scripts_glob parameter, the runtime does not load and execution fails.

When entry_scripts_glob is not specified:

>The tool generates stub files for all scripts.

Usage Examples:

You can specify either a single entry script or multiple entry scripts depending on your project requirements. For example:

>Single Entry Script: Use this format when your project has only one entry script. The tool generates stub files only for the specified script.

entry_scripts_glob: main.py

>Multiple Entry Scripts: Use this format when your project has more than one entry script. The tool generates stub files only for each valid script you specify.

entry_scripts_glob:
  - file.py
  - file2.py

Considerations:

>To enable selective stub generation, entry_scripts_glob must include at least one valid .py or .pyx file.

>Files with the .py and .pyx extensions are valid in entry_scripts_glob. If you include any other file extensions, the system generates an error. When you use native compiled protection and designate a .pyx script as an entry script, the tool compiles the script and injects the runtime prologue that executes main().

to_be_protected

Parameter that contains one or more sets of sub-parameters input_glob and (optionally) ignore_glob and feature_id (described later). The sub-parameters in each set identify a subset of files in the application to be protected with a specific Feature ID. For example:

to_be_protected:
  - input_glob: "*.*"
    ignore_glob: moduleC.py
    feature_id: 4
  - input_glob: "CD/*.py"
    feature_id: 14
  - input_glob:
      - "EF/*.py"
      - "GH/*.py"

The parameters in the example protect:

>All files in the root folder (excluding moduleC.py) with Feature ID 4.

>All Python files in the CD folder with Feature ID 14.

>All Python files in the EF and GH folders by default with the global_feature_id.

Script Envelope creates runtime stubs or natively compiles Python (.py and .pyx) files according to the configured protection method. The tool encrypts all other matching files as data files for use at run time.

Overriding Protection Parameters in Files

The following files are copied from the input_root location to the output_root location with no change:

>Files that are not included in any input_glob parameter.

>Files included in an input_glob parameter but excluded using the ignore_glob parameter.

To protect model files:

>To apply model file protection for TensorFlow, the to_be_protected parameter must include the TensorFlow model files. For example:

  - input_glob: model.keras
    ignore_glob: ""
    feature_id: 0

>To apply model file protection for PyTorch, the to_be_protected parameter must include the PyTorch model files. For example:

  - input_glob: model.pt
    ignore_glob: ""
    feature_id: 0

NOTE   Overlap Resolution: If a file matches the input_glob of multiple to_be_protected blocks, the parameters of the last matching block in the list take precedence. To prevent a file from matching a block, exclude the file using ignore_glob.

input_glob

Glob pattern that identifies the files under the input root to be protected by Envelope using the associated Feature ID (see feature_id). For example:

"**/*.py" Recursively protect all .py files under the root folder.
"ABC/*.py" Protect all Python files directly under the ABC folder.

You can specify multiple patterns. For example:

["EF/*.py", "GH/*.py"] Protect all .py files under the EF and GH folders.

For details, see https://docs.python.org/3/library/glob.html.

ignore_glob

(Optional) Files to exclude from the input glob. For example:

input_glob: "**/*.py"
ignore_glob: fileA.py

This example indicates that Script Envelope recursively protects all .py files under the root folder, excluding fileA.py.

feature_id

(Optional) Feature ID (in the range 0–65535) for the files in the associated input_glob that you are protecting. If feature_id is not specified, the global Feature ID (see global_feature_id) is used.

method

(Optional) Overrides the global_method setting (defined in the settings section) for this specific file entry block.

Available values are:

>native: Natively compiles Python scripts into cross-platform machine code modules.

>encrypt: Protects Python scripts using traditional bytecode encryption.

Default: The value defined in global_method. If global_method is also omitted, the default is encrypt.

bundle_mode

(Optional) Configures how the tool groups multiple Python files in this entry block into compiled extension modules. This setting is only applicable when the protection method is set to native.

Available values are:

>per_file: Natively compiles each matching Python script into an individual, separate compiled extension module (default mode).

>single: Groups and compiles all matching Python scripts in this entry block into a single combined compiled extension module.

>per_package: Groups and compiles each Python package directory (a directory containing an __init__.py file) into a single combined compiled extension module.

bundle_name

(Optional) Specifies a custom filename for the compiled extension module. This setting is only applicable when bundle_mode is set to single.

Default: If you omit this property, Script Envelope automatically generates a name for the combined compiled binary module.

native_settings

(Optional) Specifies compiler and linker configurations for natively compiled Python (.py) scripts and typed Cython (.pyx) modules (such as Cython modules that use cdef extern statements to statically link external C libraries).

The parameter accepts the following sub-properties, defined either as common keys or mapped under per-target platform blocks (for example, windows-x64 or linux-x86_64). Paths resolve relative to input_root:

>include_dirs: Directories containing C header (.h) files to search during compilation (maps to -I).

>library_dirs: Directories containing library files to search during linking (maps to /libpath: or -L).

>libraries: Static or import libraries to link against (maps to .lib or -l).

>compiler_flags: List of strings containing flags passed verbatim to the C compiler, one per element. Flags defined in a target-specific block append to the common list, which appends to flags defined under global settings. Compiler flags that compromise protection are rejected.

>linker_flags: List of strings containing flags passed verbatim to the linker, one per element. Flags follow the same merge order as compiler_flags.

Platform C Runtime Override: Natively compiled modules link against a minimal self-contained runtime layer by default. If an external library requires standard C runtime functions (such as malloc, printf, or fopen), declaring libcmt.lib/msvcrt.lib (Windows) or -lc/libc.so/libc.a (Linux) under libraries or linker_flags switches the module to link against the real platform C runtime.

Note: Python applications that load native libraries dynamically at run time (for example, using ctypes.CDLL) do not require native_settings.

obfuscation_level

anti_tamper_level

anti_trace_level

(Optional) Overrides the global code protection intensity passes for this specific file entry block. Requires plus: true and Feature 302 on the developer key.

Available values are:

>0: Turns off this specific code protection pass.

>1 to 3: Applies the code protection pass, with 3 representing the maximum protection level.

Anti-tamper and anti-trace require obfuscation greater than 0. If you configure obfuscation_level: 0 for a section in a project configured with plus: true, you must also set anti_tamper_level: 0 and anti_trace_level: 0 in that section. Otherwise, the section inherits the active Plus defaults for anti-tamper and anti-trace, causing the build to fail with error C0048.

Default: Inherits the values defined in global_obfuscation_level, global_anti_tamper_level, and global_anti_trace_level.

Note: The plus parameter under settings functions as a master switch. If you configure plus: false, all code obfuscation, anti-tamper, and anti-trace passes are disabled across the build, regardless of the individual levels configured here.

preserve_docstrings

(Optional) Overrides the global_preserve_docstrings setting for this specific file entry block. Available with Native Compilation without requiring Script Envelope Plus. Controls whether Python __doc__ docstrings on functions and classes remain in compiled modules.

Available values are:

>true: Preserves docstrings in the compiled binary module.

>false: Strips docstrings to prevent exposing source descriptions.

Default: The value defined in global_preserve_docstrings (default: false).

preserve_tracebacks

(Optional) Overrides the global_preserve_tracebacks setting for this specific file entry block. Controls whether function names, filenames, and line numbers appear in Python exception tracebacks.

Available values are:

>true: Preserves exception traceback frames.

>false: Disables traceback frame registration in the compiled module, stripping string literals from the binary while allowing exceptions to raise and propagate normally.

Default: The value defined in global_preserve_tracebacks (default: false).

symbols

(Optional) Configures granular, per-module or per-function code protection overrides using Python qualified-name globs.

You can define the symbols map inline or reference a standalone YAML profile file (or list of profile files) using the external key. For example:

symbols:
  external: protection/crypto-symbols.yaml
  "crypto.derive_key": ["obfuscation.level=3"]
  "crypto.SecureChannel.connect": ["obfuscation.level=2"]
  "crypto.*": ["anti-trace.off"]

Keys support Python qualified-name globs for functions (module.function), class methods (module.Class.method or module.Class.*), or entire modules (module.*). Exactly one class level is supported. For nested classes (such as module.Outer.Inner.method) or classes defined inside a function, specify the symbol using the bare-name form (module.method) instead of the nested class hierarchy (for example, specify module.method rather than module.Outer.Inner.method). Non-ASCII class or function names are addressable only by their bare name.

If a symbols key matches no function, the build emits warning C0081.

If a section specifies a symbols map when plus is set to false (or omitted), per-function protection is not applied, and the build emits warning C0080.

Note: Sentinel LDK Script Envelope does not validate directive strings specified in the symbols map. Supported directives include obfuscation.level=1..3, anti-tamper.level=1..3, anti-trace.level=1..3, and .on or .off toggles. Numeric levels are strictly 1, 2, or 3. To disable a protection family for a specific symbol, specify the .off toggle (for example, ["obfuscation.off"]). The syntax level=0 is not supported and causes the build to fail with error C0083. If you disable obfuscation for a symbol, you must also disable anti-tamper and anti-trace for that symbol. Unrecognized or misspelled directives pass through to the native protection backend and are accepted without a build warning or error. Verify that all directive strings strictly follow the supported syntax.

When several patterns match a function, entries are applied in the order of the map and entries listed later take precedence, per protection family (obfuscation, anti-tamper, anti-trace). A later entry overrides only the protection family that the entry specifies; other families carry down from earlier matching entries. If a later entry overrides an earlier entry's value for the same family, build warning C0074 is emitted. Therefore, list broad patterns (such as module.*) first and specific functions or methods after them.

The symbols map operates as the third level of the protection cascade (Global > Section > Module or Function), where the most specific definition takes precedence across levels. At the same level, the later entry takes precedence.

module_obfuscation

(Optional) Overrides the global_module_obfuscation setting for the files and packages/folders matched by this section. Replaces names of Python modules and folders with randomized, meaningless names (for example, shop/pricing.py becomes e000004/h000007.py) while preserving internal import statements.

Available values are true and false.

Works with the encrypt method; a section using native compilation ignores this setting. A section decides for folders as well as files. A section that matches only a folder renames that folder. If several sections match the same file or folder, the last matching section decides. Application entry scripts, __init__.py files, and non-Python data files retain their original names.

Deployment Considerations: A protected application with obfuscated names can be copied, moved, or renamed, and started from any working directory. Because obfuscated names are uniquely generated per project build, you must deploy the protected output directory as a complete unit. Individual modules from different protection builds cannot be combined.

Default: The value defined in global_module_obfuscation under settings. If global_module_obfuscation is not specified, the default is false.

"settings" Section of the Project File
vendor_code_file Absolute path of your vendor code file on the machine.
custom_scope

If provided, the protected program searches for a Sentinel protection key according to the custom login scope that you specify.

For information on the syntax for login scope parameters, see the topic "Scope Input XML Tags" in Sentinel Licensing API C Reference. You can also paste a login scope that was created using Sentinel LDK ToolBox in this field.

Note: In YAML, double quotes inside double-quoted strings must be escaped with a backslash (\"). Alternatively, you can enclose the entire string in single quotes to avoid escaping the inner double quotes entirely:

custom_scope: '<haspscope><hasp type="HASP-HL" /></haspscope>'
global_feature_id

(Optional) Global Feature ID (in the range 0–65535) for the complete application that you are protecting. This Feature ID is also applied by default for any input_glob (under to_be_protected) for which a corresponding feature_id is not specified.

Default: 0

background_check

(Optional) Specify a time interval (in seconds) of 0–65535 for periodic background checks for a Sentinel protection key with the required licenses. Default: 300 seconds.

If you specify a value of 0, a periodic background check is not performed.

Regardless of the setting for this parameter, the License Manager checks for the required license when the application is started.

global_module_obfuscation

(Optional) Configures the project-wide default for module and package folder name obfuscation under the encrypt method. Available values are true and false. Default: false.

global_method

(Optional) Specifies the default protection method for all file entries in the project.

Available values are:

>native: Natively compiles Python scripts into cross-platform machine code modules.

>encrypt: Protects Python scripts using traditional bytecode encryption.

Default: encrypt

targets

(Optional) A list of target platforms for which the tool natively compiles modules and copies the matching sntlruntime files.

Available target identifiers are:

>windows-x64: Targets 64-bit Windows platforms.

>windows-x86: Targets 32-bit Windows platforms.

>linux-x86_64: Targets 64-bit Linux x86 platforms.

>linux-arm64: Targets 64-bit Linux ARM platforms.

Default: If you omit this property, the tool builds for all supported target platforms.

native_fallback

(Optional) Specifies the build behavior when the compiler cannot natively compile a specific Python module.

Available values are:

>error: Stops execution and fails the build immediately.

>encrypt: Automatically falls back to protecting that specific module using traditional bytecode encryption, allowing the build to complete.

Default: error

global_obfuscation_level

global_anti_tamper_level

global_anti_trace_level

(Optional) Configures the code protection intensity passes applied directly to the natively compiled machine code modules.

Available values are:

>0: Turns off this specific code protection pass.

>1 to 3: Applies the code protection pass, with 3 representing the maximum protection level.

Default:

>When plus: false (or omitted): 0 (off).

>When plus: true: 1 (baseline hardening for all three protection families).

Rule: Anti-tamper and anti-trace require obfuscation greater than 0. If you configure global_obfuscation_level: 0 in a project with plus: true, you must also explicitly configure global_anti_tamper_level: 0 and global_anti_trace_level: 0. Otherwise, the build fails with error C0048.

plus

(Optional) Specifies whether the build processes invoke the Script Envelope Plus machine code hardening features (obfuscation, anti-tamper, and anti-trace). This parameter functions as a master switch: setting plus: false disables all machine code hardening passes across the build, regardless of the individual levels configured for sections or symbols. Valid only when protecting files using Native Compilation. Requires Feature 302 on the developer key.

Available values are:

>true: Enables machine code hardening passes on natively compiled modules.

>false: Disables machine code hardening passes.

Default: false

NOTE   Native compilation to .pyd and .so binaries is included with Sentinel LDK Script Envelope. If you specify plus: false, native compilation continues to build working binaries, but protection levels are forced to 0. If global or section protection levels are configured, the build emits warning C0056. If per-function protection overrides are configured in a symbols map, the build emits warning C0080.

global_preserve_docstrings

(Optional) Specifies the project-wide default for docstring retention in natively compiled modules. Available with Native Compilation without requiring Script Envelope Plus.

Available values are true and false.

Default: false

global_preserve_tracebacks

(Optional) Specifies the project-wide default for exception traceback frame retention in natively compiled modules.

Available values are true and false.

Default: false

min_python_version

(Optional) Specifies the minimum CPython stable Application Binary Interface (abi3) floor version. This parameter applies only to Native Compilation. A module compiled at the 3.9 floor runs on Python 3.9 through 3.14+.

Available values range from "3.9" to "3.14". Set to "3.11" when applications use Python 3.11 exception groups (except* syntax).

Default: "3.9"

background_check_policy

(Optional) Configures background licensing check and thread suspension behaviors during periodic licensing checks.

The parameter contains the following sub-properties:

>grace: A boolean value specifying whether to enable grace period licensing checks. If set to true, the application presents an interactive Retry / Ignore / Abort prompt when a background licensing check fails, rather than exiting immediately. Default: false.

>grace_count: An integer value (0–254) specifying the number of times Ignore may be selected before the grace period expires and the application terminates. Specifying 0 allows unlimited retries. The count resets to the full configured allowance as soon as a background check succeeds (reconnecting the key restores the allowance). Default: 3.

>suspend_threads: A boolean value specifying whether to freeze all application execution threads while a failure prompt is displayed, preventing unlicensed execution while waiting for user response. Default: true.

>auto_resume: A boolean value specifying whether the application automatically resumes execution when the license becomes available again. When message output mode is set to windows, the runtime closes the message dialog automatically and resumes execution without requiring user interaction. Default: false.

>auto_relogin: A boolean value specifying whether execution transparently attempts to re-establish the login session to the same global feature if a licensing session disconnects due to transient network errors (such as LDK Licensing API error 39). If re-login succeeds, execution continues without displaying an error; if re-login fails, the underlying licensing error is returned. Default: true.

license_startup_failure_policy

(Optional) Configures licensing behavior when the startup license check fails to find a valid Sentinel license.

Available values are:

>exit_process: Terminates the process immediately with the license error code (default behavior).

>fail_init: Fails only the runtime helper library initialization and raises a SentinelLicenseError exception, allowing the host application to handle the error.

>retry_infinite: Pauses execution and continuously retries the license check until a valid license appears.

Default: exit_process

global_license_failure_policy

(Optional) Specifies how a per-module license check reacts when the Feature ID assigned to a module is missing from the connected key during execution (for example, when a module is imported mid-run). Available policies are:

>retry_infinite: Pauses execution and continuously retries the license check until a valid Sentinel license key is attached (default).

>fail_load: The Python import statement raises an ImportError (or SentinelLicenseError) exception, allowing application code to handle the missing modular feature gracefully without terminating the entire application. Note: Supported on Windows only.

>exit_process: Terminates the application process immediately.

Default: retry_infinite

tensorflow_model_file_support

pytorch_model_file_support

(Optional) Enables automatic encryption and runtime transparent decryption for machine learning model files.

Available values are true and false. Default: false.

model_write_protection

(Optional) Controls run-time file creation for machine learning models.

Available values are:

>0: Allows plain model writes.

>1: Denies model writes.

>2: Automatically encrypts generated model files (default).

message_output_mode

(Optional) Specifies how run-time user messages are delivered. Available output modes are:

>windows: If assigned the value true, displays messages in a message box dialog.

>stderr: If assigned the value true, writes messages to the console.

Default: If no output modes are specified, windows mode is set to true and stderr mode is set to false.

message_sets

(Optional) Configures run-time error messages and localized grace dialogs displayed during execution of the protected application. You can define message sets either as an external reference or inline within the project file:

>External message sets: Reference an external YAML message file with the .smsgx extension using external: <path>. Paths resolve relative to the project file directory. To reference pre-delivered message sets provided with Sentinel LDK, use the ${MESSAGE_SETS} macro variable (for example, ${MESSAGE_SETS}/de-DE.smsgx). Pre-delivered message sets reside in the VendorTools\toolchain\message_sets directory on Windows (toolchain/message_sets on Linux). You can also use the ${SCRIPTENV} path token, which resolves to the Script Envelope installation directory. An external .smsgx file cannot reference another external file.

>Inline message sets: Define an array of message sets directly in the project file. Each set contains language (language identifier), caption (dialog title bar text), and messages (an array of entries with predefined message id and custom message text).

Automatic Language Selection and Fallback:

At run time, the application detects the operating system user locale and selects the message set using the following sequence:

>Exact locale match: Matches both language and region (for example, de-AT matches a de-AT set).

>Language match, any region: If no exact match is found, selects the first message set matching the primary language code (for example, the first available de-* set).

>Default set fallback: If no matching language is found, falls back to the first message set in the list (typically en-US).

Within the selected set, if a specific message ID is missing, execution falls back per message to the text in the default (first) message set.

Overriding the Locale (ENVELOPE_LOCALE):

Set the ENVELOPE_LOCALE environment variable (for example, ENVELOPE_LOCALE=de-DE, or ENVELOPE_LOCALE=he-IL for right-to-left layout) to override the operating system locale without modifying operating system language settings.

5.Save the project file.

Run Script Envelope

Perform the following steps on the machine where your project file is located and Sentinel LDK Vendor Tools are installed.

1.Open a Windows command-line prompt.

2.In the command-line prompt, change to the following path:

%ProgramFiles(x86)%\Thales\Sentinel LDK\VendorTools\VendorSuite\

3.Enter the following command:

 scriptenv.exe projectFile [-q] [-v]

where:

projectFile

The relative or absolute path and name for the project file that you prepared earlier.

-q (or --quiet)

(Optional) If specified, displays only error and warning messages.

Default: Verbose messages are displayed.

-v (or --verbose)

(Optional) Displays detailed warning and error information during the protection build.

NOTE   You can enter scriptenv.exe -h or scriptenv.exe --help to display help for scriptenv.

Envelope protects the Python application that you specified in the input_root parameter and writes the application to the path that you specified in the output_root parameter.

Migrating Legacy JSON Project Files to YAML

The convert command allows you to convert your legacy JSON project file to YAML while preserving duplicate __Comment keys as standard YAML comments (#).

NOTE   The hyphen (-) in YAML represents array items. When converting JSON to YAML, map each left bracket ([) to a hyphen and omit hyphens for properties inside the item.

1.Open a Windows command-line prompt.

2.Change to the directory containing scriptenv.

3.Run either of the following commands:

scriptenv.exe convert input.sprjx output.sprjx

or

scriptenv.exe --convert input.sprjx output.sprjx

where input.sprjx is your legacy JSON project file, and output.sprjx is the name of the new YAML-formatted project file to be created.

NOTE   The tool only processes and validates files with the .sprjx extension.

Syntax Error Handling and Validation

Script Envelope validates the project file and displays syntax suggestions if validation fails.

>YAML files: The tool checks for correct indentation with spaces.

>Legacy JSON files: The tool checks for missing commas, trailing commas, and unescaped quotes.

Native Protection with Script Envelope

Native protection enables you to protect proprietary Python application code by compiling selected Python modules into native machine code. This approach reduces exposure of Python bytecode because the protected application logic is delivered and executed as native binary modules rather than as encrypted Python bytecode.

You can use native protection alongside other Script Envelope protection mechanisms, allowing you to select the appropriate protection method for different parts of your application.

Native compilation and Script Envelope Plus hardening are separate capabilities. Native compilation converts selected Python source files into native extension modules. Script Envelope Plus adds advanced protection techniques, such as code obfuscation, anti-tamper protection, and anti-trace protection. To enable Script Envelope Plus protection, configure plus: true in the project file. The developer key used during protection must contain the Script Envelope Plus entitlement, Feature 302.

How Native Protection Works

Native protection operates in two stages: protection time and runtime.

Protection Time (Building the Protected Application)

During the protection process, Script Envelope automates the following sequence:

>Discover application modules and dependencies: The tool inspects your source directory, maps module dependencies, excludes __pycache__ bytecode directories, and reads protection settings from the .sprjx file.

>Generate native code: The tool translates designated .py and typed .pyx source files into optimized C code using Cython against the standard CPython C-API.

>Compile and harden the native modules: The native protection backend compiles the C code into optimized, target-specific machine code extension modules (.pyd on Windows and .so on Linux). When you specify plus: true with a licensed developer key (Feature 302), the backend injects requested code protection passes (obfuscation, anti-tamper, and anti-trace) directly into the compiled machine code.

>Build self-contained binaries: The compiled modules are self-contained without dynamic Microsoft Visual C++ redistributable dependencies on Windows and without restrictive GNU C Library (glibc) version dependencies on Linux, allowing modules to run portably across supported distributions.

>Package the protected application: The tool bundles modules according to your bundle_mode selection, generates cleartext bootstrap loader stubs for entry points, constructs the multi-architecture routing bridge (for example, sntlbundles.py) for multi-target builds, and encrypts non-Python resource data files.

Runtime (Running the Protected Application)

When an end user executes your protected application, the execution flow proceeds as follows:

>Initialize the protection runtime: The end user launches the application using standard Python. The generated entry loader script executes first, initializes the Script Envelope Runtime, and verifies the license for the configured Feature ID against the connected Sentinel protection key.

>Select the appropriate native module: For multi-architecture distributions, the routing bridge detects the platform architecture of the active Python interpreter (such as windows-x64 or linux-arm64) and dynamically imports the matching binary extension module from the sntlnative/ directory.

>Validate protected modules at load time: As the Python interpreter imports each compiled native module, the module performs a fast validation against the active licensing session in the Script Envelope Runtime. The module does not start background timers or additional threads.

>Execute protected logic as native code: Protected logic executes directly on the processor as machine code. Encrypted resource data files are decrypted on demand when the application accesses them.

>Maintain the licensing session: The Script Envelope Runtime maintains the active licensing session in a separate background thread, performing periodic checks on the schedule defined in background_check and displaying interactive grace dialogs if the protection key disconnects.

Protected Output Structure

When you use native protection (protection method set to native), Script Envelope compiles your application Python source modules into native binary modules and creates the following output structure:

>Entry Scripts: For each script specified in the entry_scripts_glob parameter, the tool generates a small, cleartext bootstrap loader script (for example, main.py) and compiles the actual protected application logic into a separate native module (such as main_r.pyd on Windows or main_r.abi3.so on Linux). The bootstrap loader script automatically initializes the licensing session and imports the native module.

>Non-Entry Scripts: Other protected scripts are natively compiled directly into standard native extension binaries (for example, moduleA.pyd or moduleA.abi3.so). No cleartext Python files remain in the output directory for these modules.

>Runtime Files: Required runtime libraries are placed in the sntlruntime/ directory under output_root.

Multi-Architecture Packaging Layout

If your targets list contains more than one platform architecture, Script Envelope automatically reorganizes the natively compiled modules into target-specific subdirectories under a shared sntlnative/ directory (for example, sntlnative/windows-x64/ and sntlnative/windows-x86/). The generated loader stub in the root directory contains an architecture-detecting routing mechanism that dynamically imports the correct native module matching the running Python interpreter's architecture.

Licensing Behavior and Failure Policies

Developer Key Entitlement (Feature 302)

Script Envelope Plus hardening is licensed by a dedicated feature on the developer key connected to your build machine. A project opts in to hardening passes by setting plus: true in the project file.

The developer key connected during protection must contain the Script Envelope Plus entitlement (Feature 302). If you specify plus: true with demo vendor code (DEMOMA), the build succeeds without code hardening passes and logs warning C0057. If you specify plus: true without a valid Feature 302 license on the developer key, the build stops with error C0062.

NOTE   Feature 302 applies only to Script Envelope Plus for Python. Verify that your developer key holds Feature 302 for Python hardening.

Evaluation Entitlement

You can request a temporary evaluation entitlement (for example, a 30-day evaluation) for Script Envelope Plus. Thales provides the entitlement as a remote license update to your existing developer key.

To activate the evaluation entitlement:

1.Generate a machine fingerprint (C2V file) from your developer key.

2.Send the C2V file to Thales support.

3.Apply the returned V2C license update to activate the evaluation entitlement on your developer key.

The evaluation entitlement produces fully hardened native extension modules. Each module includes the trial expiration date. At run time, the module displays a warning that indicates the number of trial days remaining and stops running after the trial expires.

Use evaluation builds only for evaluation. The trial expiration prevents you from distributing these builds to end users.

Startup and Runtime Licensing Failure Policies

You can configure how the application reacts when a license check fails:

>Startup Login Failure (license_startup_failure_policy): Configures application behavior when the initial application startup login fails (exit_process, fail_init, or retry_infinite).

>Mid-Run Module Import Failure (global_license_failure_policy): Configures behavior when a module is imported mid-run and the Feature ID assigned to that module is unentitled (retry_infinite, fail_load [Windows only], or exit_process).

For parameter options and defaults, see the property table above. For details on configuring end-user licensing models in Sentinel EMS and distributing customer licenses (C2V and V2C remote updates), see the Sentinel LDK Software Protection and Licensing Guide (for Sentinel EMS or Sentinel LDK-EMS).

Advanced Technical Configurations

Linking External C Libraries and Real Platform CRT Overrides

When you protect natively compiled Python (.py) scripts or typed Cython (.pyx) modules (including modules that use cdef extern statements to statically link external C functions), configure compiler and linker options under the native_settings block within the file_entries.to_be_protected section. This block accepts include_dirs, library_dirs, libraries, compiler_flags, and linker_flags definitions mapped per target platform. Paths resolve relative to input_root.

By default, natively compiled modules link against a minimal self-contained runtime layer that eliminates dynamic glibc or Microsoft Visual C++ redistributable dependencies. If an external C library requires functions from the standard platform C runtime (such as printf, malloc, or fopen), you can opt into the real platform runtime:

>Windows: Specify libcmt.lib or msvcrt.lib in libraries or linker_flags to link against the standard Microsoft Visual C++ runtime.

>Linux: Specify -lc, libc.so, or libc.a in libraries or linker_flags to link against standard Linux glibc.

For example:

to_be_protected:
  - input_glob: "fast_math.pyx"
    method: native
    native_settings:
      include_dirs: ["include"]
      library_dirs: ["lib"]
      libraries: ["vendor_math"]
      linker_flags: ["libcmt.lib"]

External Message Sets (.smsgx) and Multi-Language Runtime Fallback

To simplify localization across multiple projects, you can store message sets in standalone YAML files with the .smsgx extension. Reference these external files under settings.message_sets using the external key.

To avoid hard-coded file paths, you can use two built-in path tokens:

>${MESSAGE_SETS}: Resolves to the pre-delivered message sets provided with Sentinel LDK in the VendorTools\toolchain\message_sets directory on Windows (toolchain/message_sets on Linux).

>${SCRIPTENV}: Resolves to the Script Envelope installation directory.

NOTE   An external .smsgx file cannot reference another external file.

For example:

settings:
  message_sets:
    - external: ${MESSAGE_SETS}/de-DE.smsgx
    - external: ${MESSAGE_SETS}/fr-FR.smsgx
    - language: en-US
      caption: Sentinel LDK Protection System
      messages:
        - id: 7
          text: Sentinel protection key was not found

You can reference the pre-delivered message sets directly, or copy the files to your project folder and customize the text according to your requirements.

Automatic Language Selection and Fallback

At run time, the application detects the operating system user locale and selects the message set using the following sequence:

1.Exact locale match: Matches both language and region (for example, de-AT matches a de-AT set).

2.Language match, any region: If no exact match is found, selects the first message set matching the primary language code (for example, the first available de-* set).

3.Default set fallback: If no matching language is found, falls back to the first message set in the list (typically en-US).

Within the selected set, if an individual message ID is not defined, execution falls back per message to the text in the first message set in the list. In addition, evaluation notices resolve through localized message set codes: 5020 (Demo key notice), 5021 (evaluation trial warning with remaining days), and 5022 (evaluation trial expired error).

Overriding the Locale (ENVELOPE_LOCALE)

Set the ENVELOPE_LOCALE environment variable (for example, ENVELOPE_LOCALE=de-DE, or ENVELOPE_LOCALE=he-IL for right-to-left layout) to override the operating system locale without modifying operating system language settings.

Best Practices and Performance Optimization

Recommended Setup for Most Applications

For optimal security and performance, Thales recommends compiling your Python source files into native machine code and bundling all modules into a single binary extension module (method: native and bundle_mode: single):

>Native Compilation Over Encryption: Natively compiled modules eliminate recoverable bytecode from disk and memory. Bytecode encryption decrypts code into memory at run time, leaving cleartext code objects vulnerable to memory extraction.

>Single Bundle Packaging: Merging all modules into a single compiled binary removes cleartext Python file structures, eliminates cross-module import visibility, and maximizes cross-function code hardening passes.

CPython Stable ABI (abi3) Compatibility

Native compilation targets CPython's stable Application Binary Interface (abi3) by default. A module compiled at the 3.9 floor runs across CPython 3.9, 3.10, 3.11, 3.12, 3.13, and 3.14+, allowing you to distribute and test a single compiled binary artifact per platform architecture. If your application relies on features introduced in later Python versions, such as Python 3.11 exception groups (except* syntax), set min_python_version to "3.11".

Aspect abi3 (Default) Version-Specific ABI
Runs on The floor version and every newer CPython release (3.9 through 3.14+) Only the exact minor version built for (for example, only Python 3.11)
Builds to ship One binary module for all Python versions One binary module per supported Python version
C-API available Restricted stable C-API subset Full CPython C-API
New Python release Existing binary module continues to execute without rebuilding Rebuild required for each new Python minor release

Performance Characteristics and Hardening Impact

Protection methods and code protection levels influence execution speed and binary size:

>Native Compilation Speedup: Natively compiled modules execute directly as machine code, providing faster execution than interpreted Python bytecode for CPU-bound routines.

>Typed Cython Speedup: Providing type annotations in .pyx files yields significant speedups by eliminating CPython dynamic object lookups.

>Effect of Obfuscation Levels: Code protection Level 0 provides baseline performance and compact binary sizes. Increasing protection levels (1 through 3) applies control-flow transformations and integrity guards, trading execution speed and binary size for enhanced resistance to reverse engineering.

Level Protection Strength Relative Size & Speed Impact
0 (off) Compiled machine code without obfuscation Baseline performance (smallest binary, fastest execution)
1 Light instruction obfuscation Low impact on binary size and execution speed
2 Moderate control-flow transformations Medium impact on binary size and execution speed
3 Strong machine code transformations Highest impact on binary size and execution speed

NOTE   Rule: Anti-Tamper and Anti-Trace Require Obfuscation: In any scope where anti-tamper or anti-trace is set above 0, obfuscation must also be set above 0 in that same scope, or the build terminates with an error. In addition, anti-trace protection is supported on x86 and x86-64 architectures (Windows and Linux); on ARM64 architectures, anti-trace is skipped with a diagnostic warning.

Build Diagnostics and Troubleshooting

Command-Line Interface (CLI) Diagnostic Options

Script Envelope provides command-line flags to inspect developer key entitlements and troubleshoot build steps:

>scriptenv --license-info <vendor_id | file.hvc | project.sprjx>: Reports whether the developer key connected to the build machine contains a valid Feature 302 entitlement, an active evaluation license, or no license. You can pass a Vendor ID, a Vendor Code file (.hvc), or a project file (.sprjx). For example:

•scriptenv --license-info 37517 (passes a Vendor ID)

•scriptenv --license-info DemoMB.hvc (passes a Vendor Code file)

•scriptenv --license-info test.sprjx (passes a project file)

>scriptenv -v (or --verbose): Enables verbose compiler and linker diagnostic logs during protection runs.

Resource File Protection and Warning C0071

A section configured with method: native compiles only Python and typed Cython source files (.py and .pyx). If non-Python data files (such as .json or .dat) match the section input_glob, Script Envelope does not fail the build. Instead, the tool automatically encrypts the resource file and logs diagnostic warning C0071:

>warning C0071: resource file '<filename>' matches a native section (glob '<pattern>') but cannot be compiled; the file will be encrypted instead

The encrypted resource file decrypts transparently on read at run time through the runtime hook. To prevent this warning, declare non-Python resource files in a separate section with method: encrypt.

Cython .pyx Protection and Warning C0069

Cython source files (.pyx) require compilation to native C extensions and cannot run as interpreted bytecode. If you specify method: encrypt (or declare global_method: encrypt) on a section matching .pyx files, Script Envelope automatically overrides the protection method to native and logs diagnostic warning C0069:

>warning C0069: method 'encrypt' does not apply to Cython source (.pyx); '<filename>' will be compiled natively instead

Common Build Warnings

>error C0048: anti-tamper and anti-trace require obfuscation to be enabled in the same scope (obfuscation level > 0): Indicates that anti-tamper or anti-trace protection is active while obfuscation is set to 0. When you set obfuscation_level: 0 (or global_obfuscation_level: 0) with plus: true, you must also set anti_tamper_level: 0 and anti_trace_level: 0 in that scope. Anti-tamper and anti-trace mechanisms cannot operate without code obfuscation.

>warning C0056: obfuscation / anti-tamper / anti-trace levels are set but `plus` is not enabled; they have no effect: Indicates that code hardening levels are configured in the project, but plus is set to false (or omitted). The application builds without code hardening.

>warning C0057: Demo vendor code is in use. `plus` protection features are disabled: Indicates that plus: true is specified with the DEMOMA demo vendor code. The application builds without code hardening.

>error C0062: `plus` is enabled but no license for it is present; run --license-info to check: Indicates that plus: true is configured in the project file, but the connected developer key does not license Feature 302 (Script Envelope Plus). Connect a developer key programmed with Feature 302, run scriptenv --license-info to inspect entitlements on your key, or set plus: false when machine code hardening is not required.

>warning C0074: `<function>`: `<family>` set by pattern `<pattern2>` overrides the value from pattern `<pattern1>`: Indicates that a pattern listed later in the symbols map has overridden a setting from an earlier pattern. Reorder the map so that broad patterns (such as module.*) precede specific function entries.

>warning C0080: section '<glob>' sets per-function protection in `symbols` but `plus` is not enabled; the `symbols` map has no effect: Indicates that a section defines per-function protection overrides in the symbols map, but plus is set to false (or omitted). Set plus: true under settings to apply per-function protection.

>warning C0081: symbols key '<key>' in section '<section>' matched no function: Check for spelling errors or unsupported nested class paths in the symbols map.

>error C0083: symbols key '<key>' in section '<section>': '<level>' is not a valid level; the level must be 1, 2 or 3: Indicates that a directive in the symbols map specifies a protection level outside the supported 1..3 range (for example, obfuscation.level=4). Set the protection level to 1, 2, or 3, or use .off to disable that protection family.

>warning C0051: Skipping __pycache__ directory '<path>': Normal notification confirming bytecode caches were excluded from native compilation.

Sample Projects

Sentinel LDK provides sample projects under the %ProgramFiles(x86)%\Thales\Sentinel LDK\Samples\Envelope\Python\ directory to demonstrate protection configurations:

>simple_sample: The recommended starting point for customizing a project file for your own application. Includes two sample project files: simple.sprjx (standard encryption) and simple_native.sprjx (native compilation). Most settings are explicitly defined with comments explaining their default values and usage.

>pyx_bounce: Demonstrates a cross-platform bouncing-ball GUI application built using typed Cython (.pyx), single bundling, and Script Envelope Plus hardening. Designed for validating background licensing checks, thread suspension, grace prompts, and multi-language message sets (configured through message_sets and the ENVELOPE_LOCALE environment variable).

>native_sample: Demonstrates basic native compilation of a multi-module Python application end-to-end, including an x86 build variant.

>pyx_sample: Demonstrates protecting a typed Cython (.pyx) module alongside standard Python scripts, showcasing per-file and single-bundle compilation speedups.

>complex_sample: Demonstrates bundling multiple cross-importing modules into a single binary, plus multi-architecture packaging with dynamic routing.

>pkg_sample: Demonstrates packaging multiple Python packages using bundle_mode: per_package.

>resource_sample: Demonstrates scoping non-Python resource files (such as configuration JSON files) into explicit encryption sections to decrypt transparently on read at run time.

>obfuscation_sample: Demonstrates module and package folder name obfuscation under encryption, replacing module and folder names with randomized identifiers while preserving application import logic.

>symbol_protection_sample: Demonstrates tuning protection for individual functions using an inline symbols map and an external shared profile file (requires Feature 302).

>traceback_sample: Demonstrates comparing error tracebacks when preserve_tracebacks is enabled versus disabled.

>license_sample: Demonstrates handling mid-run module license failures using global_license_failure_policy: fail_load and localized runtime error dialogs.

>startup_policy_demo: Demonstrates testing the three startup licensing policies (exit_process, fail_init, and retry_infinite) against a failing license key.

>linklib_sample: Demonstrates statically linking external vendor C libraries into typed Cython modules using native_settings.

>libc_override_sample: Demonstrates opting into the real platform C runtime (libc/MSVCRT) on Windows and Linux for external libraries that require standard C runtime functions.

Related Topics

Protecting Python Applications

Protecting Model Files for Python