跳至內容

備份、匯出與匯入

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/ 保存的是會從程式庫重新佈建的寫入時複製 worktree, 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 的範圍內。只有最後一個目錄有排定的保留策略;刪除安裝快照需要 安裝擁有者明確操作。