2026 獨佔遠端 Mac 上的 iOS/macOS 公證 notarytool、stapler 與無人值守 CI 的鑰匙圈與重試基線

平台工程已把 Archive 跑綠,卻在 公證(Notarization)stapler 階段被鑰匙圈彈窗、輪詢逾時、同一台遠端 Mac 上多 Job 爭用暫存目錄拖進夜班。本文寫給熟悉 VPS 維運、要把 macOS 當成獨佔建置平面的團隊:先用七條清單拆穿無人值守公證最常見的隱性假設,再用「分發管道 × 技術動作」對照表收斂是否必須 staple,接著給出六步可交接 Runbook(API Key、notarytool submit/store、日誌留存、重試與並發邊界),並鏈到站內 Fastlane 與 CI可重現建置與鑰匙圈 的分工閱讀。

01

動手之前:七個會讓「遠端 Mac 上跑公證」在評審會上翻車的隱性假設

Apple 的公證鏈路在文件層面很清晰,但落到長期在線的 CI 使用者共用 NVMe上時,失敗往往表現為偶發、難重現、日誌分散。以下七條用於把風險從「感覺能腳本化」推進到「能寫進變更單」。

  1. 01

    把公證當成 Archive 的附屬品:未單獨為 notarytool 分配結束碼處理與成品留存時,排障會退化成「誰 last green 誰負責」;應與發佈流水線的稽核欄位對齊。

  2. 02

    混用個人 Apple ID 與 CI API Key:工作階段型憑證在無人值守環境不可控;2026 年應以 App Store Connect API Key 為預設,並與 Fastlane 無頭發佈 的金鑰合約一致。

  3. 03

    忽略 stapler 的分發語義:把 staple 寫進所有流水線會增加時間與失敗面;應對「直接下載分發/企業內部入口/僅 TestFlight」分支化,見下一節對照表。

  4. 04

    多 Job 共用預設暫存目錄:/var/folders 與自訂 zip 工作區若衝突,會出現上傳損毀或雜湊不一致;需要像 DerivedData 一樣依 pipeline 分桶

  5. 05

    不做 notarytool 輪詢退避:Apple 側佇列波動時,線性重試會把同一提交打爆;應使用指數退避與最大牆鐘預算,並把 submission id 記入工單。

  6. 06

    鑰匙圈與 TCC 仍按「桌面使用者」設定:無人值守建置應使用專用登入工作階段或明確的 CI 鑰匙圈分區,並與 可重現建置 的 Keychain 合約一起評審。

  7. 07

    沒有「公證失敗樣本庫」:團隊只在綠線時看日誌,遇到 Invalid 或 Hardened Runtime 問題時缺少對照截圖與版本三元組;應在知識庫固定採集範本。

這些假設的共同根因,是把遠端 Mac 當成「能跑 Xcode 的 Linux」,而不是帶公證狀態機與鑰匙圈邊界的獨佔節點。與 企業建置資源池 篇聯動:當多應用共用機群時,公證並發與簽章材料隔離必須寫進平台 SLA,而不是交給單一業務儲存庫「自己看著辦」。

與純 xcodebuild 相比,公證階段更強調可觀測性與冪等:同一二進位重複提交、同一 request UUID 的冪等語義,以及失敗後在工單系統裡的「可回放命令」。把三件事寫進 Runbook,才能把遠端 Mac 從「能編」升級到「能簽、能證、能稽核」。

若同時跑夜間 Archive 與白天 PR,建議把公證 Job 與編譯 Job 的資源組隔離:公證往往受網路與 Apple 側佇列影響,和 CPU 密集編譯混排會放大尾延遲;此舉與 GitLab resource_group 或 Actions 並發上限的治理思路一致,只是資源名應改成「notarization slot」這類可讀模型。

最後,遠端供應商若提供固定出口 IP 或區域選擇,應把公證鏈路的 RTT 與 TLS 攔截策略納入驗收:企業代理對 Apple 端點的中間人解密是「綠本機、紅公證」的經典根因之一。

維運成熟度也包含將 notarytool CLI 與 Xcode 一併版本化:Apple 會隨 Xcode 與命令列工具更新行為;在內部變更日誌釘選行為,可避免映像刷新後團隊集體踩預設參數變更。API Key 應排程輪替並註冊 Issuer 與團隊對應關係,讓事件應變不必猜測主機上啟用哪一支 p8。

最後,把公證遙測接到與編譯耗時相同的儀表板:追蹤提交耗時、輪詢次數、staple 耗時與磁碟可用空間百分比,當主管詢問「為何 CI 變慢」時才有資料可答,而不是憑印象調機。

02

分發管道 × 技術動作:什麼時候必須 notarytool、什麼時候必須 staple

沒有一張表能在所有組織裡直接拍板,但評審時可以用「使用者如何拿到封包」為主軸,把技術動作收斂成可測試的驗收項。

維度直接下載 .dmg/.zip企業內部分發入口TestFlight / App Store
公證提交通常需要:Gatekeeper 預設路徑要求公證憑證可驗證需要:與 MDM/入口驗簽策略對齊由 Apple 託管分發鏈主導;本機 stapler 需求與直接下載不同
stapler staple強烈建議:離線安裝時憑證隨封包走視入口是否二次重打包;重打包後需重新 staple 或改分發策略一般不等同於「本機構建完必 staple」;以發佈通道為準
失敗影響面使用者側彈窗與安裝攔截;支援成本高合規與稽核欄位;可能觸發資安事件複盤多體現為上傳/處理佇列;與 CI 公證並不同構
遠端 Mac 側效益獨佔磁碟與穩定出口;便於保留 notary zip 與日誌與內網成品倉相鄰部署可降低傳輸時間Fastlane 流水線銜接;公證與上傳角色拆分
典型誤區以為「公證通過」等於「使用者雙擊一定綠」;忽略 staple 時機入口防毒改寫封包導致憑證失效把桌面端 notarytool 經驗硬套到 App Store Connect 處理延遲上

「像買 VPS 一樣租 Mac」在公證語境裡,買的是可重複的提交平面:固定使用者、可預期的磁碟水位,以及能把 submission id 與建置指紋綁進工單的狀態機。

對照 notarytool 與已棄用路徑時,評審應明確維護窗口:團隊是否仍殘留依賴圖形工作階段的舊腳本;是否所有呼叫端都已切到 JSON 輸出與結構化日誌。把「切換完成」定義為 CI 範本與內部腳手架全部更新,而不是某個儲存庫局部 green。

若你在多地區有機群,公證上傳的地理就近不如編譯那麼敏感,但仍建議把「出口路徑與代理策略」寫入每台節點的 Runbook,避免「東京編、矽谷證」這類路徑在合規上踩雷。

資安審查附件可附一頁決策紀錄:選定管道、是否 staple、誰負責 API Key 輪替,以及乾淨虛擬機上驗證憑證的指令;比單靠截圖更耐時間考驗。

03

六步在獨佔遠端 Mac 上跑通 notarytool 並接到夜間流水線

下列順序強調「先身分與金鑰,再成品與提交,最後才放開並發」:與 可重現建置 的指紋採集腳本對齊,避免「能 submit 卻不能複盤」。

  1. 01

    建立專用 CI 使用者與登入工作階段基線:禁止與個人 Apple ID 混用;確認螢幕鎖定策略不會中斷長時間輪詢。

  2. 02

    安裝 API Key 與 notarytool 憑證:使用 p8、Issuer ID、Key ID 三件套;金鑰檔權限限制為 CI 使用者唯讀;禁止把 p8 明文貼進公開儲存庫。

  3. 03

    在流水線內產生可上傳成品:對 .app 正確簽章後打包 zip/dmg;路徑含 pipeline id;計算 SHA256 並隨建置日誌輸出。

  4. 04

    執行 submit 並擷取 submission id:使用 --wait 或自建輪詢;把 Apple 回傳狀態寫入成品目錄備查。

  5. 05

    store log 與失敗分類:Invalid 類錯誤拉取詳細日誌;在工單系統關聯 xcodebuild -version 與儲存庫 commit。

  6. 06

    依分發策略 staple 並驗收:stapler validate 與試安裝矩陣(乾淨 VM 優先);再進入入口或物件儲存上傳。

bash · notarytool 片段(示意)
ZIP_PATH="dist/MyApp.${CI_PIPELINE_ID}.zip"
xcrun notarytool submit "$ZIP_PATH" \
  --key "./AuthKey_XXXXX.p8" \
  --key-id "$ASC_KEY_ID" \
  --issuer "$ASC_ISSUER_ID" \
  --wait \
  --output-format json > "logs/notary-${CI_PIPELINE_ID}.json"

# 失敗時拉詳細日誌(submission id 從 json 解析)
# xcrun notarytool log <submission-id> --key ... > "logs/notary-detail.txt"

xcrun stapler staple "dist/MyApp.app"
info

提示:若同一流水線還承擔 App Store Connect 上傳,請閱讀 Fastlane 與 CI 銜接,把 API Key 角色(Developer vs App Manager)與 notarytool 最小權限寫進變數表,避免「一把鑰匙開所有門」。

在 Xcode 或 macOS 大版本升級日,建議先對「最小 zip 包」做金絲雀公證:固定一個 hello-world 級別的 signed bundle,驗證 notarytool 與 staple 全鏈,再放開業務儲存庫;此與黃金映像回滾策略互補。

若遠端 Mac 由供應商提供快照能力,請把「公證鏈版本」寫進快照標籤:包含 Xcode build 號、CLI tools 版本與 notarytool 行為變更紀錄,避免團隊在新映像上集體踩預設參數變化。

成品留存應對齊合規:部分產業須長期保存 notary JSON 與 stapler 輸出;及早決定這些 blob 與 IPA 並存於物件儲存,或放在具生命週期規則的稽核桶。

04

並發、磁碟與重試:把「公證槽位」寫成容量模型

平台工程裡最常見的誤判,是把「能同時開幾個 xcodebuild」的並發模型直接套到公證上。實際上,上傳頻寬、Apple 側佇列、zip 壓縮 CPU鑰匙圈解鎖 會在不同階段成為瓶頸。

建議為公證單獨定義 notary_slots:例如每台遠端 Mac 同時最多 1–2 個 submit,其餘 Job 在佇列裡等待;並把牆鐘逾時與「可接受的排隊 SLA」寫進內部文件。與 SwiftPM/Pods 磁碟治理 篇聯動:公證暫存 zip 往往體積大,應與 DerivedData 分卷或分目錄,避免系統盤在夜間批量任務時觸頂。

warning

注意:不要在磁碟低於安全水位時繼續硬塞公證任務:半寫入的 zip 會上傳失敗且難以重現;應先停排程與清理,並記錄刪除路徑以便稽核。

重試策略上,應對「網路瞬斷」與「Apple 側繁忙」分層:前者可在秒級做有限次重試;後者應使用分鐘級退避並上限,避免觸發速率限制。所有重試必須記錄 submission id 與成品雜湊,防止「重複 staple 舊包」類事故。

若組織使用集中式代理,請把 Apple 相關網域與 TLS 繞過策略寫成顯式白名單;否則會出現「編譯全綠、公證全紅」且日誌只在代理層的現象,排障成本極高。

與僅在本機筆電上偶爾 Archive 相比,獨佔遠端 Mac 的價值在於7×24 可預測環境與固定 SSH 維運面:平台可以把公證 Runbook、磁碟水位與金鑰輪替綁進同一套節點畫像,而不是讓每台開發機各自為政。

若導入自癒代理程式,請確保不會刪除進行中的 notary 工作目錄:以租約或 pipeline 感知路徑保護刪除作業,避免與 stapler 步驟競爭。

05

寫進評審材料的參考口徑(可引用)

下列條目用於內部對齊;具體閾值以你們封包大小與並發度為準。數據與比例來自常見平台工程實踐,實施前請在你們環境複測。

  • 磁碟水位:公證流水線建議長期保留 ≥25% 系統盤可用空間;低於閾值時優先停排程再清理 zip 工作區。
  • 輪詢牆鐘:對含大型框架的封包,建議把 notarytool 全鏈路的預設預算從「編譯耗時」單獨拆行估算,並為 --wait 設定顯式上限 + 可告警
  • 稽核欄位:每次成功或失敗至少保留 submission id、成品 SHA256、xcodebuild -version 三行,以便跨團隊複盤。

純辦公室筆電做公證常受睡眠策略、VPN 抖動與個人鑰匙圈干擾;純 Linux 又無法完成官方 macOS 簽章鏈。把執行平面放到獨佔、長期在線、SSH 可達的遠端 Mac,並把 notarytool 與 staple 寫進可重複範本,才能把「能發封包」變成「能證明、能稽核、能交接」。相較自建零散機器或在不透明虛擬化層裡硬跑 Xcode,NodeMini 的 Mac Mini 雲端租賃在固定 SSH、清晰磁碟檔位與可複製的節點畫像上更利於平台治理;需要對比規格與價格時,可先閱讀 租賃價格說明,再結合 說明中心 完成接入與驗收。

落地時建議把本 Runbook 與內部「發佈等級」綁定:灰度、正式與熱修復對應不同的公證並發、日誌保留天數與 staple 驗收矩陣。

FAQ

常見問題

Apple 已推動開發者使用以 App Store Connect API Key 為基礎的 notarytool;相較依賴 Apple ID 工作階段的 altool,更適合在 CI 上使用 JSON 輸出、可腳本化的輪詢與錯誤碼,並在遠端 Mac 上與專用建置使用者及鑰匙圈分區對齊。節點規格與價格可先對照 租賃價格說明

取決於分發管道:直接下載場景通常需要 staple 以便離線驗證;App Store/TestFlight 路徑由 Apple 託管鏈主導。更多接入與網路策略也可查看 說明中心

主要風險是暫存目錄競用、API 速率限制與鑰匙圈項目混淆;應限制 notary_slots、分桶工作目錄,並與可重現建置的 Keychain 合約一致。可繼續對照站內 可重現建置企業建置資源池 兩篇。