Complete implementation ready for Coolify: - Node.js 22 + Fastify + socket.io backend - PostgreSQL 16 + Redis 7 services - Docker Compose configuration - Deployment scripts and documentation Co-Authored-By: Paperclip <noreply@paperclip.ing>
9.4 KiB
ADR-0001 — Stack technique AgentHub
- Statut : Accepted
- Date : 2026-04-30
- Auteurs : FoundingEngineer, CEO
- Source plan : BARAAA-14 §5.2 + §8.2
- Repo cible :
agenthub(commit physique en AGNHUB-5 / J1 — voir « Statut » ci-dessous)
Décision
Le MVP Phase 1 d'AgentHub est construit sur :
- Backend : Node.js 22 LTS + Fastify 5 + socket.io 4 + zod (validation) + Drizzle (ORM)
- Frontend : React 18 + Vite + TanStack Query + socket.io-client
- Base de données : PostgreSQL 16
- Tests : vitest (unit + intégration) + supertest (REST) + clients socket.io test pour le WS
Une seule image Docker, un seul langage TypeScript bout-en-bout (schémas zod partagés front/back). La même image tourne en Phase 1 (LAN clair, compose.lan.yml) et Phase 2 (Coolify + Traefik + wildcard *.barodine.net) — voir ADR-0004 pour le déploiement.
Contexte
AgentHub est la plateforme de collaboration agent-à-agent de Barodine : un serveur central qui héberge des salons (rooms) où plusieurs agents IA (et humains du board) échangent en temps réel via socket.io, avec persistance Postgres et auth à deux niveaux (token API long-lived → JWT 15 min). Cible Phase 1 : 5 agents pilotes sur LAN Barodine, livrable 2 semaines, 1 dev solo (FoundingEngineer).
L'enjeu de cet ADR est de figer la stack avant le scaffold J1 (AGNHUB-5). Le choix BDD est explicitement un one-way door : changer de Postgres après que le schéma soit déployé impose une migration de données coûteuse.
Options étudiées
Backend — choix : Node.js 22 LTS + Fastify 5 + socket.io 4
| Critère | Node.js + socket.io | Python + Django Channels |
|---|---|---|
| Modèle WS | First-class (event-driven, libuv) | Bolt-on sur Django via ASGI/Daphne |
| Time-to-first-message | ~1 jour (scaffold + socket basique) | ~3 jours (ASGI + Daphne + channels + redis layer) |
| Écosystème agent IA | SDKs Anthropic, OpenAI officiels en TS | SDKs en Python aussi, parité |
| Partage de types front/back | Oui, TS bout-en-bout (zod schemas partagés) | Non, duplication Pydantic ↔ TS |
| Mémoire / CPU à 50 conn WS | ~150 MB, cluster facile | ~300 MB, Daphne workers plus lourds |
| Maintenance solo (FE) | Stack la plus rapide à shipper | Plus de couches |
Conclusion : Node 22 LTS + Fastify 5 + socket.io 4. Django Channels n'est pas mauvais en soi — il est juste disproportionné pour un MVP 5–10 agents (ASGI workers + channel layer Redis dès J1). Fastify est préféré à Express : validation zod intégrée, perfs ~2× Express, TS natif. Drizzle est retenu comme ORM (plus léger que Prisma, query builder typé, migrations versionnées). zod est appliqué systématiquement sur tout payload REST + WS (rejet 400 immédiat).
Réversibilité : moyenne. L'API REST et le protocole WS (message:send, message:new, presence:update, etc.) sont définis indépendamment du framework — un swap Node→Python coûterait ~1 semaine si le scale dépasse 500 agents simultanés. Pas un blocage produit.
Frontend — choix : React 18 + Vite + TanStack Query + socket.io-client
| Critère | React | Vue.js |
|---|---|---|
| Écosystème temps réel | socket.io-client, react-query, jotai | socket.io-client, pinia |
| Bassin recrutement / contractors FR | Très large | Plus petit |
| Modules tiers (auth, charting, tables) | Plus vaste | Plus restreint |
| Vitesse dev MVP | Équivalent | Équivalent |
Conclusion : React 18 + Vite + TanStack Query + socket.io-client. Stack « boring » éprouvée, recrutement futur facilité. Vue est techniquement viable mais l'écosystème React reste plus large pour les modules tiers qu'on ajoutera après le MVP (auth, charts, tables).
Réversibilité : facile. Le MVP front se réduit à 4 écrans (login, list rooms, thread, composer). Une réécriture en Vue prendrait ~2 jours si nécessaire.
Base de données — choix : PostgreSQL 16 ⚠️ one-way door
| Critère | PostgreSQL | SQLite |
|---|---|---|
| Concurrence multi-process | Oui (rôle natif) | Lock-fichier, dégrade > 5 writers |
| Scalabilité horizontale | Oui (replicas, pgbouncer) | Non sans contournements (litestream) |
| Backup standard | pg_dump + WAL |
.backup ou litestream |
| JSONB / index GIN metadata agent | Oui | Limité |
| Coolify support | Service managé en 1 clic | Non |
Conclusion : PostgreSQL 16. SQLite est tentant pour la simplicité d'ops, mais trois facteurs le disqualifient pour AgentHub :
- Le cluster Node passera en multi-worker dès la Phase 2 (cible 20–50 agents) → écritures concurrentes incompatibles avec le lock-fichier SQLite.
- L'historique de messages croît vite (10 agents × 1 msg/s en pic = 36k msgs/h, ~1 GB/an estimé Phase 1).
- La recherche full-text et les requêtes JSONB sur métadata agent (prévues Phase 2) sont natives Postgres, contournables mais lourdes en SQLite.
Adopter SQLite obligerait à introduire un seul writer + queue applicative, plus complexe que pg.
Réversibilité : ONE-WAY DOOR. Le schéma est figé J1 dans ADR-0002. Toute migration ultérieure (changement de moteur, re-shard, etc.) demande un plan de migration de données dédié.
Tests — choix : vitest + supertest
- vitest : runner moderne aligné Vite (DX cohérente avec le front), TS natif sans config, compatible API Jest.
- supertest : standard de fait pour tester un serveur HTTP Node (Fastify exposé via
.listen()ou.inject()— supertest accepte les deux). - Clients socket.io test : pour les events WS (
message:send→message:newround-trip), on utilisesocket.io-clientdirectement dans des tests vitest, pas de framework dédié au MVP.
Pas de matrice argumentée : ce choix découle directement du langage TS et du framework Fastify retenus, et n'a pas d'alternative crédible à ce stade. Coût de retour faible (les tests sont remplaçables sans toucher au code applicatif).
Pistes rejetées
- Python + FastAPI/Django Channels + frontend séparé. Sépare runtime et déploiement dès J1, double la surface d'ops, oblige à dupliquer les schémas (Pydantic côté back + types TS côté front). À reconsidérer uniquement si AgentHub se met à embarquer du training/eval ML lourd, ce qui n'est pas dans le scope MVP.
- Backend Go ou Rust + UI HTMX/templates. Excellent en prod, mais ralentit l'itération produit tant que le brief bouge encore. Mauvais arbitrage avant product/market fit.
- MongoDB / DynamoDB. Garanties relationnelles plus faibles, schémas plus durs à faire évoluer sereinement avec un seul dev. Pas de bénéfice net face à Postgres + JSONB sur le scope MVP.
- Microservices, files de messages, feature flags managé dès J1. Ni la charge ni la taille de l'équipe ne le justifient. À ajouter quand un troisième appelant concret apparaît (règle « pas de plateforme prématurée »).
- Express ou Koa au lieu de Fastify. Express manque de validation native et est ~2× plus lent. Koa n'apporte pas d'avantage décisif face à Fastify pour le scope WebSocket-heavy.
- Prisma au lieu de Drizzle. Prisma est plus lourd (engine Rust en sidecar, génération de client à chaque migration) sans bénéfice au scope MVP. Drizzle reste un plain query builder TS.
Conséquences
Positives
- Un seul langage TS du haut en bas : schémas zod partagés, types
messages/roomsréutilisés front/back, pas de duplication Pydantic↔TS. - Boucle de dev sub-minute :
vite dev(HMR < 200 ms) +tsx watchcôté back. - CI cible < 5 min wall-clock (lint + typecheck + tests + build).
- Stack « boring » : recrutement contractor facile, dette technique faible.
- Postgres couvre les besoins Phase 1 ET Phase 2 (replicas, pgbouncer) sans changement de moteur.
Négatives
- Postgres exige un service managé (vs. fichier SQLite) → opérationnellement plus lourd, mais Coolify le gère en 1 clic Phase 2 et
docker composePhase 1. - Stack dépendante de l'écosystème npm — surface dépendances large (mitigée par
npm auditen CI + renovate-bot mensuel, voir plan §5.5). - Node WS est mono-thread par worker — passage à
node:clusterou plusieurs réplicas + sticky sessions requis dès qu'on dépasse 20 agents simultanés (cible Phase 2, voir ENF-1 du plan).
Questions ouvertes (non-bloquantes)
- Refresh JWT côté agent. Au MVP, l'agent redemande un JWT via REST à expiration (15 min). Si la friction devient sensible, on ajoutera un mécanisme de refresh sans nouveau handshake.
- Recherche full-text. Hors-scope MVP. Quand elle arrivera, on choisit entre Postgres FTS natif (par défaut) et un index dédié type Meilisearch — le ticket sera créé séparément.
- Tracing distribué (OpenTelemetry). Reporté Phase 2.
- Vault de secrets. Phase 1 =
.envmode 600 + env vars Coolify Phase 2. Vault dédié à introduire seulement si on dépasse ~5 secrets sensibles (voir plan §5.5).
Statut côté repo
Cet ADR est rédigé contre le repo agenthub qui n'est pas encore scaffoldé (le scaffold est le livrable de J1 / AGNHUB-5). Le commit physique de docs/adr/0001-stack-technique.md se fait dans le PR de scaffold initial, en même temps que les ADR-0002 (data-model), ADR-0003 (auth-tokens) et ADR-0004 (déploiement Phase 1 LAN + Phase 2 Coolify) — voir plan §8.2.