Présentation de Dune : un CMS à fichiers plats pour Deno et Fresh
Lorsque j'ai écrit sur trente ans de Javascript côté serveur, la conclusion s'est presque écrite d'elle-même : Deno et Fresh synthétisent les leçons de trois décennies de serverjs et offrent actuellement le meilleur runtime et framework web pour Javascript côté serveur. La question que cette conclusion laissait ouverte était de savoir quoi construire avec eux. Pour moi, la réponse était la même que depuis l'époque de LiveWire : un système de publication de contenu sur le web. Je l'ai donc construit. Il s'appelle Dune, il est open source et fait tourner le site que vous êtes en train de lire.
Dune est un CMS à fichiers plats. Le contenu vit sous forme de fichiers sur disque — Markdown, MDX ou TSX — organisés dans des dossiers ordonnés et configurés avec du frontmatter YAML. Il n'y a pas de base de données à installer, pas de pipeline de build à configurer, pas de répertoire node_modules à expliquer. On écrit un fichier, le serveur sert une page. À partir de ce point de départ, il grandit avec vos besoins : des thèmes avec des templates Preact, un panneau d'administration complet, l'authentification, des plugins, une couche base de données quand un projet en a vraiment besoin. Mais l'état de repos, ce sont des fichiers dans des dossiers, et tout le reste est optionnel.
Les arguments pour les fichiers plats
Il peut sembler étrange, après une carrière passée à construire des systèmes de publication adossés à des bases de données, de plaider pour garder le contenu hors de la base de données. Mais l'argument s'est silencieusement imposé ces dernières années.
Une base de données CMS mérite sa complexité quand le contenu est véritablement relationnel et écrit simultanément par de nombreuses mains. La plupart des sites web ne sont ni l'un ni l'autre. Ce sont quelques dizaines à quelques milliers de documents, écrits par peu de personnes, lus par beaucoup. Pour ce profil de problème, le système de fichiers est une base de données remarquablement efficace : il possède une structure hiérarchique, des écritures atomiques, des horodatages et un modèle de contrôle d'accès. Combiné à git, il dispose du versionnage, de la réplication, de l'historique d'audit et de la sauvegarde distribuée — des problèmes résolus, hérités gratuitement, avec des outils que tout développeur utilise déjà. Votre contenu devient portable au sens le plus littéral : un dossier que vous pouvez copier, grep et diff.
Grav a montré à quel point ce modèle peut être agréable côté auteur, et Dune maintient délibérément ses conventions proches — des dossiers ordonnés comme 01.home/, du frontmatter en YAML, la taxonomie dans l'en-tête de page, les médias co-localisés avec la page à laquelle ils appartiennent. Assez proches, en fait, pour que Dune embarque une commande de migration qui importe un site Grav pratiquement tel quel, ainsi que des importateurs pour WordPress et Hugo. Hugo et les générateurs de sites statiques ont prouvé l'argument de performance depuis longtemps. Mais la génération statique abandonne le serveur, et avec lui tout ce qu'un serveur rend facile : l'authentification, les formulaires, les commentaires, la recherche, la personnalisation, un panneau d'administration. Dune conserve les fichiers plats et conserve le serveur. Les pages se rendent côté serveur à la demande, avec le cache et les ETags faisant le travail qu'un build statique anticiperait sinon.
Les échos de Helma
Les lecteurs de l'article précédent se souviendront de Helma, le serveur d'application Javascript sur la JVM dont j'ai fait partie de la communauté pendant de nombreuses années. En construisant Dune, je n'ai cessé de remarquer combien des idées de Helma étaient simplement de bonnes idées, attendant un runtime capable de les exprimer sans la cérémonie environnante.
Helma avait des skins — des templates HTML avec des espaces réservés appelant des macros, strictement séparés de la logique qui les alimentait. Les thèmes de Dune constituent la même séparation avec une syntaxe moderne : des templates TSX qui reçoivent la page entièrement rendue et retournent le document. Les templates sont uniquement côté serveur ; aucun Javascript d'un template n'est jamais envoyé au navigateur. Là où les skins de Helma appelaient des macros, les templates de Dune rendent des composants. Et là où E4X permettait autrefois à Helma d'assembler du markup comme une fonctionnalité native du langage, le JSX de Fresh — avec Deno qui gère la compilation de manière transparente — livre enfin cette même expérience sans étape de build visible que nous avons perdue quand E4X a été retiré du langage.
Helma mappait le chemin de requête sur un arbre d'objets ; Dune fait de même en le mappant sur l'arbre de dossiers, donnant un accès direct au système de fichiers à l'arbre d'objets et la capacité de colocaliser directement des données statiques. Un dossier est une route, un fichier est une page, et le préfixe d'ordre dans le nom du dossier est l'ordre de navigation. C'est le genre de convention qui s'explique d'elle-même la première fois qu'on voit un listing de répertoire de contenu.
L'héritage plus profond est la retenue architecturale. Helma était un framework qu'on pouvait tenir dans sa tête. Le stack web depuis lors a accumulé des couches — transpileurs, bundlers, fichiers de verrouillage pour les fichiers de verrouillage — qui ont chacun résolu un vrai problème et ensemble enterré le modèle mental. Toute la proposition de Deno est de se débarrasser de ces couches sans perdre ce qu'elles apportaient, et Dune essaie d'être le même type d'outil un niveau au-dessus : un CMS qu'on peut tenir dans sa tête.
Zéro Javascript par défaut
Dune adopte intégralement l'architecture islands de Fresh, et cela vaut la peine de préciser ce que cela signifie pour un site de contenu : par défaut, les pages n'envoient aucun Javascript. Le serveur rend du HTML, le navigateur l'affiche. Un blog construit avec Dune envoie du markup et des feuilles de style et rien d'autre — le navigateur du lecteur ne parse pas un seul script pour afficher un article.
L'interactivité est opt-in, par composant. Une barre de recherche, un carrousel d'images, un formulaire de commentaires — chacun est une île, un petit composant Preact qui s'hydrate indépendamment tandis que le reste de la page reste du HTML inerte. Les thèmes déclarent les îles en les plaçant dans un dossier islands/ ; Dune les découvre et les regroupe au démarrage. Le bundling se fait une fois, quand le serveur démarre, pas comme une étape de build qu'on exécute et committe. Il n'y a pas de répertoire dist/ dans un projet Dune.
C'est la partie du stack moderne que je voulais le plus préserver du monde des sites statiques : la discipline de ne payer pour du Javascript que là où il apporte quelque chose. Trente ans à rendre Javascript rapide, célébrés longuement dans l'article précédent, ne devraient pas être une excuse pour envoyer des mégaoctets de code pour rendre des paragraphes.
Grandir en application
Le problème avec la plupart des systèmes minimaux est la falaise à leur bout : le jour où votre projet a besoin d'une fonctionnalité que le système minimal ne possède pas, vous repassez à quelque chose de plus lourd. Dune est conçu pour ne pas avoir cette falaise, c'est pourquoi je le décris comme un CMS qui grandit du contenu Markdown aux applications web complètes.
Le chemin de croissance est incrémental. Les requêtes de frontmatter deviennent des collections — un index de blog est un dossier plus quelques lignes de YAML déclarant comment lister ses enfants. Les taxonomies taguent les pages et génèrent les vues d'archive. Quand les données structurées dépassent le frontmatter, les objets flex fournissent des enregistrements définis par schéma avec du CRUD admin généré. Les formulaires, commentaires et webhooks sont intégrés. L'authentification pour site public prend en charge les mots de passe, OAuth et les magic links, avec un contrôle d'accès basé sur les rôles et la restriction de contenu quand des parties d'un site passent en accès membres uniquement. Le multisite fait tourner plusieurs sites depuis un seul processus. Et sous tout cela, un système de plugins expose les hooks du moteur — transformations de réponse, gestionnaires de formats personnalisés, services d'administration, tâches planifiées, points d'entrée navigateur — de sorte que les fonctionnalités que Dune n'a pas peuvent être ajoutées sans forker celles qu'il possède.
Chacune de ces fonctionnalités est inactive jusqu'à ce qu'elle soit utilisée. Un site Dune qui n'est qu'un blog ne porte aucun de ces poids ; la même installation peut ensuite prendre des paiements sans changer de plateforme. Cette continuité — les mêmes fichiers, les mêmes conventions, du site vitrine à l'application — est l'objectif de conception qui a façonné la plupart des décisions.
Éditer là où le contenu vit
Il y a un manque évident dans tout système à fichiers plats : le contenu est des fichiers, mais tout le monde qui doit l'éditer ne veut pas ouvrir un éditeur de texte. Dune embarque un panneau d'administration — pages, médias, utilisateurs, historique — mais la partie que je trouve plus intéressante est l'édition en ligne, parce qu'elle montre comment l'architecture de plugins et les fichiers plats coopèrent.
Les thèmes marquent les régions éditables avec des composants marqueurs typés — EditableMarkdown pour le corps, EditableText pour les champs en ligne — importés depuis core et rendus côté serveur, sans JavaScript propre. Ce contrat de marqueurs est le couplage entier entre un thème et le système d'édition. L'éditeur lui-même est un plugin : il trouve les marqueurs, attache un handle d'édition flottant pour les admins connectés, et ouvre un éditeur WYSIWYG sur la source Markdown de la page. Plusieurs admins peuvent éditer la même page simultanément ; leurs modifications se fusionnent sans conflit via un CRDT et atterrissent dans le même fichier Markdown sur disque que vous auriez pu tout aussi bien ouvrir dans un éditeur de texte. Le fichier reste la seule source de vérité, quelle que soit la porte par laquelle vous êtes entré.
Et parce que les marqueurs sont un contrat réservé aux admins, Dune les supprime de chaque réponse servie à quelqu'un sans une session d'édition validée. Les visiteurs anonymes reçoivent un markup propre sans empreinte d'édition et sans indices sur l'emplacement du contenu sur disque. De tels paramètres par défaut sont éparpillés partout : le sandbox de permissions de Deno signifie que le processus ne touche que ce qui lui est explicitement accordé, les endpoints d'édition s'authentifient côté serveur indépendamment de ce que prétend un HTML, et l'historique du contenu enregistre qui a changé quoi. La sécurité comme posture par défaut, dans le runtime et dans l'application — cette leçon, l'écosystème l'a apprise à ses dépens, et Dune en hérite délibérément.
Du contenu avec lequel un agent peut travailler
La décision de garder le contenu sous forme de fichiers s'avère importante au-delà du cas d'usage humain. Markdown est ce sur quoi les grands modèles de langage sont entraînés — ils l'écrivent couramment, le lisent sans erreurs de parsing, et n'hallucinent jamais de syntaxe pour lui comme ils le font parfois avec des formats d'export propriétaires ou des langages de template spécifiques aux CMS. Le contenu complet d'un site à fichiers plats est visible pour tout outil capable de lire un répertoire. La convention dossier-comme-URL est suffisamment lisible pour qu'un agent puisse comprendre la structure complète d'un site depuis un listing de répertoire, sans consulter de documentation API ou de schéma de base de données. Le YAML de frontmatter est structuré et prévisible — un agent peut lire les métadonnées d'une page, patcher un champ et l'écrire en retour sans GUI, appel REST ou script de migration.
L'absence d'étape de build compte aussi. Un agent qui crée un nouveau fichier de contenu peut le valider immédiatement contre un serveur en cours d'exécution — il n'y a pas de pipeline à comprendre, pas de phase de préchauffage, pas de répertoire de sortie à inspecter. Et parce que le contenu est tracé dans git par convention, les agents bénéficient du même environnement de travail sécurisé qu'un développeur : brancher, appliquer, vérifier, committer ou annuler. Dune est conçu avec les agents IA en tête — et le modèle à fichiers plats explique pourquoi cela fonctionne : des fichiers simples, une interface directe avec le système de fichiers, pas d'étape de build, un contenu tracé dans git, un serveur en cours d'exécution qu'on peut interroger. Les mêmes choix qui le rendent simple pour un développeur le rendent lisible pour un agent.
Des outils pour les agents
Dune embarque des fichiers de compétences — des références de domaine concises installées dans .claude/skills/ par dune new et mises à jour via dune update:skills. Chaque compétence couvre un domaine du système : les conventions de contenu, le modèle de plugin, la couche de schéma, l'authentification, les tâches en arrière-plan, l'email. Un agent qui démarre une nouvelle session lit les compétences pertinentes et dispose immédiatement des connaissances de patterns dont il a besoin, sans parcourir la documentation ni inférer les conventions depuis le code source.
Au-delà de cela, le serveur MCP intégré expose neuf outils de lecture via stdio — interrogation de pages, exécution de recherches, inspection de collections, lecture de taxonomie, examen de la config, listage des templates et blueprints disponibles, et récupération de la source brute d'une page. Un agent peut obtenir une image complète d'un site en cours d'exécution sans jamais toucher au système de fichiers.
Pour les écritures, la Change API (POST /admin/api/dev/apply) accepte des opérations JSON structurées — créer ou mettre à jour un fichier, supprimer un fichier, patcher des champs de frontmatter, modifier la config, installer un plugin — avec un mode dry-run qui signale ce qui changerait avant que quoi que ce soit ne touche le disque. dune validate exécute les mêmes vérifications que le serveur effectue au démarrage, de sorte qu'un agent peut vérifier en amont un ensemble de modifications avant de les committer. Le schéma de config est exportable en JSON Schema pour l'autocomplétion des IDE et des agents. Et llms.txt et llms-full.txt sont servis depuis le site de documentation dans le format sur lequel l'écosystème s'est mis d'accord pour rendre la documentation accessible aux modèles.
Ouvert dès le départ
L'article précédent retrace comment Netscape, après avoir réussi tant de choses techniquement avec LiveWire, a tout gâché en gardant l'implémentation propriétaire — essayant de posséder le web au lieu d'y participer. WebCrossing a répété l'erreur, et Helma, bien qu'open source, est arrivé avant que l'infrastructure de distribution existait pour le porter à un large public.
Dune peut prendre le chemin opposé sur tous les points, avec trente ans d'infrastructure accumulée le rendant presque sans effort. Le code est sous licence MIT. Il est publié sur JSR, le registry natif TypeScript, où chaque version porte ses types, sa documentation et une attestation de provenance reliant le package au commit exact et à l'exécution CI qui l'a produit. L'installation du CLI est une commande ; mettre à niveau un site consiste à éditer un numéro de version dans un fichier. Les thèmes sont ouverts, les interfaces de plugins sont documentées, et la documentation elle-même est un dépôt git public — servie, naturellement, par un site Dune.
Rien de tout cela ne garantit l'adoption, et après trente ans je ne me fais aucune illusion que le système mieux conçu gagne par défaut. Mais les modes d'échec du chemin fermé sont une histoire documentée, racontée dans mon article précédent. Participer à l'écosystème ouvert est la seule stratégie qui ait jamais composé.
Dune est jeune, les numéros de version commencent encore à zéro, et il reste beaucoup à construire. Si les fichiers plats, les pages sans Javascript par défaut, et un CMS qu'on peut tenir dans sa tête ressemblent à votre stack, le point de départ est getdune.org — le guide de démarrage rapide vous permet de servir un site en cinq minutes, et les commandes de migration seront heureuses d'extraire votre contenu de la base de données dans laquelle il se trouve actuellement.
Les fichiers sont prêts quand vous l'êtes.