ait-server:遠端權威與恢復
瞭解 ait-server 的遠端工作流程權威、Snapshot 儲存、災難恢復、runner 邊界與維運職責。
適用對象: Repository 所有者與維運
為什麼要跑 ait-server#
本地的 AIT 儲存庫沒有服務端照樣能用。下面這幾件事裡只要有一件是必須 的,就該加上 ait-server:
- 遠端工作流程管理: 把 Repository、Plan、Task、Change、Patchset、 證據、策略、評審、Land 和 Worker Job 的狀態,統一放在一個遠端權威 之下。
- 異地儲存與災難恢復: 把已記錄的 Snapshot 內容和 Line 頭放到 另一臺機器或另一個故障域,之後再用它們重建本地 AIT 權威、把原始碼 檔案物化出來。
- 儲存庫自己的 CI: 接納帶型別的 job,由相容的
ait-runner透過 儲存庫宣告的ci/run.sh或ci/run.ps1執行。
這幾件事共用同一臺服務端,但彼此是分開的。服務端連得上,不等於本地 每個檔案都能恢復;有一份異地副本,本身也不等於有了自動高可用或故障 切換。
服務端擁有的權威#
一個服務端資料根,擁有一份全域性的、只追加的 Repository 登錄檔。每個 註冊過的 Repository 都會拿到一個數字 repository_index;選中它的 權威靠的是這個索引,不是顯示名。Repository 的名字是可以重複的。
每一份數字 Repository 權威,都擁有它自己的遠端側:
- Snapshot 後設資料、內容包、祖先關係和 Line 頭;
- Plan、Task、Change、Patchset、attestation、評審、策略和 Land 記錄;
- 儲存庫範圍內的 Worker Job 和 CI 結果狀態;以及
- 校驗並排序這些操作所需的協議狀態。
那些遠端記錄,服務端說了算。它不擁有開發者目前的工作目錄,不替儲存庫 挑測試框架,也變不出從來沒被記錄過的檔案。
在儲存庫的恢復清單裡,把確切的 repository_index 留好。它是路由元 資料,不算金鑰,但一丟,重建客戶端時就沒有安全辦法認出已有的那份 權威了。索引絕對不要靠猜,也別拿另一個 Repository 的頂上。
註冊並核驗一個 Repository#
在一個初始化過的儲存庫裡,把服務端加成 remote:
ait remote add origin <server-url> --default
ait remote list --json
ait config show --json
ait repo show --remote origin --json還沒配 Repository 索引時,remote add 會註冊一份新的數字權威,並把 傳回的索引存下來。已經配了確切索引時,這條命令會去那臺服務端核驗 那份權威,對不上就直接失敗。
註冊不會傳任何 Snapshot 或工作區內容。它只是把後續工作流程、push、 pull 和恢復操作所需的地址和權威定下來。
遠端工作流程管理#
對一個遠端範圍的 Task 來說,本地的 Task worktree 仍然是編寫的邊界。 建立完 Snapshot 之後,就緒流程會把選定的 Patchset,以及遠端 Change 所要求的 Snapshot 證據發布出去:
ait workflow ready <change-id> --apply
ait workflow finish <change-id> --applyCI、attestation、評審、策略和 Land 的狀態順序,由服務端排。最終 finish 消費的是那個已就緒的選定 Patchset,並推進遠端的目標 Line。 workflow ready 和 workflow finish 都是純文字決策介面;某道關卡還掛著 時,照它們列印的確切下一步動作走。設定好的 Task 級等價入口是 ait task finish <task-or-change-id>。
維運可以在不改動的前提下檢視已註冊的權威和它的執行狀態:
ait repo show --remote origin --json
ait repo jobs --remote origin --json
ait repo ci-capabilities --remote origin --json遠端工作流程的發布,同時也是它所記錄 Snapshot 內容的一份異地副本。 一個發布了但還沒 land 的 Patchset,只是工作流程證據;它不代表遠端的 目標 Line 已經往前走了。
明確地保住一條 Line#
如果目的是把一條已經記錄下來的 Line 保住,而且跟受管的遠端 Task 無關,那 ait push 就是那條直接的同步路徑:
ait status --json
ait snapshot create --message "Record the recovery point"
ait push --remote origin --line mainpush 會上傳選定的 Line 頭和必需的 Snapshot 祖先,然後推進那條遠端 Line。它不會捕獲工作區裡的髒檔案,不會建立 Snapshot,不會發布 Patchset,不會合並分叉的歷史,也不會執行 Task finish。每條需要自己 恢復點的 Line 都得 push;註冊本身不會啟動什麼自動同步計劃。
一條 Line 最近一次成功 land 或 push 出去的遠端 Line 頭,就是它的可 恢復點。用你能接受的最大資料丟失視窗來定發布節奏,然後去核驗服務端、 真的演練一次恢復,而不是把"註冊成功了"當成備份證據。
保護邊界#
服務端能保住的是:
- 由 push 或遠端工作流程發布上傳的 Snapshot 內容和祖先關係;
- 成功推進過的遠端 Line 頭;
- 已發布的遠端工作流程記錄,以及它們接納的證據;以及
- 由那份權威存下來的、儲存庫範圍內的 CI 和 Worker Job 狀態。
服務端恢復不了的是:
- 髒的、未跟蹤的、被忽略的,或者沒進過 Snapshot 的工作區內容;
- 一直留在本地、從沒 push 或發布出去的 Snapshot 和 Line 移動;
- 在記錄下那個恢復點之前就被刪掉的檔案;
- 金鑰、憑據、編輯器狀態、外部服務,以及任何不屬於已記錄 Snapshot 的依賴;或者
- 服務端本身丟了之後的服務端資料根——除非維運另外在別處保護過這份 資料根。
正因為有這條邊界,異地儲存才必須是有意為之的動作:建立 Snapshot、 把它傳出去、確認遠端狀態,並留好用來定位它的 Repository 索引。
本地權威完好時的還原#
.ait 和 remote 設定都完好時,把遠端 Line 拉下來,再把它的頭物化 到一個乾淨的工作區裡:
ait status --json
ait diff --stat
ait pull --remote origin --line main --restorepull 會先匯入遠端的 Snapshot 祖先,再去判定本地頭和遠端頭的關係。 遠端更靠前就快進,本地歷史更靠前就不動這條 Line,沒讓它處理的分叉 它會拒絕。--restore 會用選定的、拉下來的那個頭替換掉工作區。工作 區是髒的就會被拒,除非明確帶上 --force;做這個選擇之前,先把只 存在於本地、還活著的檔案保好。
本地權威丟失或損壞時的恢復#
原來的 .ait 權威沒了或者信不過時,另開一個乾淨的恢復目錄。損壞的 那個目錄留著做診斷,別刪。先把可信的、存好的根路由欄位作為本地權威 的一部分恢復回來:確切的 repository_index、default_remote 和 remotes 對映。1.1.1 沒有公開的 repository_index 設定命令,而且 ait remote add 是註冊操作,不能拿來安全地頂替那份恢復清單。
# Run after the exact saved routing fields are restored.
ait config show --json
ait repo show --remote origin --json
ait remote recover-head --remote origin
ait remote recover-head --remote origin --apply
ait pull --remote origin --line main --restore第一條 recover-head 是預演。--apply 會下載服務端邏輯 main 頭 經過校驗的 Snapshot 祖先,並在一個校驗過的本地 Binary 權威世代裡把 它重建出來。1.1.1 的 recover-head 沒有 --line,也不支援多條 Line。最後那條 pull 把 main 物化成原始碼檔案。恢復不會把服務端的 遠端工作流程賬本克隆成一份單獨的本地工作流程賬本;遠端的 Task、Change、 Patchset 和 job 記錄,仍然從它們的服務端權威那裡查。
只要出現這幾種情況就停下來:權威的響應和存下來的索引對不上、預演報 出了預期之外的頭、校驗沒過,或者工作區裡還有必須保住的檔案。如果確 切的路由欄位恢復不出來,也停。別為了繞過一個對不上,就去註冊一份 替代權威。完整的退役歸檔走的是另一套生命週期: ait repo restore --remote origin 會建立出一份帶新 Repository 索引 的還原權威。
Runner 與 CI 的邊界#
ait-runner 是執行器,不是持久的 Repository 權威。它認領一個相容的 帶型別 Worker Job,把確切的 Snapshot 內容物化到隔離的嘗試儲存裡,跑 儲存庫自己寫的入口,傳回有界的結果。準入、租約、job 狀態和結果接納, 都歸服務端。
runner 不會替你挑某種語言專屬的建置系統,不會變成 Repository 權威的 第二份副本,也不會把它嘗試工作區裡的任意檔案留下來。嘗試目錄要保持 隔離,投遞失敗的結果要盯著,終態 job 之後要把嘗試目錄自己的資料清掉。
1.1.1 的確切命令、job 生命週期、請求與結果契約、物化上限、租約行為 和部署檢查,見 `ait-runner`:原生 CI 執行面。
保護服務端資料根#
用異地服務端來保護客戶端,前提是服務端權威本身還活著。容器示例把它 放在掛到 /var/lib/ait 的持久卷裡;安裝版的使用者模式和服務模式,用 的是各自設定的資料根。維運必須保護的,就是這一整個根。
ait-server 不會去排第二個副本,不會自己複製資料根,不會選主,也 不會自動故障切換。請在另一個故障域用獨立的備份或儲存快照系統。抓一 份應用一致的副本:要麼停掉寫入,要麼用能提供同等一致性保證的儲存; 別把隨手複製的、正在被寫的單個權威檔案當成可恢復的東西。
服務端版本或不可變映象摘要、資料根備份、確切的 Repository 索引清單、 遠端 URL,以及單獨管理的訪問憑據——恢復演練要用的這些都得留著。還原 出來的副本,在對外提供服務之前先隔離驗證:
ait-server probe --data <restored-data-root> --defer-ci-admissionprobe 失敗就意味著這個還原出來的根不能啟用。/healthz 傳回成功只 能證明正在跑的這個程序是健康的;它證明不了備份夠新、夠全或者能恢復。
維運清單#
- 預設把服務綁到 loopback,或者綁到一條評審過的私有入口上。
- 在受信任的入口和主機邊界上終結 TLS,並強制做呼叫方校驗、網路管控、 監控和容量限制。
- 把相容的 1.1.1 客戶端、服務端和 runner 製品都釘死版本。
- 每個 Repository 的數字索引,都記在客戶端資料根之外的地方。
- 為每條需要保護的 Line 定好並核驗 Snapshot 的發布節奏。
- 把完整的服務端資料根備份到另一個故障域。
- 拿一份隔離的副本,把
probe、recover-head和pull --restore演練一遍。 - 盯著服務端健康度、失敗的 job、runner 相容性和儲存增長。
1.1.1 的每條 HTTP 路由、請求契約、響應和錯誤邊界,見完整的 `ait-server` REST API 參考。 遠端基礎設施 講容器 邊界,`ait-runner`:原生 CI 執行面 講完整的 runner 維運契約, 排查 講通用的證據保全 規則。