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()

python
magiclock.bootstrap(vault_dir=None, *, app_version="1.0.0") -> MagicLockRuntime

Brings 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 any max_app_version your 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()

python
magiclock.open_model(path, *, vault_dir=None, passphrase=None, key=None) -> bytes

Decrypts a .enc envelope produced by protect-model and returns the plaintext bytes — in memory only, never written to disk.

  • path — path to the .enc file.
  • 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.
python
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):

python
# 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()

python
magiclock.revalidate() -> None

Re-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:

python
import magiclock

def periodic_check():
    magiclock.revalidate()   # raises if the license or approval no longer holds

It 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()

python
magiclock.revalidate_every(seconds, *, on_error=None)

Runs revalidate() on a background daemon thread, forever. Call it once, near bootstrap():

python
import magiclock

magiclock.bootstrap()
magiclock.revalidate_every(3600)     # re-check hourly

If 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:

python
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:

python
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
ExceptionRaised when
NotActivatedErrorNo activation found on this machine — run magiclock activate (or let the CLI auto-activate on first encrypt).
VaultLockedErrorThe 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.
TrialExpiredErrorA --trial, --expires-in, or --expires-at artifact is past its decryption window.
SubscriptionExpiredErrorYour plan has lapsed. This only blocks new encryption — it's never raised on the decrypt path.
SubscriptionInvalidErrorThe subscription credential is missing where required, malformed, or fails signature verification.
PortableFormatErrorA portable envelope (the default, or --passphrase/--emit-key) is malformed or from an unsupported version.
PortableKeyErrorThe passphrase or key passed to open_model()/run --key doesn't match a portable artifact.
DebuggerDetectedErrorA 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.