Compiled Build

Compile your app to a native binary — no plaintext Python ships. Portable by default; with --bind-machine, the license gate is inserted automatically into every module, so there's no single check to find and delete.

The convenience tier ships readable-once-decrypted Python bytecode. magiclock build goes further: it compiles your whole source tree to native machine code and encrypts any models you bundle — so no plaintext Python ships at all. By default the result is portable: it runs on any machine, with no account and no activation. Build with --bind-machine and it also auto-inserts a license-gate check into every module, weaves that gate directly into the compiled native code, and embeds your vendor's public key — so there's no single check an attacker could find and patch out.

Nothing to mark

There's no decorator and nothing to annotate — you don't change your source at all. On a --bind-machine (or --web-gate) build, magiclock build walks every .py module reachable from your entry point and inserts one gate check per module (checking the python_protect capability) before compiling. The entry module also gets a magiclock.bootstrap() call inserted automatically, so you don't need to add one yourself:

python
# your source, unchanged — no decorator, no bootstrap() call added by hand
def export_report(data: str) -> bytes:
    return render_pdf(data)
shell
magiclock build app.py --bind-machine   # every module in the tree — including this one — is gated automatically

A gate check runs once per module, the first time Python imports it in a process — this is a per-module checkpoint, not a per-function-call one (see Security Model for what this means for long-running processes and expiry/revocation).

Build it

shell
magiclock build app.py                        # -> dist/app.<...>.so   (portable by default — runs anywhere)
magiclock build app.py --bind-machine          # machine-locked build with the per-module license gate
magiclock build app.py --standalone            # a self-contained app directory instead
magiclock build app.py --no-compile            # scan + inject + embed only — skip compiling (no C toolchain needed)
magiclock build app.py --model weights.onnx    # also encrypt a model into the build (repeatable)
FlagDefaultMeaning
-o, --output-dirdistWhere the build output is written.
--source-rootthe entry fileProject root to scan/transform (use this if app.py isn't at your project's top level).
--moduleCompile to a single-file native extension (the default target).
--standaloneCompile to a self-contained app directory instead of a single module. Mutually exclusive with --module.
--no-compileoffScan, inject the gate, and embed resources, but skip the actual native compile — useful for inspecting the transform, or on a machine without a C toolchain.
--keep-tmpoffKeep the intermediate transform directory instead of cleaning it up.
--model PATHnoneEncrypt a model and bundle it into the build. Repeatable.
--model-lock-passphraseoffAdd a passphrase as a second factor on bundled models (requires at least one --model and --bind-machine).
--model-trial / --model-expires-in / --model-expires-atnoneExpiry for bundled models — same semantics as Encrypting Code's expiry flags, mutually exclusive with each other.
--bind-machineoffMachine-locked build: a license gate is compiled in and the binary runs only under this machine's activated vault; bundled models are encrypted to this machine's key. Without it, the build is portable (the default — see below).
--passphrase / --emit-keyoffFor portable builds, put a secret on the bundled models instead of the keyless default: --passphrase derives the model key from a passphrase; --emit-key generates and prints a random key.

What ships

The compiled .so/.pyd/standalone directory needs no MagicLock install, no account, and no network on the machine that runs it — decryption and the license check are both fully offline (see Security Model).

By default, the build is portable: the binary runs anywhere with no account and no vault, no license gate is compiled in, and bundled models are keyless by default — they self-decrypt through the compiled-in runtime on any machine. Protection then rests on the native compilation itself (plus, if you choose one, the model secret: --emit-key prints a key to distribute, --passphrase derives one, and the binary reads them via magiclock.open_model(path, key=...) or passphrase=...). Add --web-gate to keep a remote kill switch after shipping (see Cloud Control) — a portable build stays freely distributable and remotely stoppable.

Build with --bind-machine to instead lock the binary to the machine that built it: the build bakes a signed statement naming this machine's seat into the binary, and the runtime gate refuses to run under any other machine's vault — even another activated one. Bundled models are additionally encrypted to this machine's key. If you need the same machine-locked program on a second workstation or a deployment server, activate that machine under your account too and build there.

What's next

  • Security Model — why the compiled tier resists the "just find and patch the check" bypass that the convenience tier can't fully rule out.
  • Account & Activation — activating additional build/deployment machines.