Python API 參考
bootstrap()、open_model()、編譯強檔下自動生效的閘門,以及你的程式碼可能需要處理的例外。
你需要撰寫程式碼對接的執行期 API,其實只有兩個頂層函式——其餘的一切(啟用、授權、閘門本身)都發生在它們背後。沒有裝飾器需要匯入或使用:magiclock build 會自動把授權閘門檢查插入每一個編譯後的模組(見編譯強檔)。
magiclock.bootstrap()
magiclock.bootstrap(vault_dir=None, *, app_version="1.0.0") -> MagicLockRuntime針對這台機器的啟用狀態,為目前的處理程序把授權閘門立起來。在 magiclock build 的產物裡,這個呼叫會在進入點模組自動插入;只有一般(未編譯)指令碼才需要你自己在第一次呼叫 open_model() 之前手動呼叫它。
vault_dir——覆寫本地啟用狀態的存放位置,通常不需要用到。app_version——會與你授權設定的max_app_version做比對,若你採取版本鎖定的發布策略會用到。- 回傳執行期物件(很少需要直接使用——大多數程式碼只是為了它的副作用而呼叫
bootstrap())。 - 若這台機器尚未啟用,或啟用狀態存在但對這台機器無效,會拋出例外(見下方「你可能需要處理的例外」一節)。
magiclock.open_model()
magiclock.open_model(path, *, vault_dir=None, passphrase=None, key=None) -> bytes解密由 protect-model 產生的 .enc 信封,回傳明文位元組——只存在於記憶體中,永遠不會寫入磁碟。
path——.enc檔案的路徑。passphrase/key——用於二因子(--lock-passphrase)或便攜(--no-bind-machine)產物;一般機器鎖定的產物則不需要傳入。- 機器鎖定的產物要求必須先呼叫過
bootstrap()。
import magiclock
magiclock.bootstrap()
weights = magiclock.open_model("model.onnx.enc")編譯強檔的閘門
沒有函式或裝飾器需要呼叫——magiclock build 會走訪你的原始碼樹,在編譯前為每一個模組插入一次閘門檢查(核對 python_protect 這項能力):
# 你的原始碼,你自己完全不用改:
def export_report(data: str) -> bytes:
...
# `magiclock build` 實際編譯出來的內容,概念上等同於:
current_runtime().require_feature("python_protect")
def export_report(data: str) -> bytes:
...這個檢查每個模組只會執行一次,在處理程序第一次 import 它的時候執行——不是每次呼叫都執行一次。因為它被編織進了每一個編譯後的模組,而不是集中在一處可刪除的共用裝飾器背後,攻擊者必須逐一模組去找、去 patch。非進入點模組的檢查失敗會照常向上拋出例外(它有可能被當作程式庫匯入);進入點模組檢查失敗則會印出一行乾淨的提示後結束,因為少了它其他任何東西都跑不起來。
你可能需要處理的例外
bootstrap() 與 open_model() 會針對各種狀況拋出例外,從「尚未啟用」到「這個產物已經過期」都有。大多數整合方式是廣泛地攔截例外,再依類別名稱分支處理:
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| 例外 | 觸發時機 |
|---|---|
NotActivatedError | 這台機器上找不到啟用狀態——執行 magiclock activate(或讓 CLI 在首次加密時自動啟用)。 |
VaultLockedError | 本地啟用狀態在這台機器上無法開啟——可能是機器不對,也可能是保管庫檔案被移動或遭竄改。這兩種情況在設計上刻意做成無法區分,見安全模型。 |
TrialExpiredError | --trial、--expires-in 或 --expires-at 產物已超過它的解密時限。 |
SubscriptionExpiredError | 你的方案已經過期。這只會阻擋新的加密——解密路徑永遠不會拋出這個例外。 |
SubscriptionInvalidError | 訂閱憑證在需要的地方缺失、格式錯誤,或簽章驗證失敗。 |
PortableFormatError | 便攜(--no-bind-machine)信封格式不正確,或版本不受支援。 |
PortableKeyError | 傳給 open_model()/run --key 的密碼片語或金鑰,與便攜產物不相符。 |
DebuggerDetectedError | 在解密邊界偵測到已附加的除錯器。 |
RuntimeNotInitializedError(from magiclock_host.errors import RuntimeNotInitializedError) | 在這個處理程序中,bootstrap() 尚未執行過,某個受閘控模組的檢查就先執行了。 |
相容性說明
.pya/.enc 信封內嵌了產生它們的 CPython 主版本號與次版本號。在 3.12 下加密的產物無法在 3.13 下載入——升級直譯器後請重新執行一次 protect/protect-model,而不要讓同一個產物跨 Python 版本使用。