Espaces de travail et isolation
Qu’est-ce qu’un espace de travail ?
Section intitulée « Qu’est-ce qu’un espace de travail ? »Un espace de travail est le locataire d’isolation de premier niveau dans Control Center. Il regroupe des agents, des dépôts, des espaces, des tickets, de la mémoire, des pipelines et des membres dans un contexte délimité. Tout ce qui se produit à l’intérieur d’un espace de travail y reste.
Un espace de travail typique correspond à un projet ou à une base de code. Vous pouvez avoir des espaces de travail distincts pour « api-service », « frontend-v2 » et « infrastructure ».
Les données propres à l’espace de travail
Section intitulée « Les données propres à l’espace de travail »Les lignes d’un espace de travail résident dans le fichier SQLite propre à cet
espace, à l’emplacement <dataDir>/<workspaceId>/workspace.db. Chaque espace de
travail dispose d’un répertoire plutôt que d’un simple fichier, car un espace de
travail accumule plus qu’une base de données — des arbres de travail par
conversation, les fichiers d’agents et de skills, les identifiants de robots de
chat — et lui donner un dossier signifie que tout cela vit et est supprimé
ensemble.
| Entité | Portée |
|---|---|
| Agents | Appartiennent à exactement un espace de travail |
| Dépôts | Enregistrés dans un espace de travail (voir ci-dessous) |
| Espaces, conversations et messages | Limités à l’espace de travail |
| Tickets, projets, plans, orchestrations | Limités à l’espace de travail |
| Faits de mémoire, politiques, domaines, autorisations d’accès | Limités à l’espace de travail |
| Exécutions de pipeline, modèles, déclencheurs | Limités à l’espace de travail |
| État des pull requests, espaces de revue, cohortes de revue | Limités à l’espace de travail |
| Journaux d’exécution des agents et mémoire de travail | Limités à l’espace de travail |
| Membres, invitations, autorisations par dépôt | Limités à l’espace de travail |
| Symboles et arêtes du graphe de code | Limités par repoId dans le fichier de l’espace de travail |
La plupart de ces lignes portent une colonne workspaceId ; ce sont les
fichiers de base de données séparés qui font respecter l’isolation. Voir
l’invariant d’isolation ci-dessous.
Les dépôts sont limités à l’espace de travail ; l’identité entre espaces de travail repose sur le chemin
Section intitulée « Les dépôts sont limités à l’espace de travail ; l’identité entre espaces de travail repose sur le chemin »La ligne d’un dépôt vit dans le fichier de base de données propre à son espace de
travail — le fichier est la portée, si bien que la table ne comporte ni colonne
workspaceId ni table de jointure.
Vous ajoutez un dépôt à un espace de travail dans Paramètres → Espace de travail → Dépôts. Enregistrer la même copie de travail dans deux espaces de travail crée deux lignes indépendantes dotées de deux identifiants ; l’identité d’un dépôt d’un espace de travail à l’autre repose sur le chemin dans le système de fichiers (ou sur le couple propriétaire/nom GitHub), jamais sur l’identifiant.
L’enregistrement est strict : le chemin doit se trouver à l’intérieur d’un
arbre de travail git, disposer d’un dépôt distant origin, et ce dépôt distant
doit pointer vers une forge prise en charge (github.com, gitlab.com ou
bitbucket.org). La forge est lue depuis le dépôt distant et stockée sur le dépôt,
si bien qu’un même espace de travail peut contenir des dépôts issus des trois.
Tout le reste est refusé. Le navigateur de dossiers dans lequel vous effectuez
votre choix affiche le système de fichiers de l’hôte du serveur, limité à la
liste d’autorisation --repo-roots du serveur (qui vaut par défaut le répertoire
personnel de l’utilisateur du serveur).
Retirer un dépôt d’un espace de travail supprime la ligne purement et simplement et supprime en cascade ses symboles de code, ses arêtes, ses fichiers, ses points de contrôle d’index et ses autorisations par membre. La copie de travail sur le disque n’est pas touchée.
Le graphe de code vit dans le même fichier, indexé par repoId, car des espaces
de travail différents peuvent contenir le même dépôt sur des branches différentes
avec du code différent.
L’invariant d’isolation
Section intitulée « L’invariant d’isolation »Les fuites de données entre espaces de travail sont traitées comme des bugs. L’isolation est structurelle et imposée à plusieurs niveaux :
- Couche base de données. Chaque espace de travail possède son propre fichier
SQLite, distribué par
WorkspaceDatabaseManager.of(workspaceId). Une base de données d’espace de travail ne déclare niusers, niworkspaces, ni les tables d’aucun autre espace de travail : une lecture entre espaces de travail n’est donc pas un bug que vous pouvez écrire — elle ne compile pas. Les dépôts résolvent leur DAO à chaque appel plutôt que d’en mettre un en cache, et comme chaque méthode de dépôt exige unworkspaceId, l’espace de travail ne peut jamais être déduit ni remplacé par une valeur par défaut. - L’identifiant d’espace de travail est un segment de chemin. Comme
l’identifiant devient un nom de répertoire, il doit correspondre à un motif sûr
à segment unique (début alphanumérique, puis alphanumériques, points, traits de
soulignement, traits d’union ; pas de
..). Partout dans le produit, les identifiants sont des UUID. Toute autre valeur lève une exception au lieu d’être assainie — c’est là la garde contre la traversée de chemin, et elle échoue bruyamment plutôt que d’écrire les données d’un espace de travail dans un chemin choisi par quelqu’un d’autre. - Défense en profondeur. Les colonnes
workspaceIdsont toujours là et toujours écrites. Redondantes au sein d’un fichier, elles laissent inchangés les déclencheurs du flux de synchronisation, les index FTS et la forme existante des lignes, et elles rendent un fichier auto-descriptif lorsqu’on l’examine isolément — ce sur quoi reposent l’export et l’import. - Couche domaine. Les services qui modifient des entités par identifiant
valident
entity.workspaceId == workspaceIdà un point de passage unique avant de poursuivre et lèventWorkspaceMismatchExceptionen cas de non-concordance — l’échec bruyant pour un identifiant arrivé du mauvais contexte. Refuser bruyamment importe : un no-op silencieux masque le bug, et poursuivre laisse fuiter des données. - Couche RPC. Le répartiteur RPC des dépôts est le point de passage. Chaque
opération limitée à un espace de travail doit porter
workspace_iddans ses propres arguments — il n’existe aucun « espace de travail courant » par session qui pourrait fuiter, et un identifiant inconnu est refusé comme introuvable avant que quoi que ce soit n’ouvre une base de données, si bien qu’un mauvais identifiant ne peut pas matérialiser unworkspace.dbfantôme. Les opérations réellement globales (le fil d’actualités, la file de la flotte) se déclarent sans portée, par une décision explicite et revue. Les outils MCP suivent la même règle : tout outil qui touche des données limitées à un espace de travail exigeworkspace_id. - La diffusion entre espaces de travail est énumérable. Répondre à une
question portant sur plusieurs espaces de travail implique d’ouvrir plusieurs
bases de données. Cette diffusion est confinée à un seul utilitaire,
CrossWorkspaceQueries, de sorte que la liste complète de ce qui franchit légitimement la frontière tient dans les sites d’appel d’un seul fichier au lieu de se diluer derrière des commentaires de documentation. Ces sites d’appel sont : l’agrégation à l’échelle du serveur, les réconciliateurs de démarrage, les balayages de rétention et de GC, la sauvegarde, les routeurs d’événements — et la recherche d’appartenance ci-dessous. - L’accès par identifiant seul ne suffit pas. Retrouver une entité par son UUID ne prouve pas qu’elle appartient à l’espace de travail de l’appelant. La séparation des fichiers rend le mauvais fichier inatteignable ; la validation de la couche domaine intercepte un identifiant erroné qui arrive par un fichier légitime.
C’est l’appartenance, et non la clé d’appairage, qui accorde l’accès
Section intitulée « C’est l’appartenance, et non la clé d’appairage, qui accorde l’accès »Détenir un identifiant d’appareil authentifie un appareil. Cela n’autorise rien
à l’intérieur d’un espace de travail. Chaque opération limitée à un espace de
travail résout le rôle d’appartenance de l’appelant et applique un plancher
dérivé du type de l’opération — une lecture exige guest, une modification exige
member, une opération destructive exige admin — et les opérations qui exposent
le contenu d’un dépôt vérifient en plus l’autorisation par dépôt de l’appelant.
Une clé d’appairage valide sans appartenance reçoit
Not a member of this workspace.
La conséquence pour l’isolation est élégante : les lignes d’appartenance vivent
dans le fichier propre à l’espace de travail, si bien que « qui se trouve
dans cet espace de travail ? » est une lecture à portée ordinaire. La
question inverse — « dans quels espaces de travail suis-je ? » — est par
définition répartie sur plusieurs fichiers : c’est donc l’une des rares
diffusions CrossWorkspaceQueries autorisées, et c’est ce que le sélecteur
d’espace de travail exécute avant qu’un espace de travail ait été choisi.
Voir Multijoueur pour l’échelle des rôles et les autorisations par dépôt.
Le registre global
Section intitulée « Le registre global »Tout n’est pas limité à l’espace de travail. Une seconde base de données,
<dataDir>/global.db, contient ce qui est réellement global au serveur :
- le registre des espaces de travail, pour que le sélecteur d’espaces puisse lister chaque espace de travail sans ouvrir le moindre fichier d’espace de travail — et pour que le nom, le logo, le propriétaire, l’ordre manuel et le marqueur de suppression logique d’un espace de travail vivent en dehors du fichier qu’ils décrivent ;
- l’identité : les utilisateurs, les préférences utilisateur et les appareils appairés, car un humain correspond à un seul utilisateur dans tous les espaces de travail et un appareil appairé survit à la suppression d’un espace de travail ;
- la file de la flotte (workers, tâches, journal de placement), dont l’ordonnanceur confronte la file entière à chaque worker à chaque cycle ;
- le fil d’actualités, qui est propre à l’utilisateur (et non à l’espace de travail) — un fil ajouté vous suit dans tous les espaces de travail ;
workspace_routes, l’index d’avant-authentification qui répond à « quel espace de travail possède cette clé ? » pour les points d’entrée qui arrivent sans rien d’autre qu’un secret ou qu’un identifiant opaque (un hachage d’invitation, un jeton de webhook, un lien profond) et sans espace de travail. Une absence y donne un introuvable ; il n’existe délibérément aucun repli par balayage ;- les paramètres et l’identité à l’échelle de l’installation : l’identifiant de l’installation, les paramètres au niveau de l’hôte, les connexions SSO et les politiques d’action gérées (un verrou à l’échelle de l’installation qui ne peut que resserrer les garde-fous d’un espace de travail).
Le démarrage n’ouvre que ce fichier. Les fichiers d’espaces de travail s’ouvrent
paresseusement au premier accès, si bien que le coût de démarrage reste constant
quelle que soit l’histoire que les espaces de travail accumulent, et le
quick_check de chaque fichier est payé à la première utilisation plutôt que sur
le chemin menant à la bannière de disponibilité.
Un fichier d’espace de travail ne contient qu’une workspace_meta à une seule
ligne, pour s’auto-identifier (son identifiant, l’installation qui l’a créé, la
version de schéma avec laquelle il a été créé, et quand). Tout ce qui décrit un
espace de travail se trouve dans le registre.
Créer un espace de travail
Section intitulée « Créer un espace de travail »Créer un espace de travail ne se limite pas à insérer une ligne dans le registre.
En une seule opération, le serveur inscrit l’utilisateur créateur comme
ownerUserId, enregistre l’appartenance de cet utilisateur au rôle de
propriétaire et publie un événement de domaine WorkspaceCreated. Les écouteurs
de cet événement amorcent ensuite, de façon idempotente et en arrière-plan :
- un agent CEO et quatre spécialistes —
qa,architect,engineer,librarian— les spécialistes étant rattachés au CEO ; - les modèles de pipeline intégrés et leurs déclencheurs (les déclencheurs événementiels et manuels sont activés d’origine ; les déclencheurs planifiés sont à activer, sauf le balayage quotidien de GC des arbres de travail) ;
- les suites d’évaluation de démarrage.
Cela se produit pour chaque espace de travail, pas seulement le premier. Les
agents amorcés sont créés sans adaptateur : ils s’exécutent donc sur
Control Center (built-in) avec le modèle par défaut d’Anthropic
(claude-opus-4-8 sauf si vous en épinglez un autre) jusqu’à ce que vous
définissiez un adaptateur et un modèle pour chaque agent dans Paramètres → Espace
de travail → Agents. Voir Le modèle d’agent.
L’écriture de l’appartenance du propriétaire compte plus qu’il n’y paraît : sans elle, l’espace de travail tout juste créé n’aurait aucun membre et chaque appel limité à un espace de travail que ferait ensuite le créateur serait refusé.
Supprimer, exporter, importer
Section intitulée « Supprimer, exporter, importer »Supprimer un espace de travail marque la ligne du registre et dissocie le
répertoire. L’espace de travail disparaît de toutes les listes et de toutes les
recherches. La ligne du registre subsiste (supprimée logiquement) afin que
l’identifiant reste connu de la sauvegarde et de la maintenance, mais
workspace.delete supprime immédiatement le répertoire de cet espace de travail —
base de données, arbres de travail, fichiers d’agents et le reste — via
WorkspaceDatabaseManager.dropAndClose. Seul le propriétaire de l’espace de
travail peut le supprimer.
Parce qu’un espace de travail tient dans un seul fichier, transmettre un espace de travail est une opération sur fichier plutôt qu’un vidage table par table :
workspace.exportse réduit à un seulVACUUM INTO— un instantané cohérent et défragmenté, pris sous une transaction de lecture pendant que le serveur continue de servir.workspace.importadopte un tel fichier, en remplaçant tout ce que contient actuellement l’espace de travail cible. La tableworkspace_metaintégrée permet à un import de distinguer « mon propre fichier, ré-adopté » d’« un fichier venu d’une autre installation » (ce dernier cas est autorisé, mais journalisé plutôt qu’invisible).server.backupNowcapture l’installation entière dans un répertoire horodaté qui reflète le répertoire de données actif —manifest.json,global.dbet un<workspaceId>/workspace.dbpar espace de travail — si bien que restaurer revient à le recopier.server.listBackupsrelit ce répertoire, du plus récent au plus ancien, avec la taille et la liste des espaces de travail de chaque instantané. Un instantané dont le manifeste est absent ou qui nomme des fichiers inexistants est tout de même signalé, marqué comme incomplet — le masquer est la façon dont un opérateur finit par croire qu’il dispose d’une sauvegarde qu’il n’a pas.
Les opérations de sauvegarde se trouvent dans Paramètres → Serveur → Sauvegarde
et restauration : Sauvegarder maintenant prend un instantané de
l’installation, la liste indique quels instantanés sont complets et permet au
propriétaire de l’installation d’en supprimer un après confirmation, et chaque
espace de travail propose l’export et l’import. Le pipeline intégré
Workspace backup ne prend que la base de données de cet espace de travail,
selon une planification hebdomadaire, et supprime ses propres sauvegardes plus
anciennes que l’âge de rétention configuré. Restaurer un espace de travail à
partir d’un instantané de l’installation revient à pointer workspace.import sur
le <workspaceId>/workspace.db de l’instantané ; restaurer une installation
entière reste une recopie du répertoire d’instantanés, serveur arrêté.
Ce qu’aucune d’elles ne transporte, c’est le reste du répertoire de l’espace de travail. Un espace de travail accumule plus qu’une base de données — des images collées, des fichiers de skills et d’agents, des identifiants de chat, des arbres de travail — et l’export et l’import déplacent le fichier, si bien que tout ce qui l’entoure reste où il est. C’est délibéré pour les identifiants et jetable pour les arbres de travail ; pour le reste, c’est une limite à connaître plutôt qu’une fonctionnalité. La référence sur la sauvegarde contient le tableau complet.
Déplacer une sauvegarde entre le serveur et votre appareil
Section intitulée « Déplacer une sauvegarde entre le serveur et votre appareil »Toutes les opérations ci-dessus s’expriment en chemins sur le serveur, ce qui
n’est une réponse complète que lorsque le serveur est votre propre machine. Trois
routes HTTP signées — /backup/workspace, /backup/snapshot et /backup/restore —
transportent les octets pour toutes les autres topologies, sur la même voie
authentifiée que celle déjà utilisée par le proxy multimédia et le magasin
d’images. Dans la page, il s’agit d’un bouton Télécharger à côté de chaque
export et de chaque instantané (vous choisissez où il atterrit), et de Choisir
un fichier et l’envoyer pour restaurer à partir d’un fichier présent sur la
machine que vous utilisez. C’est l’envoi qui fonctionne lorsque le serveur n’est
pas votre machine : workspace.import exige que le fichier soit déjà sur
l’hôte, et c’est ainsi qu’il y arrive.
Trois propriétés méritent d’être connues :
- Un téléchargement fabrique une copie fraîche et le serveur n’en garde rien.
Télécharger ne revient pas à « récupérer le fichier produit par le bouton
d’export » — le serveur exporte de nouveau, diffuse le résultat, puis le
supprime. La réponse est
no-storeavecAccept-Ranges: nonepour la même raison : une plage reprise recollerait deux exports différents en un fichier qui paraît valide et ne l’est pas. - Un envoi est diffusé en flux vers le disque, jamais mis en mémoire tampon, aux deux extrémités, et la copie mise en attente est supprimée sur tous les chemins de sortie — y compris en cas de refus. Une base de données d’espace de travail n’est pas une capture d’écran.
- Une connexion relayée n’a pas d’origine HTTP, c’est pourquoi les commandes de transfert y sont désactivées, avec une note qui en explique la raison. Tout ce qui s’exprime en chemins côté serveur continue de fonctionner via le relais ; seules les voies d’octets exigent une connexion directe.
Plusieurs espaces de travail
Section intitulée « Plusieurs espaces de travail »Vous pouvez exécuter plusieurs espaces de travail côte à côte. Chacun fonctionne indépendamment : les agents d’un espace de travail ne peuvent pas envoyer de message aux agents d’un autre, ne peuvent pas lire la mémoire d’un autre espace de travail et ne peuvent pas voir ses tickets. La résolution des destinataires pour la messagerie d’agent à agent est exacte et ne franchit jamais un espace de travail.
Il n’existe aucun tableau de bord inter-espaces. La surface d’analyse à l’adresse
/workspaces/:workspaceId/observability s’abonne à un flux global borné des
exécutions récentes et le restreint immédiatement à l’espace de travail actif, si
bien que ce que vous y voyez concerne un seul espace de travail à la fois. Les
seules surfaces réellement multi-espaces qu’une personne côtoie sont le sélecteur
d’espace de travail et le sélecteur d’espaces.
Une limite pratique à connaître : un espace de travail que quelqu’un utilise reste ouvert — il n’y a pas de LRU. Au-delà de 32 espaces de travail ouverts, le serveur avertit plutôt que de refuser, car chaque fichier ouvert retient un isolat d’arrière-plan et un cache de pages. La diffusion entre espaces de travail ouvre des fichiers de façon transitoire et les referme ensuite si rien d’autre ne les a réclamés.
Concepts connexes
Section intitulée « Concepts connexes »- Multijoueur : l’appartenance, les rôles et les autorisations par dépôt — la moitié « accès » de l’isolation
- Le modèle d’agent : les agents appartiennent à exactement un espace de travail
- Mémoire et connaissances : la mémoire est limitée à l’espace de travail
- Architecture : où se situent les bases de données dans la séparation client/serveur
- Sauvegarder et restaurer : prendre un instantané, déplacer un espace de travail, conserver des copies ailleurs
- Sauvegarde, export et import : chaque opération, route, champ de manifeste et limite
- Ajouter des dépôts : enregistrer une copie de travail dans un espace de travail