Přeskočit na obsah

Pracovní prostory a izolace

Co je pracovní prostor?

Sekce “Co je pracovní prostor?”

Pracovní prostor je v Control Center nejvyšší tenant izolace. Sdružuje agenty, repozitáře, prostory, tickety, paměť, pipeline a členy do ohraničeného kontextu. Všechno, co se v pracovním prostoru stane, v něm zůstane.

Běžný pracovní prostor odpovídá jednomu projektu nebo jedné kódové základně. Můžeš mít zvlášť pracovní prostory „api-service“, „frontend-v2“ a „infrastructure“.

Data v rozsahu pracovního prostoru

Sekce “Data v rozsahu pracovního prostoru”

Řádky pracovního prostoru žijí v jeho vlastním souboru SQLite, na <dataDir>/<workspaceId>/workspace.db. Každý pracovní prostor dostane adresář, ne holý soubor, protože nasbírá víc než databázi: worktree po konverzacích, soubory agentů a skillů, přihlašovací údaje chatovacího bota. Složka znamená, že tohle všechno žije a maže se pohromadě.

Entita Rozsah
Agenti Patří právě jednomu pracovnímu prostoru
Repozitáře Registrované do jednoho pracovního prostoru (viz níže)
Prostory, konverzace a zprávy V rozsahu pracovního prostoru
Tickety, projekty, plány, orchestrace V rozsahu pracovního prostoru
Fakta paměti, politiky, domény, přístupové granty V rozsahu pracovního prostoru
Běhy pipeline, šablony, spouštěče V rozsahu pracovního prostoru
Stav pull requestu, prostory kontroly, kohorty kontroly V rozsahu pracovního prostoru
Záznamy běhů agenta a pracovní paměť V rozsahu pracovního prostoru
Členové, pozvánky, přidělení na repozitář V rozsahu pracovního prostoru
Symboly a hrany grafu kódu Podle repoId uvnitř souboru pracovního prostoru

Většina těch řádků nese sloupec workspaceId. Izolaci vynucují oddělené soubory databáze. Viz invariant izolace níže.

Repozitáře patří pracovnímu prostoru; identita napříč prostory je podle cesty

Sekce “Repozitáře patří pracovnímu prostoru; identita napříč prostory je podle cesty”

Řádek repozitáře žije v databázovém souboru svého pracovního prostoru. Soubor je ten rozsah, takže tabulka nemá sloupec workspaceId ani spojovací tabulku.

Repozitář do pracovního prostoru přidáš v Nastavení → Pracovní prostor → Repozitáře. Registrace téhož checkoutu ve dvou pracovních prostorech vytvoří dva nezávislé řádky se dvěma id. Identita repozitáře napříč pracovními prostory je cesta na souborovém systému (nebo vlastník a jméno na GitHubu), nikdy id.

Registrace je přísná: cesta musí ležet v pracovním stromu gitu, musí mít remote origin a ten remote musí mířit na podporovaného hostitele kódu (github.com, gitlab.com nebo bitbucket.org). Hostitel se přečte z remote a uloží se na repozitář, takže jeden pracovní prostor může držet repozitáře od všech tří. Cokoli jiného se odmítne. Prohlížeč složek, ze kterého vybíráš, vypisuje souborový systém hostitele serveru, omezený na seznam povolených kořenů --repo-roots (výchozí je domovský adresář uživatele serveru).

Odebrání repozitáře z pracovního prostoru řádek smaže rovnou a kaskádou vezme jeho symboly kódu, hrany, soubory, checkpointy indexu a přidělení jednotlivých členů. Checkout na disku zůstane.

Graf kódu žije v tomtéž souboru, klíčovaný repoId, protože různé pracovní prostory můžou mít stejný repozitář na jiných větvích s jiným kódem.

Únik dat mezi pracovními prostory se bere jako chyba. Izolace je strukturální a vynucuje se na několika úrovních:

  • Vrstva databáze. Každý pracovní prostor má vlastní soubor SQLite, který vydává WorkspaceDatabaseManager.of(workspaceId). Databáze pracovního prostoru nedeklaruje users, workspaces ani tabulky jiného pracovního prostoru, takže čtení napříč se jako chyba napsat nedá: nepřeloží se. Repozitáře si DAO berou při každém volání, místo aby si jedno držely, a protože každá metoda repozitáře bere povinné workspaceId, pracovní prostor se nikdy neodvodí ani nedoplní výchozí hodnotou.
  • Id pracovního prostoru je segment cesty. Z id se stane jméno adresáře, takže musí sedět na bezpečný vzor jednoho segmentu (začátek alfanumerický, pak alfanumerické znaky, tečky, podtržítka a spojovníky; žádné ..). Id jsou v produktu všude UUID. Cokoli jiného vyhodí výjimku, místo aby se to očistilo. To je ochrana proti path traversal a selže nahlas, místo aby zapsala data pracovního prostoru na cestu, kterou si vybral někdo jiný.
  • Obrana do hloubky. Sloupce workspaceId tam pořád jsou a pořád se zapisují. Uvnitř souboru jsou redundantní, drží triggery synchronizačního feedu, indexy FTS a stávající tvary řádků beze změny a soubor se díky nim popisuje sám, když se prohlíží samostatně. O to se opírá export a import.
  • Doménová vrstva. Služby, které mění entity podle id, ověří entity.workspaceId == workspaceId na jediném kontrolním bodě, než pokračují, a při neshodě vyhodí WorkspaceMismatchException. To je hlasité selhání pro id, které přišlo ze špatného kontextu. Hlasité odmítnutí je podstatné: tiché nic neudělání chybu schová a pokračování ji pustí ven.
  • Vrstva RPC. Dispečer repo-RPC je ten kontrolní bod. Každá operace v rozsahu pracovního prostoru musí nést workspace_id ve vlastních argumentech. Relace žádný „aktuální pracovní prostor“ nedrží, o který by se dalo zakopnout, a neznámé id se odmítne jako nenalezené dřív, než se cokoli otevře jako databáze, takže špatné id nemůže zhmotnit ducha workspace.db. Opravdu globální operace (kanál novinek, fronta flotily) se deklarují jako bez rozsahu, a to výslovným, zkontrolovaným rozhodnutím. Nástroje MCP jedou podle téhož pravidla: každý nástroj, který sahá na data v rozsahu pracovního prostoru, vyžaduje workspace_id.
  • Rozvětvení napříč pracovními prostory jde vyjmenovat. Odpověď na otázku o několika pracovních prostorech znamená otevřít několik databází. To rozvětvení je sevřené v jednom pomocníkovi, CrossWorkspaceQueries, takže úplný seznam věcí, které hranici legitimně překročí, žije v místech volání jednoho souboru, ne rozptýlený za komentáři v dokumentaci. Ta místa volání jsou: agregace přes celý server, rekoncilery při startu, úklidy retence a GC, záloha, směrovače událostí a vyhledání členství níže.
  • Přístup jen podle ID nestačí. Vyhledání entity podle UUID nedokazuje, že patří do pracovního prostoru volajícího. Rozdělení na soubory udělá špatný soubor nedosažitelným. Validace v doménové vrstvě chytí špatné id, které přijde skrz legitimní soubor.

Přístup dává členství, ne párovací klíč

Sekce “Přístup dává členství, ne párovací klíč”

Držení přihlašovacích údajů zařízení ověřuje zařízení. Uvnitř pracovního prostoru tím nic nepovoluje. Každá operace v rozsahu pracovního prostoru vyřeší roli členství volajícího a vynutí práh podle druhu operace: čtení potřebuje guest, mutace potřebuje member, destruktivní operace potřebuje admin. Operace, které ukazují obsah repozitáře, navíc kontrolují přidělení volajícího na ten repozitář. Platný párovací klíč bez členství dostane Not a member of this workspace.

Důsledek pro izolaci je jednoduchý: řádky členství žijí v souboru samotného pracovního prostoru, takže „kdo je v tomhle pracovním prostoru?“ je obyčejné čtení v rozsahu. Opačná otázka, „ve kterých pracovních prostorech jsem?“, je z definice rozložená přes soubory, takže je to jedno z mála povolených rozvětvení CrossWorkspaceQueries a právě to spouští výběr pracovního prostoru, než je nějaký vybraný.

Žebřík rolí a přidělení na repozitář jsou v Multiplayer a přítomnost.

Ne všechno je vázané na pracovní prostor. Druhá databáze, <dataDir>/global.db, drží to, co opravdu patří celému serveru:

  • registr pracovních prostorů, aby přepínač vypsal každý pracovní prostor, aniž otevřel jediný jeho soubor, a aby jméno, logo, vlastník, ruční pořadí a značka měkkého smazání žily mimo soubor, který popisují;
  • identita: uživatelé, předvolby uživatele a spárovaná zařízení, protože jeden člověk je jeden uživatel ve všech pracovních prostorech a spárované zařízení přežije smazání pracovního prostoru;
  • fronta flotily (workeři, úlohy, záznam umístění), jejíž plánovač při každém tiku porovná celou frontu s každým workerem;
  • kanál novinek, který je na uživatele (ne na pracovní prostor): přidaný zdroj tě následuje do každého pracovního prostoru;
  • workspace_routes, index před ověřením, který odpovídá na „který pracovní prostor vlastní tenhle klíč?“ u vstupů, které přijdou jen s tajemstvím nebo s neprůhledným id (hash pozvánky, token webhooku, deep link) a bez pracovního prostoru. Minutí je nenalezené. Záložní prohledání schválně není;
  • nastavení a identita celé instalace: id instalace, nastavení hostitele, spojení SSO a spravované politiky akcí (svorka na celou instalaci, která guardraily pracovního prostoru umí jen přitáhnout).

Při startu se otevře jen tenhle soubor. Soubory pracovních prostorů se otevírají líně, při prvním sáhnutí, takže cena startu zůstává plochá, ať pracovní prostory nasbíraly historie kolik chtěly, a quick_check za soubor se platí při prvním použití, ne na cestě k banneru, že je server připravený.

Soubor pracovního prostoru nese jen jednořádkové workspace_meta pro sebeidentifikaci (své id, instalaci, která ho vytvořila, verzi schématu, se kterou vznikl, a kdy). Všechno popisné o pracovním prostoru je v registru.

Vytvoření pracovního prostoru

Sekce “Vytvoření pracovního prostoru”

Vytvoření pracovního prostoru je víc než vložení řádku do registru. V jedné operaci server zapíše zakládajícího uživatele jako ownerUserId, zaznamená jeho členství v roli vlastníka a publikuje doménovou událost WorkspaceCreated. Posluchači té události pak nasadí, idempotentně a na pozadí:

  • agenta CEO a čtyři specialisty, qa, architect, engineer, librarian, přičemž specialisté se zodpovídají CEO;
  • vestavěné šablony pipeline a jejich spouštěče (spouštěče na událost a ruční se dodávají zapnuté; plánované jsou volitelné, kromě denního úklidu worktree přes GC);
  • startovací sady evalů.

Děje se to u každého pracovního prostoru, ne jen u prvního. Nasazení agenti vzniknou bez adaptéru, takže běží na Control Center (built-in) s výchozím modelem Anthropic (claude-opus-4-8, pokud nepřipneš jiný), dokud u každého agenta nenastavíš adaptér a model v Nastavení → Pracovní prostor → Agenti. Viz Model agentů.

Zápis členství vlastníka je důležitější, než vypadá: bez něj by čerstvý pracovní prostor neměl členy a každé další volání vázané na pracovní prostor, které tvůrce udělá, by bylo odmítnuté.

Mazání, export a import

Sekce “Mazání, export a import”

Smazání pracovního prostoru označí řádek v registru a odpojí adresář. Pracovní prostor zmizí z každého seznamu i vyhledání. Řádek v registru zůstane (měkce smazaný), aby id pořád znala záloha a údržba, ale workspace.delete hned zahodí adresář toho pracovního prostoru (databázi, worktree, soubory agentů a zbytek) přes WorkspaceDatabaseManager.dropAndClose. Smazat ho může jen vlastník pracovního prostoru.

Protože jeden pracovní prostor je jeden soubor, předání pracovního prostoru je operace se souborem, ne výpis tabulku po tabulce:

  • workspace.export je jedno VACUUM INTO: konzistentní, defragmentovaný snímek pořízený pod čtecí transakcí, zatímco server dál obsluhuje.
  • workspace.import takový soubor převezme a nahradí jím to, co cílový pracovní prostor právě drží. Vestavěné workspace_meta nechá import rozeznat „můj vlastní soubor, znovu převzatý“ od „souboru z jiné instalace“ (ten druhý je povolený, ale zalogovaný, ne neviditelný).
  • server.backupNow udělá snímek celé instalace do adresáře s časovým razítkem, který zrcadlí živý datový adresář: manifest.json, global.db a jedno <workspaceId>/workspace.db na pracovní prostor. Obnova je zkopírování zpátky.
  • server.listBackups ten adresář čte zpátky, od nejnovějšího, s velikostí a seznamem pracovních prostorů každého snímku. Snímek, kterému chybí manifest nebo který jmenuje soubory, jež tam nejsou, se pořád nahlásí a označí jako neúplný. Schovat ho by operátora nechalo věřit v zálohu, která neexistuje.

Operace zálohy žijí pod Nastavení → Server → Záloha a obnovení: Zálohovat teď vezme snímek instalace, seznam ukáže, které snímky jsou úplné, a vlastník instalace může jeden po potvrzení smazat. Každý pracovní prostor nabízí export a import. Vestavěná pipeline Workspace backup bere jen databázi toho pracovního prostoru, jednou týdně, a maže vlastní zálohy starší než nastavený věk retence. Obnova pracovního prostoru ze snímku instalace je workspace.import namířený na <workspaceId>/workspace.db ve snímku. Obnova celé instalace zůstává zkopírováním adresáře snímku zpátky při zastaveném serveru.

Co žádná z nich nenese, je zbytek adresáře pracovního prostoru. Pracovní prostor nasbírá víc než databázi: vložené obrázky, soubory skillů a agentů, přihlašovací údaje chatu, worktree. Export a import přesouvají soubor, takže všechno vedle něj zůstane, kde je. U přihlašovacích údajů je to schválně, u worktree je to na odpis a u zbytku je to mez, o které máš vědět, ne funkce. Úplná tabulka je v referenci zálohy.

Přesun zálohy mezi serverem a tvým zařízením

Sekce “Přesun zálohy mezi serverem a tvým zařízením”

Každá operace výše mluví v cestách na serveru, a to je úplná odpověď jen tehdy, když je server tvůj vlastní stroj. Tři podepsané HTTP cesty, /backup/workspace, /backup/snapshot a /backup/restore, přenesou bajty pro každou jinou topologii, po stejné ověřené lince, kterou už používá proxy médií a úložiště obrázků. Na stránce je to tlačítko Stáhnout vedle každého exportu a každého snímku (vybereš, kam soubor spadne) a Vybrat soubor a nahrát, když obnovuješ ze souboru na stroji, u kterého sedíš. Nahrání je to, co funguje, když server není tvůj stroj: workspace.import potřebuje, aby soubor už ležel na hostiteli, a tudy se tam dostane.

Tři vlastnosti stojí za to znát:

  • Stažení vytvoří čerstvou kopii a server si z ní nic nenechá. Při stažení se export spustí znovu, výsledek se streamuje a pak se smaže. Odpověď je no-store s Accept-Ranges: none ze stejného důvodu: obnovený rozsah by slepil dva různé exporty do jednoho souboru, který vypadá platně a není.
  • Nahrání se streamuje na disk a nikdy se nebufferuje, na obou koncích, a dočasná kopie se smaže na každé cestě ven, včetně odmítnutí. Databáze pracovního prostoru není snímek obrazovky.
  • Spojení přes relay nemá HTTP origin, takže ovládání přenosu je tam vypnuté a poznámka říká proč. Všechno, co mluví v cestách na straně serveru, přes relay dál funguje. Přímé spojení potřebuje jen samotný přenos bajtů.

Víc pracovních prostorů

Sekce “Víc pracovních prostorů”

Pracovních prostorů můžeš mít víc vedle sebe. Každý jede sám: agenti v jednom pracovním prostoru nemůžou psát agentům v jiném, číst jeho paměť ani vidět jeho tickety. Rozlišení příjemce u zpráv mezi agenty je přesné a pracovní prostor nikdy nepřekročí.

Nástěnka napříč pracovními prostory neexistuje. Analytická plocha na /workspaces/:workspaceId/observability se přihlásí k omezenému globálnímu feedu nedávných běhů a hned ho zúží na aktivní pracovní prostor, takže tam vidíš vždycky jeden. Jediné plochy, na které člověk opravdu sáhne napříč víc pracovními prostory, jsou výběr pracovního prostoru a přepínač.

Jedna praktická mez, o které stojí za to vědět: pracovní prostor, který někdo používá, zůstane otevřený. LRU tu není. Po 32 takových server varuje, místo aby odmítal, protože každý otevřený soubor drží isolate na pozadí a page cache. Rozvětvení napříč pracovními prostory soubory otevírá jen na chvíli a potom je zavře, pokud je mezitím nic jiného nedrží.