aller au contenu
FRFlo
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.

Terminal window
# 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
└── ty

7. 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 stack

Le 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.

Glissez pour déplacer, zoomez à la molette ou par pincement. Au clavier : flèches, + et −.