Python API Reference
bootstrap(), open_model(), the automatic compiled-tier gate, and the exceptions your code may need to handle.
The runtime API you write against is two top-level functions — everything else (activation, licensing, the gate itself) happens behind them. There is no decorator to import or apply: magiclock build inserts the license-gate check into every compiled module for you (see Compiled Build).
magiclock.bootstrap()
magiclock.bootstrap(vault_dir=None, *, app_version="1.0.0") -> MagicLockRuntimeBrings the license gate up against this machine's activation, for the current process. In a magiclock build output this call is inserted automatically at the entry module — you only need to call it yourself in a plain (non-compiled) script, before your first open_model() call.
vault_dir— override where the local activation state lives. You won't normally need this.app_version— checked against anymax_app_versionyour license sets; matters if you version-lock releases.- Returns the runtime object (rarely needed directly — most code just calls
bootstrap()for its side effect). - Raises if this machine hasn't been activated yet, or if activation is present but invalid for this machine (see Errors below).
magiclock.open_model()
magiclock.open_model(path, *, vault_dir=None, passphrase=None, key=None) -> bytesDecrypts a .enc envelope produced by protect-model and returns the plaintext bytes — in memory only, never written to disk.
path— path to the.encfile.passphrase/key— for a passphrase-portable (--passphrase), key-portable (--emit-key), or two-factor (--bind-machine --passphrase) artifact; omit for a keyless portable (the default) or plain machine-locked one.- Requires
bootstrap()to have run first for machine-locked (--bind-machine) artifacts; portable envelopes decrypt without it.
import magiclock
magiclock.bootstrap()
weights = magiclock.open_model("model.onnx.enc")The compiled-tier gate
There's no function or decorator to call — on a machine-locked (--bind-machine) or cloud-controlled (--web-gate) build, magiclock build walks your source tree and inserts one gate check (the python_protect capability) into every module, before compiling (a default portable build compiles no gate in — its protection is the native compilation itself):
# your source, unchanged by you:
def export_report(data: str) -> bytes:
...
# what `magiclock build` compiles, conceptually:
current_runtime().require_feature("python_protect")
def export_report(data: str) -> bytes:
...The check runs once per module, the first time it's imported in a process — not once per call. Because it's woven into every compiled module rather than living behind one shared, deletable decorator, an attacker has to find and patch each module individually. A non-entry module's failure propagates as a normal exception (it may be imported as a library); the entry module's failure prints a clean message and exits, since nothing else can run without it.
magiclock.revalidate()
magiclock.revalidate() -> NoneRe-runs the protection checks now. The gates run once per module at import, which is the right cost for a script but leaves a long-lived process (a server, a worker, a daemon) trusting a decision it made at startup. Call this on whatever period suits you — an hourly tick is typical:
import magiclock
def periodic_check():
magiclock.revalidate() # raises if the license or approval no longer holdsIt re-runs the full local license gate, and for any cloud-controlled (--web-gate) artifact loaded in this process it forces a fresh signed approval from the server. That second part matters: it is what makes a portal disable take effect on a long-running service promptly instead of at its next natural checkpoint. If you ship cloud-controlled artifacts into long-lived processes, calling this is the difference between "stops in seconds" and "stops eventually".
Raises the same typed errors the import-time gates raise, and returns None when everything still checks out.
magiclock.revalidate_every()
magiclock.revalidate_every(seconds, *, on_error=None)Runs revalidate() on a background daemon thread, forever. Call it once, near bootstrap():
import magiclock
magiclock.bootstrap()
magiclock.revalidate_every(3600) # re-check hourlyIf a check fails, the process is terminated by default. A background thread that swallowed the failure would leave your service running on a licence that no longer holds, and nobody reads a daemon thread's traceback. Pass on_error to take over — drain connections, flip your readiness probe, then exit yourself:
magiclock.revalidate_every(3600, on_error=lambda exc: my_graceful_shutdown(exc))The returned thread has a stop() for a clean shutdown. If your on_error itself raises, the process is terminated anyway — a broken handler must not become a way to keep running.
Errors you may need to handle
bootstrap() and open_model() raise on anything from "not activated yet" to "this artifact expired." Most integrations catch broadly and branch on the exception's class name:
try:
magiclock.bootstrap()
data = magiclock.open_model("model.onnx.enc")
except Exception as exc:
name = type(exc).__name__
if name == "NotActivatedError":
... # this machine hasn't been activated — run `magiclock activate`
elif name == "TrialExpiredError":
... # the artifact's expiry window has passed
else:
raise| Exception | Raised when |
|---|---|
NotActivatedError | No activation found on this machine — run magiclock activate (or let the CLI auto-activate on first encrypt). |
VaultLockedError | The local activation state can't be opened here — wrong machine, or the vault file was moved/tampered with. These two cases are indistinguishable by design; see Security Model. |
TrialExpiredError | A --trial, --expires-in, or --expires-at artifact is past its decryption window. |
SubscriptionExpiredError | Your plan has lapsed. This only blocks new encryption — it's never raised on the decrypt path. |
SubscriptionInvalidError | The subscription credential is missing where required, malformed, or fails signature verification. |
PortableFormatError | A portable envelope (the default, or --passphrase/--emit-key) is malformed or from an unsupported version. |
PortableKeyError | The passphrase or key passed to open_model()/run --key doesn't match a portable artifact. |
DebuggerDetectedError | A debugger was attached at a decrypt boundary. |
RuntimeNotInitializedError (from magiclock_host.errors import RuntimeNotInitializedError) | A gated module's check ran before bootstrap() ran in this process. |
Compatibility note
.pya/.enc envelopes embed the CPython major/minor version that produced them. An artifact encrypted under 3.12 won't load under 3.13 — re-run protect/protect-model after upgrading your interpreter, rather than shipping one artifact across Python versions.