這篇是 2026 年 9 月 2 日補寫的(排程斷了幾天,回頭把日期補齊)。文中對 Model Signing 的描述以撰寫當日的官方規格為準。

你機器上正在跑的東西,有幾個是驗過簽章才裝的?

我的 ~/.claude/skills/ 底下躺著幾十個 skill,plugin 又是一批,MCP server 有好幾個是 npx 直接拉起來的。這些全都是別人寫的程式碼,全都在我的帳號權限下執行,而我裝它們的時候,一次簽章都沒驗過。裝的當下完全沒感覺,回頭想才發現這條線一直是敞開的。

同一件事在模型權重上更誇張。pip install 背後有 PyPI 的簽章機制,容器映像檔有 cosign 可以簽、可以驗,這兩層大家吵了好幾年終於補起來了。然後你從 Hugging Face git clone 一包幾 GB 的權重下來,直接餵給 from_pretrained(),中間沒有任何人簽過名。整條供應鏈裡最重、最難用肉眼檢查、最有機會藏東西的那一塊,反而是唯一沒被簽的。

OpenSSF 底下的 sigstore 有個專案在補這個洞,叫 model-transparency。這篇要談的不是它怎麼裝,是一個更前面的問題:這東西該不該現在就進你的流程。答案要看你站在哪一邊,我後面會講清楚。

三層包裹,由內而外

規格文件裡,最內層是 in-toto Statement。這層可以當成一張報關單:裡面每一個 subject 是一個 ResourceDescriptor,內容就是 path:hash,一個檔案一筆。注意這句話的重量,簽的是目錄裡每個檔案的雜湊值,不是整包壓縮檔的雜湊值。差別在驗證的時候會浮現:驗的那一方要把模型的所有檔案 hash 重算一遍,再跟清單上簽過的比對。官方 README 的講法是「firstly verifies that the signature is valid and secondly compute the model’s file hashes again to compare against the signed ones」。少一個檔案、多一個檔案、換掉其中一個 .safetensors,都對不起來。

中間那層是 DSSE Envelope。為什麼要多包一層信封?規格文件講得很白:「in order to avoid having two files, one for the manifest, and one for the signature, the use of a DSSE Envelope was chosen to combine the manifest and signature into a single file」。就是不想讓清單跟簽章變成兩個檔在世界上流浪,一個掉了另一個就沒意義。

最外層是 Sigstore Bundle,負責把憑證、簽章、透明日誌的憑據裝在一起交給你。

清單、信封、戳記。最容易被跳過的是第三層,偏偏它價值最高,等一下專門講。

規格文件自己承認的那個妥協

複雜度一定來自某個妥協,找出那個妥協是什麼,通常比背下規格有用。

這份規格裡最誠實的一段,是它解釋為什麼 payload type 選 in-toto:「The main reason currently for choosing In-Toto as the payload type of the DSSE Envelope is simply because it is what is currently supported by the sigstore-python library.」翻成白話就是「因為我們用的函式庫目前只支援這個」。文件底下還掛了 sigstore-python 的 issue #982,追蹤 binary payload 什麼時候能支援。

規格文件肯把「這個選擇是被工具限制的」寫出來,我覺得比寫一串架構美學的理由可信得多。這也順便告訴你,這層結構不是石頭刻的,未來換得掉。

順帶一提,predicate type 目前是 https://model_signing/signature/v1.0,而 predicates 這個字典現在基本上是空著佔位的,README 說未來想拿來放 model card 資訊(「The idea is to use the predicates to store (and therefor sign) model card information in the future」)。也就是說,這個格式從一開始就打算連「這個模型是拿什麼資料訓練的、能力邊界在哪」都一起簽進去。目前還沒做到。

Keyless 那一層,才是真正值錢的地方

簽章這件事,大部分人卡在同一個地方:金鑰放哪、誰保管、離職了怎麼辦。

model-transparency 預設走的是 Sigstore 的 keyless 流程。你跑 model_signing sign bert-base-uncased,它會開一個 OIDC 登入流程,你用既有的身分(GitHub、Google 之類)登入,拿到一張短效憑證,簽完就過期。你不需要保管任何一把長期私鑰。當然它也支援傳統的 key、certificate、PKCS #11,企業有既有 KMS 的話走那條也行。

keyless 只是省事,下一步才是重點:簽章事件會寫進一份 append-only 的透明日誌,驗證方可以查到 proof of inclusion。這代表什麼?代表如果有人偷用你的身分簽了一包模型,那個動作會留在一份你查得到的公開紀錄裡。sigstore 那邊還有個 rekor-monitor 可以跑在 GitHub Actions 上,幫你盯著自己的身分被誰用過。

簽章防不了身分被盜,透明日誌讓身分被盜這件事沒辦法安靜地發生。這兩件事常常被混為一談,但它們解的是不同的問題。

什麼時候該在意,什麼時候是過度投資

判準其實很好切,看你是分發的人還是下載的人。你如果會對外發東西,模型、adapter、skill 包、內部同事會直接下載執行的任何一坨檔案,那這三層結構現在就值得你花一個下午搞懂。不見得要導入這個 CLI,重點是那個結構本身。你的分發管道有沒有一份逐檔案的清單?清單跟簽章有沒有綁在同一個檔裡?有沒有任何第三方紀錄可以讓別人查證「這個版本確實是你在那個時間點發的」。三個問題問完,通常會發現自己缺的是第三層。

反過來,你如果只是下載模型自己跑、自己實驗、不對外分發,我不建議現在就把這套東西塞進流程。理由不是它不好,是生態還沒接上。

看一下這個 repo 的現況:Apache-2.0 授權,2023 年 8 月建立,244 顆星,最新一版 release 是 v1.1.1,2025 年 10 月 10 日發布,到寫這篇的時候快滿十一個月沒有新版本。repo 的 pushed_at 顯示 8 月 31 日還有動靜,但最近三筆 commit 全部是 dependabot 的依賴升級。專案沒有 archived、也沒宣告停更,狀態就是穩定但沒在快速迭代。

更關鍵的是另一件事:我沒有查證任何模型託管平台實際上有沒有採用這個格式。所以請不要從這篇文章得到「現在下載模型都會驗簽」的印象,那不是我查得出來的結論。當上游根本沒有人在簽的時候,你在下游裝一套驗證工具,驗不到東西,只會多一個要維護的依賴。

還有一種情況它會變成壞選擇:你的模型檔案數量很大、更新很頻繁,而你把「重算全部檔案 hash」放進 CI 的每一次跑。逐檔案簽章的代價就在驗證端,這是設計上的取捨,不是實作沒優化。

這篇的邊界

上面所有關於格式、結構、CLI 子指令的敘述,都出自 docs/model_signing_format.md 跟 README 的原文,引號裡的英文是逐字照抄。版本、授權、commit 狀態出自 GitHub API 的查詢結果。

我沒有 pip install model-signing,沒有簽過任何一個模型,沒有跑過那個 OIDC 流程,也沒有看過一份真實產生出來的簽章檔長什麼樣。這篇是規格閱讀筆記,不是使用心得。任何跟「實際跑起來手感如何」有關的問題,我答不了。

上游的三份規格,in-toto Attestation Framework、DSSE、Sigstore Bundle 的 protobuf 定義,我只從這個專案的文件知道它們各自扮演什麼角色,沒有讀內文。要談到欄位層級的細節,得先補讀那三份。

所以呢

給一個不騎牆的建議:分發者現在就去看那三層結構,下載者先不用裝。

分發者要看的不是這個工具,是那個心智模型。清單、信封、透明日誌,這三層對任何一種 artifact 分發都成立,模型只是其中一種。你手上那個給同事用的內部套件、那個放在共用磁碟的 skill 包、那個 curl | bash 的安裝腳本,拿這三層去問一遍,缺哪一層通常一目瞭然。

至於一開始那個問題,我機器上有多少東西是沒驗簽就在跑的,答案是全部。這篇沒有解決它,Claude Code 生態目前也沒有等價的機制。但至少現在知道,這個問題已經有人定義完了,而且定義得比我原本以為的清楚。


參考來源