跳到內容

備份、匯出與匯入

Control Center 能夠複製、移交或接納的一切,以及每種產物的確切形態。若要實際進行一次 備份,請參閱備份與還原;至於持久化為何如此 拆分,請參閱工作區與隔離。

共有三種資料庫副本產物,它們的涵蓋範圍與刪除原則各不相同。

產物 寫入者 形態 涵蓋範圍
安裝快照 server.backupNow 一個帶時間戳記的目錄 該安裝上的每一個資料庫
工作區備份 Workspace backup 管線 帶時間戳記、內含一個 .db 的目錄 一個工作區的資料庫
工作區匯出 workspace.export 單一 .db 檔案 一個工作區的資料庫

三者都以 SQLite VACUUM INTO 寫出,這對運作中的 WAL 資料庫是安全的:它會取得一個 讀取交易,產生一份重組過、具交易一致性的副本,而且完全不需要停止伺服器。

<dataDir>/backups/2026-07-28T09-12-33-000Z/
manifest.json
global.db
<workspaceId>/workspace.db one directory per workspace

此佈局與運作中的資料目錄完全一致,這正是讓還原一次安裝成為原樣複製回去、而非一次轉換 的原因。目錄名稱是 UTC 時間戳記,其中的 : 與 . 會替換為 -,因此它在 Windows 上 對檔案系統安全,作為純字串也能依時間順序排序。

排程的工作區備份另外存放在 <dataDir>/backups/workspace-backups/<workspaceId>/<timestamp>/workspace.db。 它們不包含清單或 global.db,也不是安裝快照。 每週管線的保留步驟只會移除自己所屬工作區中、早於其設定年限(預設 30 天)的備份; 手動建立的安裝快照絕不會被該步驟汰換。server.listBackups 只會列出安裝快照, 包括未完成的那些。

欄位 類型 意義
version int 清單格式版本。目前為 1
created_at ISO-8601 UTC 快照的產生時間
global.file string 一律為 global.db
global.schema_version int 擷取時 GlobalDatabase 的 schema 版本
global.bytes int 寫出的 global.db 大小
workspace_schema_version int 每個工作區檔案共用的 WorkspaceDatabase schema 版本
workspaces[] array 每個擷取到的工作區各一個項目
workspaces[].workspace_id string 此檔案所屬的工作區
workspaces[].file string 相對於快照根目錄的路徑,<workspaceId>/workspace.db
workspaces[].bytes int 該檔案的大小
skipped_workspaces[] array of string 備份未能擷取的工作區

當某個工作區已註冊但從未被寫入過(因此檔案尚不存在),或它的 VACUUM INTO 失敗時, 該工作區就會被跳過。無論是哪一種情況,快照仍會完成,只是清單會指出缺少了什麼。

server.listBackups 會為每個快照回報一個 complete 旗標。當清單不存在或無法解析、 global.db 遺失,或清單指出的工作區檔案不在磁碟上時,該旗標為 false。

未完成的快照仍會列出,因為維運人員看不見的快照,就是他們自以為擁有的快照。當清單無法 讀取時,列表會退回佈局本身,並回報它找到的任何 <workspaceId>/workspace.db 檔案—— 即使整個快照無法還原一次安裝,這些檔案仍可個別透過 workspace.import 接納。

兩種產物都只是資料庫。一個工作區目錄所持有的遠多於它的資料庫,而其餘的一切都不會 隨之移動:

路徑 在快照中? 在工作區匯出中?
global.db 是 否
<workspaceId>/workspace.db 是 是(它就是該檔案)
<workspaceId>/blobs/ 否 否
<workspaceId>/skills/ 否 否
<workspaceId>/agents/ 否 否
<workspaceId>/chat_credentials/ 否 否
<workspaceId>/spaces/<spaceId>/ 否 否
<workspaceId>/pr_clones/ 否 否
secrets.json 否 否
models/ 否 否
meetings/<meetingId>/ 否 否
rigs/ 否 否

其中有些是可捨棄的——spaces/ 保存的是從儲存庫重新佈建的寫入時複製工作樹, pr_clones/ 是快取,models/ 可重新下載,rigs/ 是機器狀態。有些則不是:blobs/ 保存著資料庫資料列所指向的貼上圖片,skills/ 與 agents/ 保存著代理執行時所用的 檔案,而 secrets.json 保存著該安裝上的每一個配對金鑰、供應商憑證與 SSO 設定。如何 涵蓋其餘內容,請參閱備份與還原。

安裝備份 RPC 與工作區匯出/匯入僅限 fullClient:已配對的手機無法觸發它們。在未接上 備份連接埠的伺服器上(例如示範伺服器),它們會完全不存在——不是被拒絕,而是在結構上 就缺少。

操作 種類 範圍 最低角色 引數 傳回
server.backupNow mutate 安裝 安裝擁有者 無 {ok, path}
server.listBackups read 安裝 安裝擁有者 無 {backups: [...]}
server.deleteBackup mutate 安裝 安裝擁有者 name {ok}
workspace.export read 工作區 admin 無 {ok, path}
workspace.import mutate 工作區 owner source_path {ok, workspace_id}
workspace.delete mutate 安裝* owner id 登錄資料列被標記為已刪除
  • workspace.delete 沒有範圍,是因為它所標記的資料列位於全域登錄中;其處理常式會檢查 呼叫者對所指工作區的成員資格,並要求 owner。

每個回應中的 path 都是 伺服器主機上的路徑。

最新的排在最前面,依目錄名稱排序(目錄名稱就是時間戳記,因此即使某個快照的清單無法 讀取,這個順序依然成立)。

{
"backups": [
{
"path": "/…/backups/2026-07-28T09-12-33-000Z",
"name": "2026-07-28T09-12-33-000Z",
"created_at": "2026-07-28T09:12:33.000Z",
"bytes": 481203712,
"complete": true,
"workspaces": [
{
"workspace_id": "w_9f2c…",
"path": "/…/backups/2026-07-28T09-12-33-000Z/w_9f2c…/workspace.db",
"bytes": 190840832
}
],
"skipped_workspace_ids": []
}
]
}

當清單無法讀取時,會省略 created_at。頂層的 bytes 是整個目錄在磁碟上的實際大小, 而不是清單自己加總的數字——維運人員清理快照時想問的是這個目錄佔用多少空間,而不是這次 備份原本打算寫入多少。

每個 workspaces[].path 正是 workspace.import 接受為 source_path 的值。這是 刻意的:「從這個快照還原這個工作區」就是匯入操作,因此兩者不可能對同一個檔案演變成 不同的處理方式。

在替換任何內容之前,候選檔案會以一個短生命週期的原生 SQLite 連線以唯讀方式開啟,並 讀取它的 workspace_meta 資料列。

條件 結果
不是可讀取的 SQLite 資料庫,或沒有 workspace_meta 拒絕。不會觸碰任何內容
記錄的工作區 id ≠ 目標 允許,並記錄於日誌。這正是將工作區複製或還原到新 id 之下的方式
記錄的安裝 id ≠ 本安裝 允許,並記錄於日誌。它已配對的裝置與使用者 id 屬於那次安裝,在此無法解析
工作區 id 無效,或檔案不存在 拒絕

一旦接受,目標工作區的資料庫會先被關閉並從管理器卸下,之後才把檔案複製覆蓋上去 (一個仍開啟的漂移連線會佔住該檔案及其 -wal),接著再重新開啟,讓 beforeOpen 重新安裝 FTS 與同步觸發器,並重建 FTS 索引——匯入檔案的內容表是有資料的,但它的索引 可能已過期。

上述每個操作都以伺服器端路徑來表達,唯有當伺服器就是你自己的機器時,這才是完整的 答案。這三條路由負責運送位元組,走的是媒體代理與 blob 儲存所用的同一條 PSK 簽章 通道。呼叫者會用自己裝置的預先共享金鑰對一個目標字串簽章,並將簽章以 s 傳入。

路由 方法 查詢 簽章目標 要求
/backup/workspace GET w, d, s backup-workspace:<workspaceId> 工作區管理員
/backup/snapshot GET n, d, s backup-snapshot:<name> 安裝擁有者
/backup/restore POST w, d, s backup-restore:<workspaceId> 工作區擁有者

w 是工作區 id,n 是快照目錄名稱,d 是發起呼叫的裝置 id,s 是簽章。n 是 依據備份列表解析的,而不是直接拼接到備份根目錄上,因此這條路由無法被指向一個列表本 不會顯示的目錄。

狀態 原因
400 缺少必要的查詢參數,或上傳內容為空
403 裝置未知或已撤銷、簽章錯誤,或呼叫者不具備該角色
404 此主機上沒有備份連接埠,或所指的快照/工作區檔案不存在
405 以 POST 以外的方法呼叫 /backup/restore
413 上傳內容超過 8 GiB,無論是依宣告的 content-length 或依實際讀取的位元組數
500 匯出或封存在伺服器端失敗

兩個下載都會回應 Cache-Control: no-store 與 Accept-Ranges: none。每個請求都會 產生一份全新的副本,並在串流傳送完畢後刪除它,因此被快取或續傳的回應會把兩次不同 匯出的位元組拼接成一個看起來有效、實則無效的檔案。

/backup/snapshot 會傳回 <name>.zip,其中的項目都相對於快照根目錄,因此解壓縮後 會重現 manifest.json + global.db + <workspaceId>/workspace.db。項目按排序後的 順序寫入,因此同一快照的兩份封存會是完全相同的封存。

/backup/restore 會把內容直接串流寫入暫存檔,而不是緩衝它;它在讀取前與讀取過程中 都對大小設限,並在每一條退出路徑上刪除暫存檔,包括被拒絕時。

項目 值
快照 <dataDir>/backups/<timestamp>/
排程的工作區備份 <dataDir>/backups/workspace-backups/<workspaceId>/<timestamp>/workspace.db
工作區匯出 <dataDir>/backups/exports/<workspaceId>-<timestamp>.db
傳輸暫存 <dataDir>/backups/transfer/——一塊工作檯,由寫入它的那條路由負責清理
快照封存名稱 <snapshot directory name>.zip
上傳上限 每次 POST /backup/restore 8 GiB
保留策略 安裝快照:手動刪除。工作區備份:每週管線,預設 30 天

exports/、transfer/ 與 workspace-backups/ 目錄都不是安裝快照,也不在 server.listBackups 的範圍內。只有最後一個目錄有排程的保留策略;刪除安裝快照需要 安裝擁有者明確採取動作。