Référence de l'API Python

bootstrap(), open_model(), la barrière automatique du niveau compilé, et les exceptions que votre code peut avoir à gérer.

L'API d'exécution que vous utilisez tient en deux fonctions de premier niveau — tout le reste (activation, licence, la barrière elle-même) se passe derrière elles. Il n'y a aucun décorateur à importer ni à appliquer : magiclock build insère la vérification de la barrière de licence dans chaque module compilé à votre place (voir Build compilé).

magiclock.bootstrap()

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

Met en place la barrière de licence à partir de l'activation de cette machine, pour le processus courant. Dans une sortie de magiclock build, cet appel est inséré automatiquement dans le module d'entrée — vous n'avez à l'appeler vous-même que dans un script ordinaire (non compilé), avant votre premier appel à open_model().

  • vault_dir — remplace l'emplacement de l'état d'activation local. Vous n'en aurez normalement pas besoin.
  • app_version — vérifiée contre tout max_app_version fixé par votre licence ; utile si vous verrouillez vos versions.
  • Retourne l'objet runtime (rarement nécessaire directement — la plupart du code appelle bootstrap() uniquement pour son effet de bord).
  • Lève une exception si cette machine n'a pas encore été activée, ou si une activation est présente mais invalide pour cette machine (voir la section « Erreurs » ci-dessous).

magiclock.open_model()

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

Déchiffre une enveloppe .enc produite par protect-model et retourne les octets en clair — en mémoire uniquement, jamais écrits sur disque.

  • path — chemin du fichier .enc.
  • passphrase / key — pour un artefact à deux facteurs (--lock-passphrase) ou portable (--no-bind-machine) ; à omettre pour un artefact simplement verrouillé machine.
  • Nécessite que bootstrap() ait été exécuté au préalable pour les artefacts verrouillés machine.
python
import magiclock

magiclock.bootstrap()
weights = magiclock.open_model("model.onnx.enc")

La barrière du niveau compilé

Il n'y a ni fonction ni décorateur à appeler — magiclock build parcourt votre arborescence source et insère une vérification de barrière (la capacité python_protect) dans chaque module, avant compilation :

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

La vérification s'exécute une fois par module, à sa première importation dans un processus — pas une fois par appel. Comme elle est tissée dans chaque module compilé plutôt que placée derrière un décorateur partagé et supprimable, un attaquant doit trouver et neutraliser chaque module individuellement. L'échec d'un module non-entrée se propage comme une exception normale (il peut être importé comme bibliothèque) ; l'échec du module d'entrée affiche un message propre et quitte, puisque rien d'autre ne peut tourner sans lui.

magiclock.revalidate()

python
magiclock.revalidate() -> None

Réexécute les vérifications de protection maintenant. Les barrières s'exécutent une fois par module à l'import, ce qui est le bon coût pour un script mais laisse un processus de longue durée (un serveur, un worker, un démon) faire confiance à une décision prise au démarrage. Appelez cette fonction à la période qui vous convient — un tick horaire est typique :

python
import magiclock

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

Elle réexécute la barrière de licence locale complète et, pour tout artefact contrôlé par le cloud (--web-gate) chargé dans ce processus, force une approbation signée fraîche auprès du serveur. Cette seconde partie compte : c'est elle qui fait qu'une désactivation depuis le portail prend effet rapidement sur un service de longue durée, au lieu d'attendre son prochain point de contrôle naturel. Si vous livrez des artefacts contrôlés par le cloud dans des processus de longue durée, appeler cette fonction fait la différence entre « s'arrête en quelques secondes » et « s'arrête un jour ».

Lève les mêmes erreurs typées que les barrières au moment de l'import, et retourne None quand tout est encore en règle.

magiclock.revalidate_every()

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

Exécute revalidate() sur un thread démon en arrière-plan, indéfiniment. Appelez-la une fois, près de bootstrap() :

python
import magiclock

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

Si une vérification échoue, le processus est terminé par défaut. Un thread d'arrière-plan qui avalerait l'échec laisserait votre service tourner sur une licence qui ne tient plus, et personne ne lit la traceback d'un thread démon. Passez on_error pour prendre la main — videz les connexions, basculez votre sonde de disponibilité, puis quittez vous-même :

python
magiclock.revalidate_every(3600, on_error=lambda exc: my_graceful_shutdown(exc))

Le thread retourné dispose d'un stop() pour un arrêt propre. Si votre on_error lève lui-même une exception, le processus est terminé quand même — un gestionnaire cassé ne doit pas devenir un moyen de continuer à tourner.

Erreurs que vous pouvez avoir à gérer

bootstrap() et open_model() lèvent des exceptions pour tout, de « pas encore activé » à « cet artefact a expiré ». La plupart des intégrations attrapent largement et branchent sur le nom de la classe de l'exception :

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
ExceptionLevée quand
NotActivatedErrorAucune activation trouvée sur cette machine — lancez magiclock activate (ou laissez la CLI auto-activer au premier chiffrement).
VaultLockedErrorL'état d'activation local ne peut pas être ouvert ici — mauvaise machine, ou fichier de coffre déplacé/altéré. Ces deux cas sont indiscernables à dessein ; voir Modèle de sécurité.
TrialExpiredErrorUn artefact --trial, --expires-in ou --expires-at a dépassé sa fenêtre de déchiffrement.
SubscriptionExpiredErrorVotre offre est échue. Cela ne bloque que le nouveau chiffrement — jamais levée sur le chemin du déchiffrement.
SubscriptionInvalidErrorLe justificatif d'abonnement est absent là où il est requis, malformé, ou échoue à la vérification de signature.
PortableFormatErrorUne enveloppe portable (--no-bind-machine) est malformée ou provient d'une version non prise en charge.
PortableKeyErrorLa phrase secrète ou la clé passée à open_model()/run --key ne correspond pas à un artefact portable.
DebuggerDetectedErrorUn débogueur était attaché à une frontière de déchiffrement.
RuntimeNotInitializedError (from magiclock_host.errors import RuntimeNotInitializedError)La vérification d'un module verrouillé s'est exécutée avant que bootstrap() ne s'exécute dans ce processus.

Note de compatibilité

Les enveloppes .pya/.enc embarquent la version majeure/mineure de CPython qui les a produites. Un artefact chiffré sous 3.12 ne se chargera pas sous 3.13 — relancez protect/protect-model après avoir mis à niveau votre interpréteur, plutôt que de livrer un même artefact à travers plusieurs versions de Python.