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()
magiclock.bootstrap(vault_dir=None, *, app_version="1.0.0") -> MagicLockRuntimeMet 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 toutmax_app_versionfixé 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()
magiclock.open_model(path, *, vault_dir=None, passphrase=None, key=None) -> bytesDé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.
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 :
# 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()
magiclock.revalidate() -> NoneRé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 :
import magiclock
def periodic_check():
magiclock.revalidate() # raises if the license or approval no longer holdsElle 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()
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() :
import magiclock
magiclock.bootstrap()
magiclock.revalidate_every(3600) # re-check hourlySi 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 :
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 :
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 | Levée quand |
|---|---|
NotActivatedError | Aucune activation trouvée sur cette machine — lancez magiclock activate (ou laissez la CLI auto-activer au premier chiffrement). |
VaultLockedError | L'é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é. |
TrialExpiredError | Un artefact --trial, --expires-in ou --expires-at a dépassé sa fenêtre de déchiffrement. |
SubscriptionExpiredError | Votre offre est échue. Cela ne bloque que le nouveau chiffrement — jamais levée sur le chemin du déchiffrement. |
SubscriptionInvalidError | Le justificatif d'abonnement est absent là où il est requis, malformé, ou échoue à la vérification de signature. |
PortableFormatError | Une enveloppe portable (--no-bind-machine) est malformée ou provient d'une version non prise en charge. |
PortableKeyError | La phrase secrète ou la clé passée à open_model()/run --key ne correspond pas à un artefact portable. |
DebuggerDetectedError | Un 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.