Python API 參考

bootstrap()、open_model()、編譯強檔下自動生效的閘門,以及你的程式碼可能需要處理的例外。

你需要撰寫程式碼對接的執行期 API,其實只有兩個頂層函式——其餘的一切(啟用、授權、閘門本身)都發生在它們背後。沒有裝飾器需要匯入或使用:magiclock build 會自動把授權閘門檢查插入每一個編譯後的模組(見編譯強檔)。

magiclock.bootstrap()

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

python
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()
python
import magiclock

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

編譯強檔的閘門

沒有函式或裝飾器需要呼叫——magiclock build 會走訪你的原始碼樹,在編譯前為每一個模組插入一次閘門檢查(核對 python_protect 這項能力):

python
# 你的原始碼,你自己完全不用改:
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() 會針對各種狀況拋出例外,從「尚未啟用」到「這個產物已經過期」都有。大多數整合方式是廣泛地攔截例外,再依類別名稱分支處理:

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
例外觸發時機
NotActivatedError這台機器上找不到啟用狀態——執行 magiclock activate(或讓 CLI 在首次加密時自動啟用)。
VaultLockedError本地啟用狀態在這台機器上無法開啟——可能是機器不對,也可能是保管庫檔案被移動或遭竄改。這兩種情況在設計上刻意做成無法區分,見安全模型
TrialExpiredError--trial--expires-in--expires-at 產物已超過它的解密時限。
SubscriptionExpiredError你的方案已經過期。這只會阻擋新的加密——解密路徑永遠不會拋出這個例外。
SubscriptionInvalidError訂閱憑證在需要的地方缺失、格式錯誤,或簽章驗證失敗。
PortableFormatError便攜(--no-bind-machine)信封格式不正確,或版本不受支援。
PortableKeyError傳給 open_model()/run --key 的密碼片語或金鑰,與便攜產物不相符。
DebuggerDetectedError在解密邊界偵測到已附加的除錯器。
RuntimeNotInitializedErrorfrom magiclock_host.errors import RuntimeNotInitializedError在這個處理程序中,bootstrap() 尚未執行過,某個受閘控模組的檢查就先執行了。

相容性說明

.pya/.enc 信封內嵌了產生它們的 CPython 主版本號與次版本號。在 3.12 下加密的產物無法在 3.13 下載入——升級直譯器後請重新執行一次 protect/protect-model,而不要讓同一個產物跨 Python 版本使用。