Dune vorstellen: Ein Flat-File-CMS für Deno und Fresh
Als ich über dreissig Jahre Javascript auf dem Server schrieb, schrieb sich das Fazit fast von selbst: Deno und Fresh synthetisieren die Lehren von drei Jahrzehnten Serverjs und bieten derzeit den besten Laufzeit- und Web-Framework für Javascript auf dem Server. Die Frage, die dieses Fazit offenliess, war, was man damit bauen sollte. Für mich war die Antwort dieselbe wie seit den LiveWire-Tagen: ein System zur Veröffentlichung von Inhalten im Web. Also habe ich eines gebaut. Es heisst Dune, ist Open Source und betreibt die Website, auf der Sie das hier lesen.
Dune ist ein Flat-File-CMS. Inhalte leben als Dateien auf der Festplatte — Markdown, MDX oder TSX — in geordneten Ordnern organisiert und mit YAML-Frontmatter konfiguriert. Es gibt keine Datenbank zu installieren, keine Build-Pipeline zu konfigurieren, kein node_modules-Verzeichnis zu erklären. Man schreibt eine Datei, der Server liefert eine Seite. Von diesem Ausgangspunkt wächst es mit den eigenen Bedürfnissen: Themes mit Preact-Templates, ein vollständiges Admin-Panel, Authentifizierung, Plugins, eine Datenbankschicht, wenn ein Projekt sie wirklich braucht. Aber der Ruhezustand sind Dateien in Ordnern, und alles andere ist optional.
Die Argumente für Flat Files
Es mag seltsam erscheinen, nach einer Karriere, die mit dem Aufbau datenbankgestützter Publishing-Systeme verbracht wurde, dafür zu plädieren, Inhalte aus der Datenbank herauszuhalten. Aber das Argument hat sich in den letzten Jahren still durchgesetzt.
Eine CMS-Datenbank verdient ihre Komplexität, wenn Inhalte wirklich relational sind und von vielen Händen gleichzeitig geschrieben werden. Die meisten Websites sind weder das eine noch das andere. Sie bestehen aus ein paar Dutzend bis ein paar Tausend Dokumenten, geschrieben von wenigen Menschen, gelesen von vielen. Für dieses Problem-Muster ist das Dateisystem eine bemerkenswert gute Datenbank: Es hat hierarchische Struktur, atomare Schreibvorgänge, Zeitstempel und ein Zugriffskontrollmodell. Kombiniert mit Git hat es Versionierung, Replikation, Prüfhistorie und verteilte Sicherung — gelöste Probleme, kostenlos geerbt, mit Tools, die jeder Entwickler bereits kennt. Die Inhalte werden portabel im wörtlichsten Sinne: ein Ordner, den man kopieren, durchsuchen und vergleichen kann.
Grav hat gezeigt, wie angenehm dieses Modell auf der Autorenseite sein kann, und Dune hält seine Konventionen bewusst nah daran — geordnete Ordner wie 01.home/, Frontmatter in YAML, Taxonomie im Seitenheader, Medien zusammen mit der Seite, zu der sie gehören. Nah genug, dass Dune einen Migrationsbefehl mitliefert, der eine Grav-Site grösstenteils unverändert importiert, zusammen mit Importern für WordPress und Hugo. Hugo und die statischen Website-Generatoren haben das Leistungsargument längst bewiesen. Aber statische Generierung gibt den Server auf, und damit alles, was ein Server einfach macht: Authentifizierung, Formulare, Kommentare, Suche, Personalisierung, ein Admin-Panel. Dune behält die Flat Files und behält den Server. Seiten werden server-seitig auf Anfrage gerendert, mit Caching und ETags, die die Arbeit übernehmen, die ein statischer Build sonst vorab erledigen würde.
Echos von Helma
Leser des vorherigen Artikels werden sich an Helma erinnern, den Javascript-Applikationsserver auf der JVM, dessen Community ich viele Jahre lang angehörte. Beim Bau von Dune bemerkte ich immer wieder, wie viele von Helmas Ideen einfach gute Ideen waren, die auf eine Laufzeit warteten, die sie ohne den umgebenden Aufwand ausdrücken konnte.
Helma hatte Skins — HTML-Templates mit Platzhaltern, die Makros aufriefen, streng getrennt von der Logik, die sie fütterte. Dunes Themes sind dieselbe Trennung mit moderner Syntax: TSX-Templates, die die vollständig gerenderte Seite empfangen und das Dokument zurückgeben. Templates sind nur server-seitig; kein Javascript aus einem Template wird jemals an den Browser gesendet. Wo Helmas Skins Makros aufriefen, rendern Dunes Templates Komponenten. Und wo E4X es Helma einst erlaubte, Markup als natives Sprachmerkmal zusammenzusetzen, liefert Freshs JSX — mit Deno, das die Kompilierung transparent handhabt — endlich dieselbe Erfahrung ohne sichtbaren Build-Schritt, die wir verloren, als E4X aus der Sprache entfernt wurde.
Helma bildet den Anfragepfad auf einen Objektbaum ab; Dune tut dasselbe, indem es ihn auf den Ordnerbaum abbildet und so direkten Dateisystemzugriff auf den Objektbaum und die Möglichkeit gibt, statische Daten direkt zu platzieren. Ein Ordner ist eine Route, eine Datei ist eine Seite, und das Ordnungspräfix im Ordnernamen ist die Navigationsreihenfolge. Es ist die Art von Konvention, die sich beim ersten Blick auf eine Inhalts-Verzeichnisliste von selbst erklärt.
Das tiefere Erbe ist architektonische Zurückhaltung. Helma war ein Framework, das man im Kopf behalten konnte. Der Web-Stack seitdem hat Schichten angehäuft — Transpiler, Bundler, Lock-Dateien für die Lock-Dateien — die jeweils ein echtes Problem gelöst und das mentale Modell gemeinsam vergraben haben. Denos ganzes Angebot besteht darin, diese Schichten abzuwerfen ohne das zu verlieren, was sie geboten haben, und Dune versucht, dasselbe Tool eine Ebene höher zu sein: ein CMS, das man im Kopf behalten kann.
Kein Javascript als Standard
Dune übernimmt Freshs Islands-Architektur vollständig, und es lohnt sich zu erklären, was das für eine Content-Site bedeutet: Standardmässig liefern Seiten überhaupt kein Javascript. Der Server rendert HTML, der Browser zeigt es an. Ein mit Dune erstelltes Blog sendet Markup und Stylesheets und nichts sonst — der Browser des Lesers parst kein einziges Skript, um einen Artikel anzuzeigen.
Interaktivität ist opt-in, pro Komponente. Eine Suchleiste, ein Bildkarussell, ein Kommentarformular — jedes ist eine Insel, eine kleine Preact-Komponente, die unabhängig hydratisiert, während der Rest der Seite inert bleibt. Themes deklarieren Inseln, indem sie diese in einem islands/-Ordner ablegen; Dune entdeckt und bündelt sie beim Start. Das Bündeln geschieht einmal, wenn der Server startet, nicht als Build-Schritt, den man ausführt und committe. Es gibt kein dist/-Verzeichnis in einem Dune-Projekt.
Das ist der Teil des modernen Stacks, den ich am meisten aus der statischen Website-Welt bewahren wollte: die Disziplin, für Javascript nur dort zu zahlen, wo es etwas bringt. Dreissig Jahre, Javascript schnell zu machen, wie im vorherigen Artikel ausgiebig gefeiert, sollten keine Entschuldigung sein, Megabytes davon zu liefern, um Absätze zu rendern.
Als Anwendung wachsen
Das Problem mit den meisten minimalen Systemen ist die Klippe an ihrem Ende: der Tag, an dem ein Projekt eine Funktion braucht, die das minimale System nicht hat, und man auf etwas Schwereres umsteigt. Dune ist darauf ausgelegt, diese Klippe nicht zu haben, weshalb ich es als CMS beschreibe, das von Markdown-Inhalten zu vollwertigen Web-Apps wächst.
Der Wachstumspfad ist inkrementell. Frontmatter-Abfragen werden zu Collections — ein Blog-Index ist ein Ordner plus ein paar Zeilen YAML, die deklarieren, wie seine Kinder aufgelistet werden. Taxonomien taggieren Seiten und generieren die Archivansichten. Wenn strukturierte Daten über Frontmatter hinauswachsen, bieten Flex-Objekte schema-definierte Datensätze mit generiertem Admin-CRUD. Formulare, Kommentare und Webhooks sind eingebaut. Authentifizierung für öffentliche Sites unterstützt Passwörter, OAuth und Magic Links, mit rollenbasierter Zugriffskontrolle und Inhalts-Gating, wenn Teile einer Site nur für Mitglieder zugänglich sein sollen. Multisite betreibt mehrere Sites aus einem Prozess. Und unter alledem ein Plugin-System, das die Hooks der Engine offenlegt — Response-Transforms, Custom-Format-Handler, Admin-Services, geplante Jobs, Browser-Einstiegspunkte — sodass die Funktionen, die Dune nicht hat, hinzugefügt werden können, ohne die vorhandenen zu forken.
Jedes dieser Features ist inaktiv, bis es verwendet wird. Eine Dune-Site, die nur ein Blog ist, trägt keines davon; dieselbe Installation kann später Zahlungen aufnehmen, ohne die Plattform zu wechseln. Diese Kontinuität — dieselben Dateien, dieselben Konventionen, von der Broschüren-Site bis zur Anwendung — ist das Designziel, das die meisten Entscheidungen geprägt hat.
Bearbeiten, wo der Inhalt lebt
Es gibt eine offensichtliche Lücke in jedem Flat-File-System: Der Inhalt sind Dateien, aber nicht jeder, der ihn bearbeiten muss, möchte einen Texteditor öffnen. Dune liefert ein Admin-Panel — Seiten, Medien, Benutzer, Verlauf — aber der Teil, den ich interessanter finde, ist das Inline-Editing, weil es zeigt, wie die Plugin-Architektur und die Flat Files zusammenarbeiten.
Themes markieren bearbeitbare Bereiche mit typisierten Marker-Komponenten — EditableMarkdown für den Body, EditableText für Inline-Felder — importiert aus Core und server-seitig gerendert, ohne eigenes JavaScript. Dieser Marker-Vertrag ist die gesamte Kopplung zwischen einem Theme und dem Bearbeitungssystem. Der Editor selbst ist ein Plugin: Er findet die Marker, hängt ein schwebendes Bearbeitungs-Handle für eingeloggte Admins an und öffnet einen WYSIWYG-Editor über die Markdown-Quelle der Seite. Mehrere Admins können gleichzeitig dieselbe Seite bearbeiten; ihre Änderungen werden konflikfrei durch ein CRDT zusammengeführt und landen in derselben Markdown-Datei auf der Festplatte, die man ebenso gut in einem Texteditor hätte öffnen können. Die Datei bleibt die einzige Quelle der Wahrheit, durch welche Tür man auch eingetreten ist.
Und weil Marker ein Admin-exklusiver Vertrag sind, entfernt Dune sie aus jeder Antwort, die an jemanden ohne eine validierte Bearbeitungssitzung gesendet wird. Anonyme Besucher erhalten sauberes Markup ohne Bearbeitungs-Fingerabdruck und ohne Hinweise auf den Speicherort des Inhalts auf der Festplatte. Solche Standardwerte sind überall verstreut: Denos Berechtigungs-Sandbox bedeutet, dass der Prozess nur das berührt, was ihm ausdrücklich gewährt wird, Bearbeitungsendpunkte authentifizieren server-seitig unabhängig davon, was ein HTML behauptet, und der Inhaltsverlauf zeichnet auf, wer was geändert hat. Sicherheit als Standard-Haltung, in der Laufzeit und in der Anwendung — diese Lektion hat das Ökosystem auf die harte Tour gelernt, und Dune erbt sie bewusst.
Inhalt, mit dem ein Agent arbeiten kann
Die Entscheidung, Inhalte als Dateien zu halten, erweist sich als wichtig über den menschlichen Autorenfall hinaus. Markdown ist das, womit grosse Sprachmodelle trainiert wurden — sie schreiben es fliessend, lesen es ohne Parse-Fehler und halluzinieren keine Syntax dafür, wie sie es gelegentlich mit proprietären Exportformaten oder CMS-spezifischen Template-Sprachen tun. Der vollständige Inhalt einer Flat-File-Site ist für jedes Tool sichtbar, das ein Verzeichnis lesen kann. Die Ordner-als-URL-Konvention ist so lesbar, dass ein Agent die vollständige Struktur einer Site aus einer Verzeichnisliste verstehen kann, ohne API-Dokumentation oder ein Datenbankschema zu konsultieren. Frontmatter-YAML ist strukturiert und vorhersehbar — ein Agent kann die Metadaten einer Seite lesen, ein Feld patchen und es zurückschreiben, ohne eine GUI, einen REST-Aufruf oder ein Migrationsskript.
Das Fehlen eines Build-Schritts ist ebenfalls wichtig. Ein Agent, der eine neue Inhaltsdatei erstellt, kann sie sofort gegen einen laufenden Server validieren — es gibt keine Pipeline zu verstehen, keine Aufwärmphase, kein Ausgabeverzeichnis zu inspizieren. Und da Inhalte konventionsgemäss in Git verfolgt werden, bekommen Agents dieselbe sichere Arbeitsumgebung, die ein Entwickler bekommt: Branch, anwenden, verifizieren, committen oder zurückrollen. Dune ist mit KI-Agents im Sinn entworfen — und das Flat-File-Modell ist der Grund, warum es funktioniert: einfache Dateien, eine direkte Dateisystem-Schnittstelle, kein Build-Schritt, git-verfolgte Inhalte, ein laufender Server, den man abfragen kann. Dieselben Entscheidungen, die es für einen Entwickler einfach machen, machen es für einen Agent lesbar.
Tools für Agents
Dune liefert Skill-Dateien — kompakte Domänen-Referenzen, die von dune new in .claude/skills/ installiert und per dune update:skills aktualisiert werden. Jeder Skill deckt einen Bereich des Systems ab: Content-Konventionen, das Plugin-Modell, die Schema-Schicht, Authentifizierung, Hintergrundjobs, E-Mail. Ein Agent, der eine neue Sitzung startet, liest die relevanten Skills und hat sofort das nötige Musterwissen, ohne die Dokumentation zu durchforsten oder Konventionen aus dem Quellcode abzuleiten.
Darüber hinaus stellt der eingebaute MCP-Server neun Lese-Tools über stdio bereit — Abfragen von Seiten, Ausführen von Suchen, Inspizieren von Collections, Lesen von Taxonomie, Untersuchen von Config, Auflisten verfügbarer Templates und Blueprints sowie Abrufen der Rohquelle einer Seite. Ein Agent kann ein vollständiges Bild einer laufenden Site erhalten, ohne jemals das Dateisystem zu berühren.
Für Schreiboperationen akzeptiert die Change API (POST /admin/api/dev/apply) strukturierte JSON-Operationen — eine Datei erstellen oder aktualisieren, eine Datei löschen, Frontmatter-Felder patchen, Config ändern, ein Plugin installieren — mit einem Dry-Run-Modus, der meldet, was sich ändern würde, bevor irgendetwas die Festplatte berührt. dune validate führt dieselben Prüfungen durch, die der Server beim Start durchführt, sodass ein Agent einen Satz von Änderungen vorab prüfen kann, bevor er sie committet. Das Config-Schema ist als JSON-Schema exportierbar für IDE- und Agent-Autovervollständigung. Und llms.txt und llms-full.txt werden von der Docs-Site in dem Format bereitgestellt, auf das sich das Ökosystem geeinigt hat, um Dokumentation für Modelle zugänglich zu machen.
Von Anfang an offen
Der vorherige Artikel verfolgte, wie Netscape, nachdem es mit LiveWire technisch so vieles richtig gemacht hatte, alles zunichte machte, indem es die Implementierung proprietär hielt — versuchte, das Web zu besitzen, anstatt daran teilzunehmen. WebCrossing wiederholte den Fehler, und Helma, obwohl Open Source, kam an, bevor die Verbreitungsinfrastruktur existierte, die es zu einem breiten Publikum hätte tragen können.
Dune kann auf jedem Punkt den entgegengesetzten Weg einschlagen, wobei dreissig Jahre angesammelter Infrastruktur es fast mühelos machen. Der Code ist MIT-lizenziert. Er wird auf JSR veröffentlicht, dem TypeScript-nativen Registry, wo jede Version ihre Typen, ihre Dokumentation und Provenienz-Attestierung trägt, die das Paket mit dem genauen Commit und CI-Run verknüpft, der es produziert hat. Die Installation der CLI ist ein Befehl; das Upgrade einer Site ist das Bearbeiten einer Versionsnummer in einer Datei. Die Themes sind offen, die Plugin-Schnittstellen sind dokumentiert, und die Docs selbst sind ein öffentliches Git-Repository — natürlich von einer Dune-Site bereitgestellt.
Nichts davon garantiert Adoption, und nach dreissig Jahren mache ich mir keine Illusion, dass das besser konzipierte System standardmässig gewinnt. Aber die Fehler des geschlossenen Weges sind dokumentierte Geschichte, nacherzählt in meinem vorherigen Artikel. Die Teilnahme am offenen Ökosystem ist die einzige Strategie, die sich je zusammengesetzt hat.
Dune ist jung, die Versionsnummern beginnen noch mit Null, und es bleibt viel zu bauen. Wenn Flat Files, standardmässig kein Javascript und ein CMS, das man im Kopf behalten kann, wie Ihr Stack klingen, ist der Ausgangspunkt getdune.org — der Quickstart bringt Sie in fünf Minuten dazu, eine Site bereitzustellen, und die Migrationsbefehle werden Ihre Inhalte gerne aus welcher Datenbank auch immer herausschnitzen.
Die Dateien sind bereit, wenn Sie es sind.