Ga naar inhoud

Backup, export en import

Alles wat Control Center kan kopiëren, overdragen of overnemen, en de exacte vorm van elk artefact. Hoe je daadwerkelijk een back-up maakt, staat in Back-uppen en herstellen; waarom persistentie zo is opgesplitst, staat in Werkruimtes en isolatie.

Er zijn drie artefacten die een database kopiëren, met verschillende bereiken en verwijderbeleid.

Artefact Geschreven door Vorm Dekt
Installatiemomentopname server.backupNow Een map met tijdstempel Elke database van de installatie
Werkruimteback-up Pipeline Workspace backup Map met tijdstempel met één .db De database van één werkruimte
Werkruimte-export workspace.export Eén .db-bestand De database van één werkruimte

Alle drie worden geschreven met SQLite VACUUM INTO, wat veilig is tegen een actieve WAL-database: het neemt een leestransactie, produceert een gedefragmenteerde en transactioneel consistente kopie en vraagt er nooit om de server te stoppen.

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

De indeling spiegelt de live datadir exact, en dat is wat het herstellen van een installatie een terugkopie maakt in plaats van een vertaling. De mapnaam is de UTC-tijdstempel waarin : en . zijn vervangen door -, dus hij is veilig voor het bestandssysteem op Windows en sorteert chronologisch als een gewone tekenreeks.

Geplande werkruimteback-ups staan apart in <dataDir>/backups/workspace-backups/<workspaceId>/<timestamp>/workspace.db. Ze bevatten geen manifest of global.db en zijn geen installatiemomentopnamen. De bewaarstap van de wekelijkse pipeline verwijdert alleen back-ups van zijn eigen werkruimte die ouder zijn dan de ingestelde leeftijd (standaard 30 dagen); handmatig gemaakte installatiemomentopnamen worden door die stap nooit verwijderd. server.listBackups vermeldt alleen installatiemomentopnamen, ook onvolledige.

Veld Type Betekenis
version geheel getal Versie van het manifestformaat. Momenteel 1
created_at ISO-8601 UTC Wanneer de momentopname is gemaakt
global.file tekenreeks Altijd global.db
global.schema_version geheel getal Schemaversie van GlobalDatabase op het moment van vastleggen
global.bytes geheel getal Grootte van het geschreven global.db
workspace_schema_version geheel getal Schemaversie van WorkspaceDatabase, gedeeld door elk werkruimtebestand
workspaces[] array Eén vermelding per vastgelegde werkruimte
workspaces[].workspace_id tekenreeks De werkruimte die dit bestand bevat
workspaces[].file tekenreeks Pad ten opzichte van de hoofdmap van de momentopname, <workspaceId>/workspace.db
workspaces[].bytes geheel getal Grootte van dat bestand
skipped_workspaces[] array van tekenreeksen Werkruimtes die de back-up niet kon vastleggen

Een werkruimte wordt overgeslagen wanneer hij is geregistreerd maar er nooit naar is geschreven (er bestaat dus nog geen bestand), of wanneer zijn VACUUM INTO is mislukt. Hoe dan ook wordt de momentopname alsnog voltooid en noemt het manifest wat er ontbreekt.

server.listBackups rapporteert per momentopname een complete-markering. Die is onwaar wanneer het manifest ontbreekt of niet te parsen is, wanneer global.db ontbreekt, of wanneer het manifest een werkruimtebestand noemt dat niet op schijf staat.

Een onvolledige momentopname wordt toch vermeld, want een momentopname die een operator niet ziet, is er een waarvan hij gelooft dat hij hem heeft. Wanneer het manifest onleesbaar is, valt de lijst terug op de indeling zelf en rapporteert hij de <workspaceId>/workspace.db-bestanden die hij vindt — die blijven afzonderlijk over te nemen via workspace.import, ook al kan de momentopname als geheel geen installatie herstellen.

Beide artefacten zijn alleen databases. Een werkruimtemap bevat meer dan zijn database, en niets daarvan reist mee:

Pad In een momentopname? In een werkruimte-export?
global.db Ja Nee
<workspaceId>/workspace.db Ja Ja (het is het bestand)
<workspaceId>/blobs/ Nee Nee
<workspaceId>/skills/ Nee Nee
<workspaceId>/agents/ Nee Nee
<workspaceId>/chat_credentials/ Nee Nee
<workspaceId>/spaces/<spaceId>/ Nee Nee
<workspaceId>/pr_clones/ Nee Nee
secrets.json Nee Nee
models/ Nee Nee
meetings/<meetingId>/ Nee Nee
rigs/ Nee Nee

Sommige daarvan is wegwerpbaar — spaces/ bevat copy-on-write-worktrees die opnieuw worden geprovisioneerd vanuit de repo’s, pr_clones/ is een cache, models/ wordt opnieuw gedownload, rigs/ is machinestatus. Sommige niet: blobs/ bevat geplakte afbeeldingen waar rijen in de database naar verwijzen, skills/ en agents/ bevatten de bestanden waarmee een agent draait, en secrets.json bevat elke koppelingssleutel, providerreferentie en SSO-instelling van de installatie. Zie Back-uppen en herstellen voor het dekken van de rest.

Back-up-RPC’s voor de installatie en export/import van werkruimtes zijn alleen voor fullClient: een gekoppelde telefoon kan ze niet starten. Ze zijn allemaal volledig afwezig — niet geweigerd, maar structureel ontbrekend — op een server waar geen back-uppoort is aangesloten, zoals de demoserver.

Op Soort Bereik Minimale rol Argumenten Retourneert
server.backupNow mutate Installatie eigenaar van de installatie geen {ok, path}
server.listBackups read Installatie eigenaar van de installatie geen {backups: [...]}
server.deleteBackup mutate Installatie eigenaar van de installatie name {ok}
workspace.export read Werkruimte admin geen {ok, path}
workspace.import mutate Werkruimte owner source_path {ok, workspace_id}
workspace.delete mutate Installatie* owner id registerrij gemarkeerd als verwijderd
  • workspace.delete heeft geen bereik omdat de rij die hij markeert in het globale register staat; zijn handler controleert het lidmaatschap van de aanroeper van de genoemde werkruimte en vereist owner.

path in elk antwoord is een pad op de serverhost.

Nieuwste eerst, gesorteerd op mapnaam (dat is de tijdstempel, dus de volgorde klopt ook voor een momentopname waarvan het manifest niet kon worden gelezen).

{
"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 ontbreekt wanneer het manifest niet kon worden gelezen. bytes op het hoogste niveau is de werkelijke grootte op schijf van de hele map, niet de som uit het manifest — de vraag die een operator die momentopnamen opruimt stelt, is wat de map kost, niet wat de back-up van plan was te schrijven.

Elke workspaces[].path is precies wat workspace.import als source_path aanneemt. Dat is bewust: “deze werkruimte herstellen uit deze momentopname” is de importbewerking, dus de twee kunnen niet uit elkaar lopen in verschillende behandelingen van hetzelfde bestand.

Voordat er iets wordt vervangen, wordt het kandidaatbestand alleen-lezen geopend met een kortstondige ruwe SQLite-verbinding en wordt de workspace_meta-rij gelezen.

Voorwaarde Resultaat
Geen leesbare SQLite-database, of geen workspace_meta Geweigerd. Er wordt niets aangeraakt
Vastgelegd werkruimte-id ≠ het doel Toegestaan, gelogd. Zo wordt een werkruimte gedupliceerd of onder een nieuw id hersteld
Vastgelegd installatie-id ≠ deze installatie Toegestaan, gelogd. De gekoppelde apparaten en gebruikers-id’s horen bij die installatie en worden hier niet herleid
Ongeldig werkruimte-id, of bestand bestaat niet Geweigerd

Bij acceptatie wordt de database van de doelwerkruimte gesloten en uit de manager gehaald voordat het bestand eroverheen wordt gekopieerd (een open drift-verbinding houdt het bestand en zijn -wal vast), en daarna heropend zodat beforeOpen de FTS- en synctriggers opnieuw installeert, en de FTS-indexen worden opnieuw opgebouwd — de inhoudstabellen van een geïmporteerd bestand zijn gevuld, maar de index kan verouderd zijn.

Elke bewerking hierboven spreekt in paden aan de serverkant, wat alleen een volledig antwoord is als de server je eigen machine is. Deze drie dragen de bytes, op dezelfde met PSK ondertekende baan die de mediaproxy en het blobarchief gebruiken. De aanroeper ondertekent een doeltekenreeks met de vooraf gedeelde sleutel van zijn apparaat en geeft de handtekening mee als s.

Route Methode Query Ondertekend doel Vereist
/backup/workspace GET w, d, s backup-workspace:<workspaceId> Werkruimte-beheerder
/backup/snapshot GET n, d, s backup-snapshot:<name> Installatie-eigenaar
/backup/restore POST w, d, s backup-restore:<workspaceId> Werkruimte-eigenaar

w is een werkruimte-id, n een naam van een momentopnamemap, d het id van het aanroepende apparaat, s de handtekening. n wordt herleid tegen de back-uplijst in plaats van aan de backups-root te worden geplakt, zodat de route niet kan worden gericht op een map die de lijst niet zou hebben getoond.

Status Oorzaak
400 Een vereiste queryparameter ontbreekt, of de body van een upload is leeg
403 Onbekend of ingetrokken apparaat, slechte handtekening, of de aanroeper mist de rol
404 Geen back-uppoort op deze host, of de genoemde momentopname/het genoemde werkruimtebestand bestaat niet
405 /backup/restore aangeroepen met iets anders dan POST
413 De uploadbody is groter dan 8 GiB, volgens de opgegeven content-length of volgens de werkelijk gelezen bytes
500 De export of het archief is aan de serverkant mislukt

Beide downloads antwoorden met Cache-Control: no-store en Accept-Ranges: none. Elk verzoek maakt een nieuwe kopie aan en verwijdert die zodra hij is gestreamd, dus een uit de cache gehaald of hervat antwoord zou bytes van twee verschillende exports aaneenrijgen tot één bestand dat geldig lijkt en het niet is.

/backup/snapshot retourneert <name>.zip, met items ten opzichte van de hoofdmap van de momentopname, zodat uitpakken manifest.json + global.db + <workspaceId>/workspace.db reproduceert. De items worden in gesorteerde volgorde geschreven, dus twee archieven van dezelfde momentopname zijn hetzelfde archief.

/backup/restore streamt de body rechtstreeks naar een stagingbestand in plaats van hem te bufferen, begrenst hem vóór en tijdens het lezen, en verwijdert het klaargezette bestand op elk uitpad, ook bij een weigering.

Onderdeel Waarde
Momentopnamen <dataDir>/backups/<timestamp>/
Geplande werkruimteback-ups <dataDir>/backups/workspace-backups/<workspaceId>/<timestamp>/workspace.db
Werkruimte-exports <dataDir>/backups/exports/<workspaceId>-<timestamp>.db
Staging voor overdrachten <dataDir>/backups/transfer/ — een werkbank, opgeruimd door de route die hem schrijft
Naam van het momentopnamearchief <snapshot directory name>.zip
Uploadplafond 8 GiB per POST /backup/restore
Bewaring Installatiemomentopnamen: handmatige verwijdering. Werkruimteback-ups: wekelijkse pipeline, standaard 30 dagen

De mappen exports/, transfer/ en workspace-backups/ zijn geen installatiemomentopnamen en zijn uitgesloten van server.listBackups. Alleen de laatste map heeft geplande bewaring; het verwijderen van een installatiemomentopname vereist een expliciete actie van de eigenaar van de installatie.