備份、匯出與匯入
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 只會列出安裝快照,
包括未完成的那些。
manifest.json
標題為「manifest.json」的章節| 欄位 | 類型 | 意義 |
|---|---|---|
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 都是 伺服器主機上的路徑。
server.listBackups 的回應
標題為「server.listBackups 的回應」的章節最新的排在最前面,依目錄名稱排序(目錄名稱就是時間戳記,因此即使某個快照的清單無法 讀取,這個順序依然成立)。
{ "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 的值。這是
刻意的:「從這個快照還原這個工作區」就是匯入操作,因此兩者不可能對同一個檔案演變成
不同的處理方式。
workspace.import 驗證
標題為「workspace.import 驗證」的章節在替換任何內容之前,候選檔案會以一個短生命週期的原生 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 的範圍內。只有最後一個目錄有排程的保留策略;刪除安裝快照需要
安裝擁有者明確採取動作。
另請參閱
標題為「另請參閱」的章節- 備份與還原 — 這些操作所屬的配方
- 工作區與隔離 — 為什麼一個工作區就是一個檔案
cc_serverCLI — 這些路徑所在的資料目錄- 路由圖 — 使用者介面所在的位置
- 架構 — 這些路徑所遵循的用戶端/伺服器切分