T3 Code : Workflows de Dév en Full Remote et Multi-Agents
/ 17 min de lecture
Mis à jour :Table des matières
Les agents de code autonomes ont besoin d’un environnement stable, de retours de compilation rapides et d’un accès soigneusement limité aux ressources. J’ai fait évoluer mon environnement T3 Code : l’image personnalisée a laissé place à dizys/t3code-docker, les anciens modules de secrets ont été réunis dans t3-vault, et les agents passent par des gateways d’IA privées.
Architecture d’ensemble
Une architecture distante n’est pas seulement un serveur accessible par le réseau. Je sépare les rôles : le navigateur et VS Code sont des clients, la frontière Zero Trust contrôle l’accès, le conteneur héberge T3 Code et ses agents, et les stockages persistants survivent au remplacement de l’image.
Le schéma présente les flux fonctionnels sans exposer d’hôtes ni de ports d’infrastructure. Les références de ports internes sont volontairement génériques (90xx). Les flèches décrivent des échanges, pas une autorisation implicite : chaque chemin doit rester limité au service concerné.
flowchart TB
subgraph Clients["Clients distants"]
Browser["Navigateur web"]
VSCode["VS Code Remote SSH"]
User["Utilisateur"]
end
subgraph Edge["Frontière Zero Trust"]
Access["Cloudflare Access et SSO OIDC"]
Tunnel["Tunnel vers <service-host>"]
WARP["Maillage privé WARP"]
end
subgraph Container["Conteneur t3code"]
SSHD["sshd, compte node, clés Ed25519"]
Server["t3 serve, interface web et API workspace"]
subgraph Agents["Agents"]
Codex["Codex CLI et codex-lsp"]
Pi["Pi Coding Agent 1.4.0"]
OpenCode["OpenCode et autres agents"]
end
subgraph LSP["LSPs partagés"]
Lugo["Lugo, Lua 5.4 et FiveM"]
TS["typescript-language-server"]
Go["gopls"]
Ty["ty pour Python"]
end
DockerClient["Client Docker en lecture seule"]
end
subgraph Services["Services internes"]
Vault["t3-vault"]
Gateways["Gateways IA privées"]
Proxy["docker-socket-proxy, POST=0"]
Model["Fournisseurs de modèles"]
Stack["Services de la stack"]
end
subgraph Volumes["Persistance"]
Workspace["/workspace : dépôts et worktrees"]
UserData["/data/userdata : préférences"]
end
User -->|"Pilote la session"| Browser
Browser -->|"HTTPS protégé"| Access
Access -->|"Transmet la session autorisée"| Tunnel
Tunnel -->|"Accède à l'interface web"| Server
User -->|"Ouvre une session IDE"| VSCode
VSCode -->|"Connexion privée 90xx"| WARP
WARP -->|"Atteint le service SSH"| SSHD
SSHD -->|"Ouvre le workspace distant"| Workspace
Server -->|"Lance les agents"| Codex
Server -->|"Lance les agents"| Pi
Server -->|"Lance les agents"| OpenCode
Codex -->|"Demande des diagnostics"| LSP
Pi -->|"Demande des diagnostics"| LSP
LSP -->|"Analyse le code du dépôt"| Workspace
Codex -->|"Demande un secret à l'utilisateur"| Vault
Pi -->|"Demande un secret à l'utilisateur"| Vault
Codex -->|"Envoie une requête d'inférence"| Gateways
Pi -->|"Envoie une requête d'inférence"| Gateways
OpenCode -->|"Envoie une requête d'inférence"| Gateways
Gateways -->|"Relaye selon les quotas"| Model
DockerClient -->|"Inspecte via API restreinte"| Proxy
Proxy -->|"Expose les opérations autorisées"| Stack
Server -->|"Persiste les préférences"| UserData
Server -->|"Référence les dépôts actifs"| Workspace
Cette séparation aide à raisonner sur les pannes. La perte d’un conteneur ne doit pas supprimer le code ni les préférences persistantes ; l’indisponibilité d’une gateway ne doit pas empêcher l’accès aux worktrees locaux ; et l’accès au proxy Docker ne doit pas donner de capacité d’administration.
1. Socle conteneurisé officiel et runtimes
Pourquoi abandonner l’image personnalisée
Une image modifiée localement accumule des écarts invisibles : une dépendance peut être installée à la main, une autre compilée à une version différente, et une reconstruction peut ne plus reproduire le conteneur en cours. J’utilise maintenant l’image publiée dizys/t3code-docker, distribuée sur Docker Hub et GitHub Container Registry.
Le choix d’une image publiée déplace le travail plutôt qu’il ne le supprime. Je maintiens une configuration de déploiement explicite et je sépare l’image, les données utilisateur et les dépôts. Ainsi, une mise à jour de l’image ne sert pas de mécanisme de sauvegarde et un rollback ne dépend pas du contenu mutable du conteneur.
| Besoin | Emplacement ou mécanisme | Arbitrage |
|---|---|---|
| Image applicative | dizys/t3code-docker |
Suivre un artefact publié plutôt qu’une image locale dérivante |
| Dépôts et worktrees | /workspace |
Données de travail distinctes de l’image |
| Préférences utilisateur | /data/userdata |
État persistant distinct des dépôts |
| Outils additionnels | T3_PREINSTALL |
Installation déclarative au boot, avec un coût de démarrage |
Runtimes injectés au démarrage
Le runtime doit répondre aux besoins des dépôts sans transformer la base en image monolithique. La variable T3_PREINSTALL injecte au démarrage Bun, uv, Wrangler et Python 3, en complément de Go, Git, ripgrep et des outils déjà disponibles.
uv fournit la gestion des environnements et dépendances Python ; Wrangler sert aux projets Cloudflare. Installer ces outils au boot rend leur présence reproductible dans la configuration mais peut allonger le démarrage et dépendre de la disponibilité des sources de paquets. Pour limiter cet effet, je garde la liste utile aux projets réellement hébergés dans cet espace.
# Extrait indicatif de la configuration du conteneur.# Les valeurs exactes de T3_PREINSTALL sont propres au déploiement.T3_PREINSTALL="bun uv wrangler python3"Cette notation décrit les outils requis et n’est pas une commande universelle d’installation : la syntaxe concrète est celle attendue par l’image publiée et doit rester alignée sur sa documentation. Le code et les préférences demeurent dans leurs montages persistants, pas dans le processus d’installation.
Outils CLI pour l’exploration et la maintenance
Les agents sont plus utiles quand ils peuvent examiner une base de code de façon ciblée plutôt que lire tous les fichiers séquentiellement. J’ai conservé une boîte à outils CLI orientée recherche, transformation syntaxique et maintenance :
| Outil | Utilité dans le workflow | Pourquoi le garder |
|---|---|---|
ripgrep (rg) |
Recherche texte rapide | Réduit le bruit lors de l’exploration |
fd |
Recherche de fichiers | Complète la recherche textuelle par les chemins |
fzf |
Filtrage interactif | Accélère la navigation dans les résultats |
ast-grep |
Recherche et réécriture syntaxiques | Évite les remplacements textuels fragiles |
jq et yq |
Lecture et transformation JSON/YAML | Inspecte configs sans scripts ad hoc |
moreutils (sponge) |
Réécriture contrôlée de fichiers | Évite les pièges de redirection sur place |
gh |
Opérations GitHub en CLI | Relie le travail local aux PR et issues |
shellcheck et shfmt |
Analyse et formatage shell | Repère erreurs et dérives de style |
La présence d’un outil ne constitue pas une autorisation supplémentaire. Les droits de GitHub, les montages et les capacités réseau doivent rester les plus étroits possibles. L’image reproductible et les volumes séparés rendent les mises à jour plus prévisibles sans confondre disponibilité des outils et privilèges.
2. Vault unifié et gestion sécurisée des secrets
Un seul centre pour les identifiants
Des modules fragmentés obligent à maintenir plusieurs modèles d’accès et plusieurs comportements de demande. J’ai remplacé les anciens modules t3-ssh et t3-api par t3-vault, centre unifié des identifiants consommés par le workflow.
Le Vault gère onze familles d’identifiants. Les exemples utiles ici comprennent les clés SSH, les tokens cloud AWS et GCP, les tokens Kubernetes, les fichiers de configuration sensibles et les clés d’API. Cette diversité évite de traiter tout secret comme une simple chaîne, notamment lorsque l’intégration attend un fichier ou un type de credential précis.
| Famille représentative | Exemple d’usage | Précaution de conception |
|---|---|---|
| Clés SSH | Authentification à un dépôt ou service | Ne pas imprimer la clé dans les sorties |
| Tokens cloud AWS/GCP | Accès à une API cloud | Limiter les permissions et la durée d’usage |
| Tokens Kubernetes | Opérations sur un cluster autorisé | Restreindre contexte et portée |
| Fichiers de configuration sensibles | Config consommée par un outil | Transmettre comme fichier, pas comme texte de conversation |
| Clés d’API | Appel à un service | Centraliser la rotation et éviter les clés par agent |
La liste des exemples n’est pas un inventaire exhaustif des onze catégories. Elle illustre surtout pourquoi un gestionnaire unique doit préserver le type et le mode de consommation du secret.
Demande interactive sans fuite dans l’historique
Un agent ne devrait pas demander à l’utilisateur de coller un mot de passe dans le fil de discussion. Lorsqu’il identifie un identifiant requis, il sollicite T3 Code ; l’interface affiche la carte de secret native, l’utilisateur fournit l’accès et le mécanisme du Vault remet à l’agent l’accès autorisé sans injecter la valeur dans l’historique de conversation.
sequenceDiagram
actor User as Utilisateur
participant Agent as Agent autonome
participant T3 as Interface T3 Code
participant Vault as t3-vault
Agent->>T3: Sollicite le type d'identifiant requis
T3-->>User: Affiche la carte de secret native
User->>T3: Autorise et fournit le secret
T3->>Vault: Résout l'identifiant demandé
Vault-->>Agent: Fournit l'accès via le canal prévu
Note over Agent,Vault: La valeur secrète n'est pas écrite dans l'historique de conversation
Cette architecture réduit les copies accidentelles, mais ne rend pas un agent digne de confiance par défaut. Je limite les identifiants accessibles à la tâche, je vérifie les opérations demandées et je traite les logs et sorties d’outils comme des surfaces à protéger. Le Vault sépare le secret de la conversation ; il ne remplace ni le contrôle d’accès ni la revue des actions.
3. Passerelles d’IA privées et quotas
Routage depuis Setup → Gateways
Connecter chaque agent directement à chaque fournisseur multiplie les clés, la configuration et les chemins de facturation. Je configure Setup → Gateways pour router Pi, OpenCode et les autres agents vers des gateways privées dédiées, accessibles sur le réseau interne Zero Trust : CLIProxyAPI, LiteLLM ou OpenRouter.
Le schéma de routage sépare les agents du fournisseur final. L’agent parle à la gateway ; la gateway applique le chemin configuré et relaie la requête. Cette couche rend le changement de fournisseur moins intrusif et permet une politique de quota centralisée, mais elle devient aussi une dépendance de disponibilité et une frontière à surveiller.
Agent (Pi / OpenCode / autre) -> Gateway privée choisie dans Setup → Gateways -> Fournisseur de modèle configuréJe ne publie volontairement aucun nom d’hôte, endpoint ou port réel : la gateway est joignable uniquement au sein du réseau privé et les ports internes utilisent la notation générique 90xx.
Clés et limites d’usage
Les clés d’inférence sont conservées dans le Vault plutôt que copiées dans chaque configuration d’agent. Dans Usage → Limits, je définis des quotas et limites stricts par fournisseur. Cette séparation répond à deux problèmes : réduire le nombre de copies de credentials et empêcher qu’une boucle d’agent ne consomme indéfiniment le budget d’un fournisseur.
| Couche | Responsabilité | Contrôle attendu |
|---|---|---|
| Agent | Envoie une requête compatible | Modèle et tâche explicitement choisis |
| Gateway | Route et relaie la requête | Accès réseau privé et configuration surveillée |
| Vault | Fournit les clés nécessaires | Pas de clé en clair dans l’historique |
Usage → Limits |
Encadre la consommation | Quotas distincts par fournisseur |
Un quota peut interrompre un travail long ; c’est un compromis préférable à une consommation sans borne. Je prévois donc des limites adaptées aux tâches et une vérification du statut de la gateway avant d’attribuer un échec au modèle ou au code.
4. Double accès distant et durcissement Zero Trust
Web UI et session VS Code
Le navigateur est pratique pour consulter une tâche et piloter une session sans préparer un client local. Pour les longues sessions d’édition, VS Code Remote SSH offre un IDE local relié à l’environnement distant. Je conserve les deux chemins et leur réconciliation native afin qu’ils aboutissent au même espace de travail plutôt qu’à des états divergents.
L’interface web passe par Cloudflare Access et une authentification SSO OIDC. La session VS Code Remote SSH rejoint le conteneur via le maillage privé WARP. Aucun port d’infrastructure précis n’est publié dans cet article ; la notation 90xx représente uniquement la plage interne générique.
flowchart LR
User["Développeur distant"] -->|"Navigue vers l'interface"| Browser["Navigateur moderne"]
Browser -->|"Authentification SSO OIDC"| Access["Cloudflare Access"]
Access -->|"Session autorisée"| Web["T3 Code Web UI"]
User -->|"Ouvre VS Code Remote SSH"| VSCode["Client VS Code"]
VSCode -->|"Connexion privée 90xx"| WARP["Maillage WARP"]
WARP -->|"Authentifie par clé Ed25519"| SSH["sshd du conteneur, utilisateur node"]
Web -->|"Réconcilie la session"| Workspace["Workspace T3 Code"]
SSH -->|"Ouvre le même workspace"| Workspace
Pour SSH, seul l’utilisateur non privilégié node est utilisé et l’authentification se fait par clés Ed25519. Le réseau privé réduit l’exposition, tandis que la clé évite un mot de passe réutilisable. Ces contrôles ne dispensent pas de protéger les clés clientes ni de révoquer celles qui ne sont plus nécessaires.
Installation, cookies et en-têtes proxy
Le protocole d’installation est une frontière de confiance : la clé setup-key sert à cette étape et ne doit pas être héritée par les sous-processus des agents. Son non-héritage réduit le risque qu’un processus lancé par un agent puisse la lire depuis son environnement.
Les cookies sont protégés pour éviter une réutilisation triviale de la session. Lorsque T3 Code est placé derrière un proxy de confiance, T3_TRUST_PROXY_HEADERS=1 active la prise en compte des en-têtes proxy. Cette option n’est sûre que si le proxy amont retire ou remplace les en-têtes fournis par le client et si l’accès direct au service est contrôlé. Sans ces conditions, un client pourrait falsifier les informations de transport ou d’origine que l’application croit fiables.
| Contrôle | Risque ciblé | Dépendance opérationnelle |
|---|---|---|
Non-héritage de setup-key |
Lecture de la clé par un sous-processus | Vérifier l’environnement des agents |
| Cookies protégés | Rejeu ou exposition de session | Configuration correcte du serveur et du navigateur |
T3_TRUST_PROXY_HEADERS=1 |
Interprétation erronée des informations proxy | Proxy amont de confiance et chemin direct fermé |
| WARP et clé Ed25519 | Accès SSH non autorisé | Gestion des appareils enrôlés et des clés |
5. Moteur multi-agents : Codex et Pi
Les agents n’ont pas tous le même rôle. Utiliser un seul profil pour une correction locale et une exploration d’architecture est possible, mais oblige à choisir un compromis entre rapidité, planification et délégation. Je garde Codex CLI et Pi Coding Agent côte à côte, et les assigne selon la forme du travail.
| Critère | Codex CLI | Pi Coding Agent 1.4.0 |
|---|---|---|
| Usage privilégié | Refactorings ciblés, corrections atomiques, tests | Exploration d’architecture et tâches multi-étapes |
| Boucle de retour | codex-lsp transmet les diagnostics des LSPs |
Peut déléguer l’analyse à des sous-agents |
| Parallélisme | Tâche focalisée dans un worktree dédié | Sous-agents Explore et Plan en tâche de fond |
| Point de vigilance | Valider les changements et les diagnostics | Coordonner les sorties et contrôler le périmètre |
Codex CLI et codex-lsp
Pour un changement circonscrit, Codex CLI peut examiner le code, modifier une zone et lancer les vérifications pertinentes. Le plugin codex-lsp fournit des diagnostics en direct depuis les serveurs de langage. Le modèle peut ainsi réagir à des erreurs de type ou de syntaxe pendant l’édition, au lieu de découvrir tous les problèmes après une longue phase de tests.
Cette boucle ne remplace pas les tests. Un diagnostic LSP ne prouve ni le comportement fonctionnel ni la sûreté d’une migration ; il réduit seulement le délai avant de détecter certaines erreurs statiques.
Pi et sous-agents Explore / Plan
Pour une question transversale, Pi Coding Agent 1.4.0 peut lancer des sous-agents Explore et Plan en tâche de fond. L’un peut cartographier les points d’entrée pendant qu’un autre examine les contraintes ou propose une séquence de changements. Le fil principal conserve la synthèse et décide des modifications.
La délégation augmente le débit d’exploration, mais également le volume de résultats à vérifier. Je demande des tâches d’analyse bornées, j’évite que plusieurs agents écrivent dans le même worktree et je fais converger les propositions avant l’implémentation. Chaque agent utilise les gateways configurées et ne reçoit que les secrets explicitement demandés.
6. Serveurs de langage partagés
Un agent sans serveur de langage peut inventer une signature, manquer un import ou ne découvrir une erreur qu’au moment du build. Pour réduire ce délai, Codex et Pi partagent les LSPs installés sous /home/node/.local/opt/t3code-lsp.
| Langage | Serveur | Rôle et spécificités |
|---|---|---|
| Lua 5.4 / FiveM | Lugo | Serveur Go avec prise en compte des manifests fxmanifest.lua, analyse d’arité des événements réseau et frontières de confiance (trust boundary) |
| TypeScript / JavaScript | typescript-language-server |
Navigation et diagnostics TypeScript/JavaScript, en complément des runtimes du workspace |
| Go | gopls |
Diagnostics, navigation et informations de types pour les projets Go |
| Python | ty |
Analyse statique et vérification de types Python |
Lugo est particulièrement utile dans les projets FiveM : le manifest indique les ressources et fichiers pertinents, tandis que l’analyse des événements réseau aide à repérer les désaccords entre appels et gestionnaires. Les diagnostics de frontière de confiance apportent un signal supplémentaire, mais ne constituent pas un audit de sécurité complet.
Le répertoire commun réduit la duplication des binaires et maintient les deux agents sur des outils cohérents. En contrepartie, une mise à jour d’un serveur peut affecter tous les consommateurs : je contrôle donc les versions et vérifie les diagnostics après changement. LSP et build ont des objectifs différents ; les tests et le compilateur restent nécessaires.
/home/node/.local/opt/t3code-lsp/├── lugo├── typescript-language-server├── gopls└── ty7. Isolation absolue par Git worktrees
Pourquoi bannir le travail direct sur develop
Une branche partagée est un espace mutable unique. Si un agent change des fichiers pendant que je teste, il peut écraser un travail en cours, modifier l’index ou créer un état impossible à attribuer. Travailler directement sur develop expose aussi la branche de référence à des modifications incomplètes.
Je bannis donc l’édition directe de develop par les agents. Chaque tâche s’exécute dans un worktree distinct créé depuis la référence distante voulue. Le worktree partage la base Git, mais possède son propre répertoire et son propre état de travail. Cette séparation est plus légère qu’un clone indépendant tout en isolant les modifications non commitées.
workspaceStrategy: { type: "worktree", baseRef: "origin/develop"}Cycle de vie d’une tâche isolée
Le cycle commence par actualiser la référence distante, crée un worktree dédié, attribue ce chemin à l’agent et vérifie le résultat avant publication. Une fois la tâche terminée, le worktree temporaire est retiré ; la branche poussée conserve les commits pour la revue.
sequenceDiagram
autonumber
actor Dev as Développeur
participant T3 as T3 Code
participant Git as Dépôt sous /workspace
participant Agent as Codex ou Pi
Dev->>T3: Créer une tâche dans un worktree
T3->>Git: git fetch origin develop
T3->>Git: git worktree add ../wt-task origin/develop
Note over Git: Répertoire dédié, base Git partagée, modifications séparées
T3->>Agent: Attribuer le chemin du worktree
loop Implémentation et vérification
Agent->>Agent: Modifier, consulter LSP et lancer les tests utiles
end
Agent->>Git: Créer un commit sur la branche de tâche
Agent->>Git: Pousser la branche pour revue
T3->>Git: git worktree remove ../wt-task
T3-->>Dev: Worktree retiré, branche disponible pour la PR
Ce que l’isolation garantit — et ne garantit pas
| Bénéfice | Mécanisme | Limite à garder en tête |
|---|---|---|
| Parallélisme | Un répertoire par tâche | Deux tâches peuvent encore modifier la même logique dans des branches séparées |
| Base propre | Départ depuis origin/develop actualisé |
Il faut récupérer la bonne référence avant le démarrage |
| Pas de pollution de l’arbre courant | L’agent n’écrit pas dans le workspace actif du développeur | Les changements doivent être revus et intégrés |
| Nettoyage | Retrait du worktree après publication | Un worktree avec changements non commités nécessite une décision explicite |
Un worktree ne résout pas les conflits conceptuels : deux agents peuvent proposer des implémentations incompatibles. Il rend seulement les changements isolables, comparables et réversibles. La revue de diff et les tests restent le passage obligatoire avant fusion.
8. Diagnostic Docker sécurisé de la stack
Pour diagnostiquer des services, un agent peut avoir besoin d’observer leur état sans obtenir les privilèges d’administration du démon Docker. Je passe par docker-socket-proxy, configuré en lecture seule avec POST=0, afin de limiter les opérations exposées.
T3 Code -> client Docker -> docker-socket-proxy (POST=0) -> services de la stackLe proxy est une réduction de surface, pas une frontière magique. Je ne monte pas le socket Docker brut dans les agents et je limite les opérations accessibles au diagnostic nécessaire. Une API en lecture seule peut tout de même révéler des métadonnées opérationnelles ; les réponses doivent être traitées comme des informations internes et ne pas être copiées dans une sortie publique.
Cette approche permet à l’agent d’aider à examiner l’état des services tout en séparant observation et administration. Elle ne remplace ni les journaux centralisés ni une procédure de gestion d’incident, et n’autorise pas les actions de modification de la stack.
Conclusion
Cette évolution conserve la substance du workflow distant tout en ajoutant les nouvelles frontières de sécurité et d’exploitation. dizys/t3code-docker fournit l’image publiée ; /workspace et /data/userdata séparent dépôts et préférences ; T3_PREINSTALL complète les runtimes ; t3-vault centralise les identifiants ; les gateways privées appliquent un routage et des limites.
Codex CLI et Pi répondent à des tâches différentes, mais partagent les quatre LSPs et travaillent dans des worktrees isolés. Le navigateur protégé et VS Code Remote SSH offrent des accès complémentaires, tandis que le protocole d’installation, les cookies, le proxy de confiance et le proxy Docker limitent les surfaces d’exposition.
Je privilégie des frontières explicites plutôt qu’une promesse d’autonomie sans risque : chaque agent reçoit un workspace, une gateway et des secrets nécessaires à sa tâche, puis ses changements sont relus avant intégration. L’image publiée et les composants open source sont identifiables par leurs noms, notamment dizys/t3code-docker ; les dépôts publics correspondants constituent le point de départ pour consulter leur code et leur documentation.