備份、匯出與匯入
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/ 保存的是會從程式庫重新佈建的寫入時複製 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 都是伺服器主機上的路徑。
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 — 這些路徑所在的資料目錄- 路由圖 — 介面所在的位置
- 架構 — 這些路徑所遵循的用戶端/伺服器切分