Nodefony — le framework Node.js fullstack
Temps réel natif, développement agentic-ready, sur un socle TypeScript isomorphe. Dix minutes pour comprendre ce que le framework couvre, ce qu'il coûte et ce qui lui manque.
Où aller ensuite
Ce qu'est Nodefony
Pourquoi pas NestJS ? Même modèle — injection, décorateurs, modules — avec une différence de structure : le WebSocket est un transport de la même route, de la même session et du même pare-feu, pas une passerelle à côté. Et l’application se décrit à un agent — générateurs pilotables, catalogue,
AGENTS.mddérivé du projet — au lieu d’être devinée.
État — version 10.0.0 · non publiée sur npm : le registre sert encore nodefony@7.0.2
(JavaScript) · branche par défaut main (dernière publication), développement sur dev · ~7 700 tests quand toute
l’infrastructure répond (npm run test:all).
Nodefony est un framework serveur fullstack pour Node.js, écrit en TypeScript strict et bâti
directement sur les modules natifs de la plateforme — node:http, node:http2, WebSocket. Il
apporte un noyau à injection de dépendances, un système de modules, un pare-feu applicatif, une
persistance portable, une console d’administration et la construction des frontends.
Sa particularité tient en une propriété : le WebSocket n’y est pas un ajout. C’est un transport de première classe, servi par le même pipeline, la même table de routes et la même sécurité que le HTTP. Une application temps réel s’y écrit comme une application web ordinaire.
Les frontends ne sont pas laissés dehors. Nodefony pilote Vite : en développement il démarre les serveurs de développement — React, Vue, Angular —, les surveille, relaie leur rechargement à chaud et sert leurs pages ; en production il construit les bundles et les distribue. Une application peut en porter plusieurs, chacun monté sur sa route.
Le socle, lui, est isomorphe : le même paquet s’importe côté serveur et côté navigateur. Le client temps réel, les règles d’autorisation, les types d’une ressource sont écrits une fois et s’exécutent là où ils servent — une règle corrigée l’est des deux côtés.
D’où ça vient
Nodefony est publié depuis 2017, en JavaScript, et a mûri jusqu’à sa version 7 : des applications réelles tournent encore dessus. Fin 2023, plutôt que de le migrer par petits pas, le choix a été fait d’une réécriture complète en TypeScript, fichier par fichier, en gardant les concepts et en jetant tout le reste.
Pourquoi une réécriture et pas une migration progressive ? Parce qu’un framework ne se contente pas d’exécuter du code : il contraint celui qu’on écrit contre lui. Une couche de types posée après coup sur du JavaScript décrit ce que le code fait ; elle ne garantit rien. Les partis pris de cette réécriture sont donc structurels, et chacun ferme une porte :
| Décision | Ce qu’elle rend impossible |
|---|---|
TypeScript strict, zéro @ts-ignore |
qu’un contrat se dégrade en silence entre deux modules |
| ESM exclusivement | la double résolution CommonJS/ESM et ses pièges de chargement |
| Décorateurs plutôt que convention | qu’une route existe sans être déclarée là où on la lit |
| Configuration validée au boot | qu’une clé mal orthographiée soit ignorée sans un mot |
| Un processus = une instance | la supervision maison ; l’échelle revient à l’orchestrateur |
La version 10 est l’aboutissement de cette réécriture. Ce n’est pas un portage : c’est le même projet, repensé pour ce que Node.js et TypeScript sont devenus.
Une action, deux transports
Ce contrôleur est celui que nodefony create controller produit. La première action répond en REST ;
la seconde est un point d’entrée WebSocket. Même classe, même session, mêmes règles d’accès :
import {
route,
controller,
Controller,
CurrentUser,
} from "@nodefony/framework";
import type { ContextType } from "@nodefony/http";
@controller("/api/blog")
class BlogController extends Controller {
constructor(context: ContextType) {
super("blog", context);
}
@route("blog-index", { path: "", method: "GET" })
async index(@CurrentUser() user?: { identifier?: string }) {
return this.renderJson({
hello: "blog",
who: user?.identifier ?? "anonyme",
});
}
// Même classe, même décorateur : seul le transport déclaré change.
@route("blog-echo", {
path: "/echo",
requirements: { methods: ["WEBSOCKET"] },
})
async echo(message: string | Buffer | null) {
if (!message) return this.renderJson({ handshake: true });
return this.renderJson({ echo: message.toString() });
}
}
Ce que ça implique sous le capot — pourquoi ce n'est pas du sucre syntaxique
- La pseudo-méthode
WEBSOCKETest traitée comme un verbe HTTP ordinaire : une seule table de routes pour les deux transports, un seul résolveur de contrôleur. - Le WebSocket ouvre la même bulle
AsyncLocalStorageque le HTTP et la propage à chaque message : l’identifiant de requête et l’utilisateur restent stables du handshake à la fermeture. - Conséquence directe : une règle d’autorisation protège une action quel que soit le transport, et une session ouverte en HTTP est celle que voit la socket. Pas de passerelle, pas de seconde pile d’authentification, pas de logique dupliquée.
- C’est aussi ce qui fait du streaming — d’un fichier, d’un événement, d’un modèle de langage — un cas d’usage naturel plutôt qu’un montage : un générateur asynchrone branché sur un canal, dans le même contexte de sécurité que le reste de l’application.
Un seul langage, du serveur au navigateur
Le client n’est pas une bibliothèque à part, publiée séparément et rattrapée à chaque version : il est dans le même paquet que le serveur, et il partage ses types. Le contrat d’un canal, la forme d’un message, la hiérarchie des rôles — on les écrit une fois, et les deux côtés en dépendent :
import { RealtimeClient } from "nodefony/client";
const socket = RealtimeClient.shared({ url: "wss://localhost:5152" });
await socket.subscribe("chat:room"); // canaux
socket.on("chat:room", (message) => render(message));
const modules = await socket.request("/nodefony/kernel/api/modules"); // appel de service
Les mêmes sous-chemins servent le reste : nodefony/react pour les hooks, nodefony/roles pour
évaluer une autorisation dans l’interface avec la règle exacte du serveur — un bouton caché parce
que le rôle manque est caché par la même logique que celle qui refusera l’appel.
Pourquoi TypeScript, et pas un portage vers un langage plus rapide ? Parce que l’isomorphisme se paierait exactement là. Réécrire le cœur ailleurs ferait gagner des microsecondes et perdre la seule chose qu’un framework fullstack peut vraiment offrir : un contrat unique, vérifié par le compilateur, du contrôleur jusqu’au composant. Ce qui coûte cher dans une application temps réel, ce n’est pas le langage — c’est la frontière entre deux mondes qui doivent se redire la même chose et finissent par diverger. Nodefony supprime la frontière plutôt que d’optimiser le passage.
Prêt pour les agents — ce que ça veut dire ici
Une application est aujourd’hui écrite à deux mains : la personne et l’agent qu’elle pilote. Or un agent lâché dans un projet bâti sur un framework qu’il connaît mal invente. Il produit du code plausible : un CRUD écrit à la main là où un générateur existait, un import direct du driver de base de données qui contourne la façade, une socket bas niveau là où le framework offre un canal. Ce code compile, il passe même les tests — et il aura vieilli avant d’être relu.
La réponse de Nodefony n’est pas un assistant intégré. C’est de rendre l’application capable de se décrire, pour que l’agent lise au lieu de deviner :
| Ce qui est posé | Ce que ça évite |
|---|---|
Un AGENTS.md généré à la racine |
la convention périmée : le fichier est dérivé du projet réel, il ne peut pas mentir |
| La doc voyage dans les paquets npm | l’agent qui cherche sur le web une version qui n’est pas la vôtre |
| Un catalogue des briques publié | le choix de module fait au jugé, sans savoir ce qu’un adaptateur ne couvre pas |
inspect · check · env |
la déduction depuis le code de ce que l’application fait vraiment : routes, services, config |
| Des générateurs pilotables en JSON | l’imitation d’un fichier d’exemple, au lieu d’appeler l’outil qui produit le vrai code |
| Un graphe symbolique du code | la fouille par grep : qui étend quoi, qui implémente quoi, en une lecture |
Le standard retenu — AGENTS.md — est celui que lisent la plupart des outils
de codage, avec la règle « le plus proche gagne ». Rien n’est propriétaire : le fichier est un
index, court par construction, qui pointe vers la documentation installée plutôt que de la
recopier.
Et surtout : c'est mesuré — un banc de découvrabilité, pas une intention
Une application témoin est générée, puis un agent y reçoit des tâches réelles : « ajoute un CRUD produit », « protège une route », « écris une commande CLI », « configure l’application par l’environnement », « choisis la brique adaptée à ce besoin ». Le harnais lit ensuite le transcript et le code produit, et répond à une seule question : l’agent a-t-il lu, ou deviné ?
La métrique n’est pas « le code marche » — il marche souvent, c’est bien le piège. C’est : a-t-il lancé le générateur ? ouvert le catalogue ? interrogé la configuration effective plutôt que de la supposer ? Le banc n’est pas entièrement vert aujourd’hui, et chaque échec désigne un endroit précis où l’application ne se rend pas assez évidente. C’est exactement à ça qu’il sert.
Ce que Nodefony revendique n’est pas d’écrire un AGENTS.md — n’importe quel projet peut le
faire — mais de traiter la découvrabilité comme une propriété mesurée : le banc ci-dessus est
rejouable, et ses échecs sont publics. C’est cette place-là que le framework occupe délibérément.
Démarrage
Les paquets 10.x ne sont pas encore publiés sur npm ; le framework s’essaie depuis ce dépôt :
git clone https://github.com/nodefony/nodefony-core.git
cd nodefony-core
npm install && npm run build
npm run dev
L’application répond sur http://127.0.0.1:5151, la console d’administration sur /nodefony.
Générer du code — une application, un module, un contrôleur, une entité et toute sa chaîne :
nodefony create app mon-app
nodefony create module blog --frontend react
nodefony create entity Article title:string! body:text views:int
Chaque générateur montre le plan et le diff avant d’écrire quoi que ce soit. Un refus ne laisse rien derrière lui. Il publie aussi son catalogue en JSON et accepte ses réponses par fichier : la même porte sert la personne au terminal, la console d’administration et un agent.
Le framework se regarde tourner
@nodefony/studio est une console d’administration livrée avec le framework : topologie du runtime,
journaux en direct avec rejeu, suivi d’une requête de bout en bout par son identifiant, schéma de la
base, graphe des classes par module, gouvernance de la sécurité (audit, pare-feu, rôles, sessions,
clés d’API), et les générateurs de code pilotables à la souris, la sortie diffusée comme un terminal.
Sa force est en dessous : elle ne consomme aucune API privée. Toutes ses données viennent d’un plan de données JSON protégé par les mêmes règles d’accès que le reste, auto-décrit — un appel en renvoie le catalogue — et duplex : le même point d’accès répond en HTTP et par la socket. Ce qu’affiche la console, un script ou un agent peut le lire tel quel.
Le suivi d’une requête en est l’illustration la plus directe : chaque requête porte un identifiant propagé dans tout le pipeline, et la console rejoue son trajet complet — phases, requêtes de base de données, décisions du pare-feu.
La sécurité, fermée par défaut
Le pare-feu découpe l’application en zones, chacune avec sa chaîne d’authentification. Il refuse plutôt que d’ouvrir : une configuration invalide capture le trafic et répond 401.
| Identités | session serveur pour le web · jetons et clés d’API pour les machines · OAuth2/OIDC · WebAuthn · TOTP |
| Cryptographie | Argon2id pour les mots de passe · signatures Ed25519 · secrets chiffrés au repos |
| Défenses | CSRF par métadonnées de requête · en-têtes de sécurité · limitation de débit · journal d’audit |
| Autorisation | hiérarchie de rôles vérifiée au démarrage, refus par défaut |
Équipe rouge, équipe bleue
Ces briques ne sont pas éprouvées par des cas nominaux — « le login fonctionne » ne prouve rien. Les campagnes se mènent en deux passes, et l’ordre est le cœur du dispositif :
- Passe rouge — la menace d’abord. La matrice d’attaque est construite depuis les standards et les faiblesses connues avant d’avoir lu le code. C’est un garde-fou contre son propre biais : qui lit l’implémentation en premier ne teste que ce qu’elle prévoit, et rate précisément ce qu’elle a oublié.
- Passe bleue — le code ensuite. On lit l’implémentation, on couvre les branches restantes, on regarde les chemins que la passe rouge n’imaginait pas.
- Le cycle. Faille trouvée → corrigée → re-prouvée par un test qui échouait avant elle. Un correctif sans son test de non-retour ne compte pas comme corrigé.
Ce qui distingue ces attaques d’un scan générique : beaucoup visent des surfaces propres à cette architecture — les portées de l’injection de dépendances, les messages du pipeline WebSocket partagé, le jeton porté par le contexte asynchrone, les zones du pare-feu et leurs contournements. Aucun outil sur étagère ne connaît ces surfaces : il faut concevoir les attaques.
⚠️ À savoir avant de concevoir votre application. Le refus par défaut opère par zone du pare-feu, pas route par route : une route située hors de toute zone déclarée est publique. Déclarez vos zones.
Ce qu’il y a dans la boîte
| Brique | Rôle |
|---|---|
nodefony |
Noyau : modules, injection de dépendances, configuration validée au démarrage, journalisation structurée corrélée, CLI — et le client temps réel partagé avec le navigateur |
@nodefony/http |
Serveurs HTTP, HTTPS, HTTP/2 et WebSocket natifs, sessions, contextes de requête, certificats TLS |
@nodefony/framework |
Routeur, contrôleurs, décorateurs, vues — le modèle de programmation |
@nodefony/security · @nodefony/user |
Pare-feu par zones, authentification, autorisation par rôles, CSRF, audit |
@nodefony/realtime |
Canaux, appels bidirectionnels, contre-pression, diffusion entre plusieurs instances |
@nodefony/orm-core |
Un contrat de dépôt de données, plusieurs moteurs : Drizzle (SQLite, PostgreSQL, MySQL), Mongoose, Redis |
@nodefony/frontend |
Construction et rechargement à chaud des frontends de chaque module |
@nodefony/studio |
Console d’administration |
Un processus Node égale une instance : pas de superviseur maison, le passage à l’échelle revient à l’orchestrateur, et les journaux partent sur la sortie standard.
Où aller ensuite
- Par où commencer — quatre parcours selon ce que vous venez faire
- Documentation · Guides · Première application
- L’architecture en vue d’ensemble — ce que le framework est, et ce que ses partis pris coûtent
- Performance — ce qui a été mesuré, avec quel protocole, et ce que ces chiffres ne permettent pas de conclure ; les mesures de chaque version publiée sont sur la page dédiée
- Décisions d’architecture — les choix structurants et leur pourquoi
- Signaler une faille — canal privé, jamais en ticket public
- Contribuer : ouvrez une issue avant toute contribution substantielle. Le dépôt impose les
Conventional Commits et un
npm run typecheckcomplet avant chaque envoi.
État du projet
Le cœur — serveurs, routage, sécurité, temps réel, persistance, console d’administration,
construction des frontends — est couvert par des suites de tests exécutables sur infrastructure
réelle (npm run test:all), et le dépôt versionne des seuils de fuite mémoire et de charge
opposables à chaque exécution.
Ce que ça donne en charge. À travail égal — mêmes journaux, même contexte de requête, mêmes en-têtes de sécurité, même protection CSRF — un processus rend ~92 % du débit d’un Express muni des mêmes intergiciels (12 226 requêtes/s, p99 9,57 ms sur la machine de référence), et vingt minutes de charge continue laissent le tas plat. La page de mesures de la version courante — peut-on partir en production ? — porte le comparatif, la tenue dans la durée, le dimensionnement d’un pod et un calculateur ; elle nomme aussi ce que ces chiffres ne prouvent pas. Le dossier Performance donne le protocole, les scripts qui rejouent chaque chiffre et les instruments qui ont menti avant qu’on s’en aperçoive — aucun absolu pris derrière un chemin virtualisé n’est transposable.
Ce qu’il faut savoir avant de bâtir dessus : les versions JavaScript historiques (≤ 7.x) ne reçoivent plus de correctifs. Projet libre, développé bénévolement par une seule personne.
Licence CeCILL-B — libre de droit français, compatible BSD. Christophe Camensuli · ccamensuli@gmail.com
La suite est une couche d’agents IA construite sur ce socle : le même pipeline, la même sécurité, le même temps réel — et une application qui sait déjà se décrire à une machine.
L'essentiel en trente secondes
Un framework Node.js fullstack, en TypeScript strict, bâti directement sur les modules natifs de la plateforme. Pensé pour le temps réel : le serveur HTTP et le serveur WebSocket partagent le même contexte de contrôleur.
Trois propriétés le définissent, et chacune se vérifie dans le code plutôt qu'elle ne se raconte : le temps réel de première classe, l'isomorphisme serveur/navigateur, et une application qui sait se décrire à un outil comme à une personne.
D'où ça vient — de 2017 à la réécriture
Nodefony est publié depuis 2017, en JavaScript, et a mûri jusqu'à sa version 7 : des applications réelles tournent encore dessus. Fin 2023, plutôt qu'une migration par petits pas, le choix a été fait d'une réécriture complète en TypeScript — en gardant les concepts, en jetant tout le reste.
Pourquoi ? Parce qu'un framework ne se contente pas d'exécuter du code : il contraint celui qu'on écrit contre lui. Une couche de types posée après coup décrit ce que le code fait ; elle ne garantit rien. Chaque parti pris de la réécriture ferme une porte :
| Décision | Ce qu'elle rend impossible |
|---|---|
TypeScript strict, zéro any | qu'un contrat se dégrade en silence entre deux modules |
| ESM exclusivement | la double résolution CommonJS/ESM et ses pièges de chargement |
| Décorateurs plutôt que convention | qu'une route existe sans être déclarée là où on la lit |
| Configuration validée au démarrage | qu'une clé mal orthographiée soit ignorée sans un mot |
| Un processus = une instance | la supervision maison ; l'échelle revient à l'orchestrateur |
Le pari : un transport n'est pas une architecture
La plupart des piles traitent le WebSocket comme une annexe : une seconde table de routes, une
seconde pile d'authentification, une passerelle entre les deux. Nodefony refuse cette séparation.
La pseudo-méthode WEBSOCKET est un verbe comme GET :
@controller("/api/blog")
class BlogController extends Controller {
@route("blog-index", { path: "", method: "GET" })
async index(@CurrentUser() user?: { identifier?: string }) {
return this.renderJson({ hello: "blog", who: user?.identifier ?? "anonyme" });
}
// Même classe, même décorateur : seul le transport déclaré change.
@route("blog-echo", { path: "/echo", requirements: { methods: ["WEBSOCKET"] } })
async echo(message: string | Buffer | null) {
return this.renderJson({ echo: message?.toString() ?? null });
}
}
L'isomorphisme — et pourquoi le cœur reste en TypeScript
Le client n'est pas une bibliothèque à part, publiée séparément et rattrapée à chaque version : il vit dans le même paquet que le serveur et partage ses types. Le contrat d'un canal, la forme d'un message, la hiérarchie des rôles s'écrivent une fois.
| Sous-chemin | Ce qu'il donne au navigateur |
|---|---|
nodefony/client | socket temps réel : canaux, appels de service, reconnexion |
nodefony/react | hooks (useNodefony, état de connexion, identité) |
nodefony/roles | évaluation d'autorisation avec la règle exacte du serveur |
nodefony/debugbar | barre de diagnostic branchée sur le contexte de la requête |
Un bouton masqué faute de rôle l'est par la même logique que celle qui refusera l'appel. Il n'y a pas deux vérités à synchroniser.
Prêt pour les agents — ce qui tranche pour la suite
Une application est aujourd'hui écrite à deux mains : la personne, et l'agent qu'elle pilote. Un agent lâché dans un projet bâti sur un framework qu'il connaît mal invente — et il invente du plausible : un CRUD écrit à la main là où un générateur existait, un import direct du driver de base qui contourne la façade, une socket bas niveau là où le framework offre un canal. Ce code compile. Il passe même les tests. Il a vieilli avant d'être relu.
La réponse n'est pas un assistant intégré, c'est de rendre l'application capable de se décrire :
| Ce qui est posé | Ce que ça évite |
|---|---|
Un AGENTS.md généré à la racine | la convention périmée : le fichier est dérivé du projet réel, il ne peut pas mentir |
| La documentation voyage dans les paquets | l'agent qui cherche sur le web une version qui n'est pas la vôtre |
| Un catalogue des briques publié | le choix de module fait au jugé, sans savoir ce qu'un adaptateur ne couvre pas |
inspect · check · env | la déduction depuis le code : routes, services, configuration effective et sa provenance |
| Des générateurs pilotables en JSON | l'imitation d'un fichier d'exemple au lieu de l'appel à l'outil qui produit le vrai code |
| Un graphe symbolique du code | la fouille par recherche textuelle : qui étend quoi, qui implémente quoi, en une lecture |
AGENTS.md — est celui que lisent la plupart des outils de
codage, avec la règle « le plus proche gagne ». Rien de propriétaire : c'est un index court qui
pointe vers la documentation installée au lieu de la recopier. Une règle recopiée
dérive ; une règle pointée reste vraie.Aucun framework backend Node n'offre aujourd'hui d'équivalent officiel. C'est un espace vide, et c'est délibérément là que Nodefony se place.
Ce que les suites éprouvent — les angles, pas seulement le nombre
Un compte de tests ne dit rien de ce qu'ils regardent. Ces chiffres sont comptés dans le dépôt au moment de la génération de cette page, fichier par fichier.
S'y ajoutent 12 flux d'intégration continue — analyse statique de sécurité, gate mémoire, suites ORM sur plusieurs moteurs, bout-en-bout autonomes, contrôle avant publication et installation à blanc des paquets depuis leurs archives.
La matrice — ce qui est couvert, domaine par domaine
Calculé depuis le dépôt au moment de la génération de cette page : pour chaque domaine, le code, les suites de tests, la documentation destinée aux humains, et la fiche d'entrée du paquet.
| Noyau, injection, configuration, journalisation, CLI | nodefony | 418 | couvert ailleurs | 16 | ✓ |
| Transports HTTP · HTTPS · HTTP/2 · WebSocket, sessions | @nodefony/http | 213 | couvert ailleurs | 7 | ✓ |
| Pare-feu, authentification, autorisation, audit | @nodefony/security | 118 | 77 | 17 | ✓ |
| Routage, contrôleurs, décorateurs, vues | @nodefony/framework | 88 | couvert ailleurs | 7 | ✓ |
| Canaux, appels bidirectionnels, diffusion multi-instances | @nodefony/realtime | 76 | couvert ailleurs | 10 | ✓ |
| Moteur SQL — SQLite, PostgreSQL, MySQL | @nodefony/drizzle | 64 | 93 | 2 | ✓ |
| Construction des frontends, rechargement à chaud | @nodefony/frontend | 42 | couvert ailleurs | 1 | ✓ |
| Contrat de dépôt de données, transactions, critères | @nodefony/orm-core | 31 | 18 | 2 | ✓ |
| Cache, sessions, diffusion entre instances | @nodefony/redis | 30 | couvert ailleurs | 3 | ✓ |
| Socle utilisateur, encodeurs, service de comptes | @nodefony/user | 25 | 18 | 2 | ✓ |
| Console d'administration | @nodefony/studio | 23 | couvert ailleurs | 2 | ✓ |
| Moteur documentaire — MongoDB | @nodefony/mongoose | 21 | 19 | 2 | ✓ |
| Portail de documentation | @nodefony/documentation | 19 | couvert ailleurs | 2 | ✓ |
| devkit | @nodefony/devkit | 9 | 9 | 1 | ✓ |
La sécurité s'éprouve par l'attaque, pas par la lecture
Le pare-feu découpe l'application en zones, chacune avec sa chaîne d'authentification, et il refuse plutôt que d'ouvrir : une configuration invalide capture le trafic et répond 401.
| Surface | Ce qui est en place |
|---|---|
| Identités | session serveur pour le web · jetons et clés d'API pour les machines · OAuth2/OIDC · WebAuthn · TOTP |
| Cryptographie | Argon2id pour les mots de passe · signatures Ed25519 · secrets chiffrés au repos |
| Défenses | CSRF par métadonnées de requête · en-têtes de sécurité · limitation de débit · journal d'audit persistant |
| Autorisation | hiérarchie de rôles vérifiée au démarrage, refus par défaut, isomorphe côté navigateur |
Équipe rouge, équipe bleue
« Le login fonctionne » ne prouve rien. Les campagnes se mènent en deux passes, et l'ordre est le cœur du dispositif :
- Passe rouge — la menace d'abord. La matrice d'attaque est construite depuis les standards et les faiblesses connues avant d'avoir lu le code. C'est un garde-fou contre son propre biais : qui lit l'implémentation en premier ne teste que ce qu'elle prévoit, et rate précisément ce qu'elle a oublié.
- Passe bleue — le code ensuite. On lit l'implémentation, on couvre les branches restantes, on regarde les chemins que la passe rouge n'imaginait pas.
- Le cycle. Faille trouvée → corrigée → re-prouvée par un test qui échouait avant elle. Un correctif sans son test de non-retour ne compte pas comme corrigé.
Ce qui distingue ces attaques d'un scan générique : beaucoup visent des surfaces propres à cette architecture — les portées de l'injection de dépendances, les messages du pipeline WebSocket partagé, le jeton porté par le contexte asynchrone, les zones du pare-feu et leurs contournements. Aucun outil sur étagère ne connaît ces surfaces : il faut concevoir les attaques.
Deux disciplines qui vont plus loin que l'usage
- Un test qu'on n'a jamais vu échouer ne prouve rien. Un test neuf est débranché volontairement une fois, pour vérifier qu'il tombe. Sinon il est complaisant par construction.
- L'historique du dépôt est scanné à la recherche de secrets — pas seulement l'état courant : passer un dépôt en public expose chaque révision.
Le framework se regarde tourner
Une console d'administration est livrée avec le framework : topologie du runtime, journaux en direct avec rejeu, suivi d'une requête de bout en bout par son identifiant, schéma de la base, graphe des classes par module, gouvernance de la sécurité, et les générateurs de code pilotables à la souris — la sortie diffusée comme un terminal.
Chaque requête porte un identifiant propagé dans tout le pipeline : la console rejoue son trajet complet — phases, requêtes de base de données, décisions du pare-feu.
Même principe pour les générateurs de code : ils publient leur catalogue, montrent le plan et le diff avant d'écrire quoi que ce soit, et acceptent leurs réponses par fichier. Un refus ne laisse rien derrière lui.
Performance : la question honnête
Le framework fait à chaque requête ce qu'une pile minimale ne fait pas : contexte asynchrone corrélé, identifiant de requête, en-têtes de sécurité, contrôle CSRF, résolution de zone de pare-feu. Comparer sans ce travail ne compare rien. Les constantes ci-dessous servent à dimensionner, pas à gagner un concours.
Ce que le dépôt garantit à chaque commit
| Barrière | Ce qu'elle empêche |
|---|---|
| Typage strict, sur tous les espaces de travail | aucun avertissement du compilateur mis sous le tapis ; les exceptions de type ne vivent que dans les tests, où elles servent d'assertions |
| Seuils de mémoire et de charge versionnés | une fuite ou une régression de latence passe en échec, pas en discussion |
| Suites exécutables sur infrastructure réelle | un test sauté faute de base compte comme vert : le lanceur dit ce qu'il n'a pas exercé |
| Graphe symbolique régénéré à chaque commit | un renommage silencieux, une relation cassée entre modules |
| Intégration continue multi-systèmes | le « ça marche chez moi » sur une seule plateforme |
Les preuves existent, et elles sont rejouables
Chaque affirmation de ce support s'adosse à un artefact produit par le dépôt lui-même, pas à une déclaration d'intention. Ces rapports se régénèrent par une commande :
| Artefact | Ce qu'il établit |
|---|---|
| Rapport de capacité | les constantes de dimensionnement d'une instance, et le nombre d'instances pour une charge donnée |
| Banc de charge HTTP et WebSocket | le débit soutenable, les percentiles, le point de rupture |
| Audit du pipeline de requête | où passe le temps, phase par phase, profileur éteint |
| Scan de l'historique | aucun secret dans les révisions du dépôt, pas seulement dans l'état courant |
| Banc du générateur de code | le code produit compile, ses tests passent, sa ressource répond réellement en HTTP |
| Registre des écarts documentation ↔ code | chaque page de référence est confrontée au code qu'elle décrit |
Ce qui manque — dit avant qu'on le demande
| Manque | Portée réelle |
|---|---|
| Paquets non publiés sur npm | le framework s'installe depuis le dépôt ; la publication est la prochaine étape |
| Couche d'agents IA | chantier ouvert, hors périmètre de cette présentation — rien n'en dépend aujourd'hui |
| Console d'administration sans tests de composants | ses écrans ne sont couverts que par le typage |
| Projet à un seul contributeur | bénévole ; les délais de réponse s'en ressentent, c'est écrit noir sur blanc dans la politique de sécurité |
Dire les manques n'est pas une précaution rhétorique : c'est ce qui rend le reste vérifiable. Un dossier sans zone d'ombre décrit rarement un vrai logiciel.
La direction
La suite est une couche d'agents construite sur ce socle : le même pipeline, la même sécurité, le même temps réel — et une application qui sait déjà se décrire à une machine.
Rien de cette couche n'est promis dans la version courante. Ce qui est acquis, en revanche, c'est le terrain sur lequel elle se posera : un plan de données auto-décrit, un graphe symbolique du code, un générateur pilotable par fichier, et un transport duplex qui fait du flux un cas ordinaire plutôt qu'un montage.