Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Program Bundles & IP Protection

A program bundle (.st-bundle) is a self-contained archive that packages your compiled PLC program for deployment to a target device. Bundles support three modes that control what is included — from full debug information for development to fully stripped binaries for customer delivery.

Bundle Modes

ModeSource FilesDebug MapVariable NamesRemote DebugUse Case
developmentYesFullOriginal namesFullInternal development
releaseNoNoneStrippedRejectedCustomer delivery, production
release-debugNoObfuscatedv0, v1, …LimitedField diagnostics

Development Mode (Default)

st-cli bundle

Includes everything: source files, full debug map with variable names and source locations, unmodified bytecode. This is the mode you use during development — it enables the full VS Code debugging experience on the target.

Release Mode

st-cli bundle --release

Strips all proprietary information:

  • No source files in the bundle
  • No debug map (no debug.map file)
  • Variable names replaced with opaque indices (g0, g1, f0_v0, …) in the bytecode
  • Source maps cleared — no line number information
  • Type definition names stripped

The agent rejects debug connections for release bundles. The runtime skips debug hook setup for better performance. This is the mode for shipping to customers or deploying to production.

Release-Debug Mode

st-cli bundle --release-debug

A middle ground for field support:

  • No source files in the bundle
  • Obfuscated debug map — line maps present (for stack traces) but variable names replaced with v0, v1, …
  • Source maps kept in the bytecode (allows line-based breakpoints)
  • The agent allows debug connections but shows indices instead of variable names

Use this when you need to diagnose issues on deployed systems without exposing your source code.

Bundle Contents

Development Bundle

my-program.st-bundle (tar.gz)
├── manifest.yaml          # Name, version, mode, checksum, entry point
├── program.stc            # Compiled bytecode (JSON Module)
├── debug.map              # Full debug info (variable names, source maps)
├── plc-project.yaml       # Project configuration
├── _io_map.st             # Auto-generated I/O map
├── source/                # Original ST source files
│   ├── main.st
│   └── helpers.st
└── profiles/              # Device profiles (YAML)
    └── motor_drive.yaml

Release Bundle

my-program.st-bundle (tar.gz)
├── manifest.yaml          # Name, version, mode, checksum
├── program.stc            # Stripped bytecode (no var names, no source maps)
├── plc-project.yaml       # Runtime configuration
├── _io_map.st             # I/O map (field names from device profiles only)
└── profiles/              # Device profiles
    └── motor_drive.yaml

No source/ directory. No debug.map. The bytecode contains no human-readable identifiers from the original source.

Creating Bundles

# Development (default)
st-cli bundle

# Release (stripped, no source)
st-cli bundle --release

# Release with obfuscated debug info
st-cli bundle --release-debug

# Custom output path
st-cli bundle --release -o dist/my-program.st-bundle

Inspecting Bundles

st-cli bundle inspect my-program.st-bundle

Output:

Bundle: my-program.st-bundle
  Name:     BottleFillingLine
  Version:  1.0.0
  Mode:     release
  Compiled: 2026-04-10T14:30:00Z
  Compiler: 0.1.1
  Entry:    Main
  Checksum: 166a5025cf03ffbd (valid)
  Size:     2850 bytes

Files:
    257 B  manifest.yaml
    1.1 KB  plc-project.yaml
   86.8 KB  program.stc

The checksum is verified on extraction — the agent rejects tampered bundles.

What Gets Stripped

When you build a release bundle, the following transformations are applied to the compiled bytecode before packaging:

ElementDevelopmentReleaseRelease-Debug
POU names (Main, Helper)OriginalKept (runtime needs them)Kept
Local variable namesOriginalf0_v0, f0_v1, …f0_v0, f0_v1, …
Global variable namesOriginalg0, g1, …g0, g1, …
Type/struct namesOriginalt0, t1, …t0, t1, …
Struct field namesOriginalf0, f1, …f0, f1, …
Source maps (line info)PresentClearedPresent
InstructionsUnchangedUnchangedUnchanged

POU names (Main, Helper, etc.) are always preserved because the runtime needs them to find the entry point program. Everything else that could reveal proprietary logic is stripped or replaced with opaque indices.

Debug Capabilities by Bundle Mode

CapabilityDevelopmentRelease-DebugRelease
Set breakpoints (by line)YesYesNo
Step In / Over / OutYesYesNo
View source in editorYesNoNo
Variable names in localsYesIndices onlyNo
Stack traces with line numbersYesYesNo
Cycle stats & monitoringYesYesYes
Start / stop / restartYesYesYes