Spazi di lavoro e isolamento
Che cos’è uno spazio di lavoro?
Sezione intitolata “Che cos’è uno spazio di lavoro?”Uno spazio di lavoro è il tenant di isolamento di primo livello in Control Center. Raggruppa agenti, repository, spazi, ticket, memoria, pipeline e membri in un contesto delimitato. Tutto ciò che accade dentro uno spazio di lavoro resta al suo interno.
Uno spazio di lavoro tipico corrisponde a un progetto o a una codebase. Puoi avere spazi di lavoro separati per “api-service”, “frontend-v2” e “infrastructure”.
Dati con ambito di spazio di lavoro
Sezione intitolata “Dati con ambito di spazio di lavoro”Le righe di uno spazio di lavoro vivono nel file SQLite di quello spazio di lavoro, in <dataDir>/<workspaceId>/workspace.db. Ogni spazio di lavoro riceve una directory anziché un file nudo, perché uno spazio di lavoro accumula più di un database — worktree per conversazione, i file di agenti e skill, le credenziali dei bot di chat — e dargli una cartella significa che tutto questo vive e viene eliminato, insieme.
| Entità | Ambito |
|---|---|
| Agenti | Appartengono a esattamente uno spazio di lavoro |
| Repository | Registrati in uno spazio di lavoro (vedi sotto) |
| Spazi, conversazioni e messaggi | Con ambito di spazio di lavoro |
| Ticket, progetti, piani, orchestrazioni | Con ambito di spazio di lavoro |
| Fatti di memoria, policy, domini, concessioni di accesso | Con ambito di spazio di lavoro |
| Esecuzioni, modelli e trigger delle pipeline | Con ambito di spazio di lavoro |
| Stato delle pull request, spazi di revisione, coorti di revisione | Con ambito di spazio di lavoro |
| Log di esecuzione degli agenti e memoria di lavoro | Con ambito di spazio di lavoro |
| Membri, inviti, concessioni per repository | Con ambito di spazio di lavoro |
| Simboli e archi del grafo del codice | Con ambito di repoId all’interno del file dello spazio di lavoro |
La maggior parte di queste righe porta una colonna workspaceId; file di database separati impongono l’isolamento. Vedi l’invariante di isolamento qui sotto.
I repository hanno come ambito lo spazio di lavoro; l’identità tra spazi di lavoro è data dal percorso
Sezione intitolata “I repository hanno come ambito lo spazio di lavoro; l’identità tra spazi di lavoro è data dal percorso”Una riga di repository vive nel file di database del proprio spazio di lavoro — il file è l’ambito, quindi la tabella non ha alcuna colonna workspaceId e nessuna tabella di join.
Aggiungi un repository a uno spazio di lavoro in Impostazioni → Spazio di lavoro → Repository. Registrare lo stesso checkout in due spazi di lavoro crea due righe indipendenti con due id; l’identità di un repository tra spazi di lavoro è data dal percorso nel filesystem (o da owner/name di GitHub), mai dall’id.
La registrazione è rigorosa: il percorso deve trovarsi dentro un git work tree, deve avere un remote origin e quel remote deve puntare a una forge supportata (github.com, gitlab.com o bitbucket.org). La forge viene letta dal remote e memorizzata sul repository, quindi uno spazio di lavoro può contenere repository di tutte e tre. Qualsiasi altro caso viene rifiutato. Il browser delle cartelle da cui scegli elenca il filesystem dell’host del server, limitato alla allow-list --repo-roots del server (che per impostazione predefinita è la home directory dell’utente del server).
Rimuovere un repository da uno spazio di lavoro elimina la riga del tutto e rimuove a cascata i suoi simboli del codice, gli archi, i file, i checkpoint dell’indice e le concessioni per membro. Il checkout su disco resta intatto.
Il grafo del codice vive nello stesso file, con chiave repoId, perché spazi di lavoro diversi possono avere lo stesso repository su branch diversi con codice diverso.
L’invariante di isolamento
Sezione intitolata “L’invariante di isolamento”Le fughe di dati tra spazi di lavoro sono trattate come bug. L’isolamento è strutturale e viene imposto a più livelli:
- Livello del database. Ogni spazio di lavoro ha il proprio file SQLite, distribuito da
WorkspaceDatabaseManager.of(workspaceId). Un database di spazio di lavoro non dichiarausers,workspacesné le tabelle di alcun altro spazio di lavoro, quindi una lettura tra spazi di lavoro non è un bug che puoi scrivere — non compila. I repository risolvono il proprio DAO a ogni chiamata invece di metterlo in cache e, poiché ogni metodo di repository richiede unworkspaceIdobbligatorio, lo spazio di lavoro non può mai essere dedotto né assunto come valore predefinito. - L’id dello spazio di lavoro è un segmento di percorso. Poiché l’id diventa un nome di directory, deve corrispondere a un pattern sicuro a segmento singolo (inizio alfanumerico, poi alfanumerici, punti, underscore, trattini; niente
..). In tutto il prodotto gli id sono UUID. Qualsiasi altro valore genera un’eccezione anziché essere sanificato — è la protezione contro il path traversal e fallisce in modo rumoroso invece di scrivere i dati di uno spazio di lavoro in un percorso scelto da qualcun altro. - Difesa in profondità. Le colonne
workspaceIdsono ancora lì e vengono ancora scritte. Ridondanti all’interno di un file, mantengono invariati i trigger del feed di sincronizzazione, gli indici FTS e le forme delle righe esistenti, e rendono un file auto-descrittivo quando lo si esamina da solo — cosa su cui si basano esportazione e importazione. - Livello di dominio. I servizi che mutano entità per id convalidano
entity.workspaceId == workspaceIdin un unico punto di controllo prima di procedere e generanoWorkspaceMismatchExceptionin caso di mancata corrispondenza — il fallimento rumoroso per un id arrivato dal contesto sbagliato. Negare in modo rumoroso conta: un no-op silenzioso nasconde il bug e procedere provoca una fuga. - Livello RPC. Il dispatcher RPC dei repository è il punto di controllo. Ogni operazione con ambito di spazio di lavoro deve portare
workspace_idnei propri argomenti — non esiste uno «spazio di lavoro corrente» per sessione che possa trapelare e un id sconosciuto viene rifiutato come non trovato prima che qualsiasi cosa apra un database, quindi un id errato non può materializzare unworkspace.dbfantasma. Le operazioni realmente globali (il feed di notizie, la coda della flotta) si dichiarano prive di ambito con una decisione esplicita e revisionata. Gli strumenti MCP seguono la stessa regola: qualsiasi strumento che tocchi dati con ambito di spazio di lavoro richiedeworkspace_id. - Il fan-out tra spazi di lavoro è enumerabile. Rispondere a una domanda su più spazi di lavoro significa aprire più database. Quel fan-out è confinato a un unico helper,
CrossWorkspaceQueries, così l’elenco completo delle cose che attraversano legittimamente il confine vive nei call site di un solo file invece di diffondersi dietro i commenti della documentazione. Quei call site sono: aggregazione a livello di server, riconciliatori di avvio, passaggi di conservazione e GC, backup, router di eventi — e la ricerca dell’appartenenza descritta sotto. - L’accesso basato solo sull’id non è sufficiente. Cercare un’entità tramite il suo UUID non dimostra che appartenga allo spazio di lavoro del chiamante. La separazione dei file rende irraggiungibile il file sbagliato; la convalida a livello di dominio intercetta un id errato che arriva attraverso un file legittimo.
È l’appartenenza, non la chiave di abbinamento, a concedere l’accesso
Sezione intitolata “È l’appartenenza, non la chiave di abbinamento, a concedere l’accesso”Possedere una credenziale di dispositivo autentica un dispositivo. Non autorizza nulla all’interno di uno spazio di lavoro. Ogni operazione con ambito di spazio di lavoro risolve il ruolo di appartenenza del chiamante e applica una soglia minima derivata dal tipo di operazione — una lettura richiede guest, una mutazione richiede member, un’operazione distruttiva richiede admin — e le operazioni che espongono contenuto dei repository verificano inoltre la concessione per repository del chiamante. Una chiave di abbinamento valida senza appartenenza ottiene Not a member of this workspace.
La conseguenza per l’isolamento è elegante: le righe di appartenenza vivono nel file dello spazio di lavoro stesso, quindi «chi c’è in questo spazio di lavoro?» è una normale lettura con ambito. La domanda inversa — «in quali spazi di lavoro mi trovo?» — è per definizione distribuita su più file, quindi è uno dei pochi fan-out CrossWorkspaceQueries autorizzati ed è ciò che esegue il selettore dello spazio di lavoro prima che ne sia stato scelto uno.
Vedi Multigiocatore per la scala dei ruoli e le concessioni per repository.
Il registro globale
Sezione intitolata “Il registro globale”Non tutto ha come ambito lo spazio di lavoro. Un secondo database, <dataDir>/global.db, contiene ciò che è realmente a livello di server:
- il registro degli spazi di lavoro, così il commutatore può elencare ogni spazio di lavoro senza aprire un solo file di spazio di lavoro — e così il nome, il logo, il proprietario, l’ordine manuale e il marcatore di eliminazione logica di uno spazio di lavoro vivono fuori dal file che descrivono;
- l’identità: utenti, preferenze utente e dispositivi abbinati, perché un essere umano è un unico utente in tutti gli spazi di lavoro e un dispositivo abbinato sopravvive all’eliminazione di uno spazio di lavoro;
- la coda della flotta (worker, job, registro di collocazione), il cui scheduler confronta l’intera coda con ogni worker a ogni tick;
- il feed di notizie, che è per utente (non per spazio di lavoro) — aggiungere un feed ti segue in ogni spazio di lavoro;
workspace_routes, l’indice pre-autenticazione che risponde alla domanda «quale spazio di lavoro possiede questa chiave?» per i punti di ingresso che arrivano con nient’altro che un segreto o un id opaco (un hash di invito, un token di webhook, un deep link) e nessuno spazio di lavoro. Un mancato riscontro lì è un non trovato; non esiste deliberatamente alcun fallback con scansione;- impostazioni e identità a livello di installazione: l’id dell’installazione, le impostazioni a livello di host, le connessioni SSO e le policy delle azioni gestite (un limite a livello di installazione che può solo stringere i guardrail degli spazi di lavoro).
L’avvio apre solo questo file. I file degli spazi di lavoro si aprono pigramente al primo accesso, quindi il costo di avvio resta costante indipendentemente da quanta cronologia accumulano gli spazi di lavoro, e il quick_check per file viene pagato al primo utilizzo anziché sul percorso verso il banner di pronto.
Un file di spazio di lavoro contiene solo un workspace_meta a riga singola per l’auto-identificazione (il suo id, l’installazione che l’ha creato, la versione dello schema con cui è stato creato, quando). Tutto ciò che descrive uno spazio di lavoro è nel registro.
Creare uno spazio di lavoro
Sezione intitolata “Creare uno spazio di lavoro”Creare uno spazio di lavoro fa più che inserire una riga nel registro. In un’unica operazione il server contrassegna l’utente che lo crea come ownerUserId, registra l’appartenenza con ruolo di proprietario di quell’utente e pubblica un evento di dominio WorkspaceCreated. I listener su quell’evento poi inizializzano, in modo idempotente e in background:
- un agente CEO più quattro specialisti —
qa,architect,engineer,librarian— con gli specialisti che riportano al CEO; - i modelli di pipeline integrati e i relativi trigger (i trigger a evento e manuali sono abilitati di serie; quelli pianificati sono opt-in, tranne la pulizia GC giornaliera dei worktree);
- le suite di eval iniziali.
Questo accade per ogni spazio di lavoro, non solo per il primo. Gli agenti inizializzati vengono creati senza adapter, quindi girano su Control Center (integrato) con il modello predefinito di Anthropic (claude-opus-4-8, a meno che tu non ne fissi un altro) finché non imposti un adapter e un modello per agente in Impostazioni → Spazio di lavoro → Agenti. Vedi Il modello degli agenti.
La scrittura dell’appartenenza del proprietario conta più di quanto sembri: senza di essa lo spazio di lavoro appena creato non avrebbe membri e ogni chiamata con ambito di spazio di lavoro che il creatore fa subito dopo verrebbe negata.
Eliminazione, esportazione, importazione
Sezione intitolata “Eliminazione, esportazione, importazione”Eliminare uno spazio di lavoro contrassegna la riga del registro e scollega la directory. Lo spazio di lavoro scompare da ogni elenco e da ogni ricerca. La riga del registro resta (eliminata logicamente), così l’id è ancora noto al backup e alla manutenzione, ma workspace.delete elimina immediatamente la directory di quello spazio di lavoro — database, worktree, file degli agenti e il resto — tramite WorkspaceDatabaseManager.dropAndClose. Solo il proprietario dello spazio di lavoro può eliminarlo.
Poiché uno spazio di lavoro è un unico file, passare uno spazio di lavoro da una parte all’altra è un’operazione su file anziché un dump tabella per tabella:
workspace.exportè un unicoVACUUM INTO— un’istantanea coerente e deframmentata, presa sotto una transazione di lettura mentre il server continua a servire.workspace.importadotta un file di questo tipo, sostituendo qualunque cosa lo spazio di lavoro di destinazione contenga in quel momento. Ilworkspace_metaintegrato consente a un’importazione di distinguere «il mio file, riadottato» da «un file di un’altra installazione» (quest’ultimo è consentito, ma registrato nei log anziché invisibile).server.backupNowcrea un’istantanea dell’intera installazione in una directory con marca temporale che rispecchia la directory dati attiva —manifest.json,global.dbe un<workspaceId>/workspace.dbper spazio di lavoro — quindi ripristinare significa ricopiarla indietro.server.listBackupsrilegge quella directory, dal più recente, con la dimensione e l’elenco degli spazi di lavoro di ogni istantanea. Un’istantanea il cui manifest è assente o che nomina file non presenti viene comunque segnalata, contrassegnata come incompleta — nasconderla è il modo in cui un operatore arriva a credere di avere un backup che non ha.
Le operazioni di backup si trovano in Impostazioni → Server → Backup e ripristino:
Esegui backup ora crea un’istantanea dell’installazione, l’elenco mostra quali
istantanee sono complete e consente al proprietario dell’installazione di
eliminarne una dopo conferma, e ogni spazio di lavoro offre esportazione e
importazione. La pipeline integrata Workspace backup prende solo il database
di quello spazio di lavoro con cadenza settimanale ed elimina i propri backup più
vecchi dell’età di conservazione configurata. Ripristinare uno spazio di lavoro
da un’istantanea dell’installazione equivale a puntare workspace.import sul
<workspaceId>/workspace.db dell’istantanea; ripristinare un’intera
installazione resta una ricopiatura della directory dell’istantanea a server
fermo.
Ciò che nessuna di esse trasporta è il resto della directory dello spazio di lavoro. Uno spazio di lavoro accumula più di un database — immagini incollate, file di skill e di agenti, credenziali di chat, worktree — ed esportazione e importazione spostano il file, quindi tutto ciò che gli sta accanto resta dov’è. Per le credenziali è deliberato e per i worktree è sacrificabile; per il resto è un limite da conoscere anziché una funzionalità. Il riferimento sul backup contiene la tabella completa.
Spostare un backup tra il server e il tuo dispositivo
Sezione intitolata “Spostare un backup tra il server e il tuo dispositivo”Tutte le operazioni precedenti parlano in termini di percorsi sul server, il che è una risposta completa solo quando il server è la tua macchina. Tre route HTTP firmate — /backup/workspace, /backup/snapshot e /backup/restore — trasportano i byte per ogni altra topologia, sulla stessa corsia autenticata già usata dal proxy dei media e dall’archivio delle immagini. Nella pagina questo si traduce in un pulsante Scarica accanto a ogni esportazione e a ogni istantanea (scegli tu dove finisce) e in Scegli un file e caricalo per ripristinare da un file che si trova sulla macchina che stai usando. Il caricamento è quello che funziona quando il server non è la tua macchina: workspace.import richiede che il file sia già sull’host, ed è così che ci arriva.
Vale la pena conoscere tre proprietà:
- Un download conia una copia nuova e il server non ne conserva nulla. Scaricare non significa «recuperare il file creato dal pulsante di esportazione» — esporta di nuovo, trasmette quella copia in streaming e la elimina. La risposta è
no-storeconAccept-Ranges: noneper lo stesso motivo: un intervallo ripreso unirebbe due esportazioni diverse in un unico file che sembra valido e non lo è. - Un caricamento viene trasmesso in streaming su disco, mai bufferizzato, su entrambe le estremità, e la copia in staging viene eliminata su ogni percorso in uscita — compreso un rifiuto. Un database di spazio di lavoro non è uno screenshot.
- Una connessione in relay non ha un’origine HTTP, quindi i controlli di trasferimento sono disabilitati lì con una nota che ne spiega il motivo. Tutto ciò che parla in termini di percorsi lato server continua a funzionare sulla connessione relay; solo le corsie dei byte richiedono una connessione diretta.
Più spazi di lavoro
Sezione intitolata “Più spazi di lavoro”Puoi eseguire più spazi di lavoro fianco a fianco. Ognuno opera in modo indipendente: gli agenti di uno spazio di lavoro non possono inviare messaggi agli agenti di un altro, non possono leggere la memoria di un altro spazio di lavoro e non possono vedere i suoi ticket. La risoluzione dei destinatari per la messaggistica da agente ad agente è esatta e non attraversa mai uno spazio di lavoro.
Non esiste una dashboard tra più spazi di lavoro. La superficie di analisi su /workspaces/:workspaceId/observability si iscrive a un feed globale delimitato delle esecuzioni recenti e lo restringe immediatamente allo spazio di lavoro attivo, quindi ciò che vedi lì è uno spazio di lavoro alla volta. Le uniche superfici realmente multi-spazio di lavoro che una persona tocca sono il selettore degli spazi di lavoro e il commutatore.
Un limite pratico che vale la pena conoscere: uno spazio di lavoro che qualcuno sta usando resta aperto — non c’è alcun LRU. Oltre 32 di questi il server avvisa anziché rifiutare, perché ogni file aperto mantiene un isolate in background e una cache di pagina. Il fan-out tra spazi di lavoro apre i file in modo transitorio e li chiude subito dopo se nessun altro li ha rivendicati.
Concetti correlati
Sezione intitolata “Concetti correlati”- Multigiocatore: appartenenza, ruoli e concessioni per repository — la metà accesso dell’isolamento
- Il modello degli agenti: gli agenti appartengono a esattamente uno spazio di lavoro
- Memoria e conoscenza: la memoria ha come ambito lo spazio di lavoro
- Architettura: dove si collocano i database nella separazione client/server
- Backup e ripristino: creare un’istantanea, spostare uno spazio di lavoro, conservare copie altrove
- Backup, esportazione e importazione: ogni operazione, route, campo del manifest e limite
- Aggiungi repo: registrare un checkout in uno spazio di lavoro