Headless mit Cockpit und JSON
Headless beschreibt in ContentKit die Trennung von Inhaltsmodell und Ausgabekanal, nicht den Verzicht auf eine Oberfläche. Die geschützte Read API liefert den Veröffentlichungsstand als JSON mit ETags und Suche, während das Cockpit Menschen über dasselbe Inhaltsmodell Zugriff auf dessen veröffentlichte Sicht gibt.
ContentKit liefert unter /cockpit eine Verwaltungsoberfläche für Menschen aus und bleibt ein Headless-System. Headless bezeichnet hier die Trennung von Inhaltsmodell und Ausgabekanal, nicht das Fehlen einer Oberfläche. Der veröffentlichte Stand existiert einmal und wird in mehrere Projektionen übersetzt: gerendertes HTML für Browser, geschütztes JSON für Systeme über die Read API. Das Cockpit gibt Menschen über dieselbe API und dasselbe Inhaltsmodell Zugriff; was es als veröffentlicht zeigt, ist genau dieser eine Stand.
Teil 6 der Serie „Contentkit – Publishing für Maschinen“.
Diese Begriffsklärung hat eine praktische Konsequenz. Eine zweite Redaktionsansicht mit eigener Datenhaltung neben dem API-System würde einen zusätzlichen Synchronisationspfad und mögliche Zustandsabweichungen erzeugen, die bei jedem Auseinanderlaufen geklärt werden müssten. Wer headless als getrennte Projektion versteht, stellt stattdessen eine andere Frage: Welche Abnehmer brauchen welchen Vertrag über denselben Veröffentlichungsstand?
Ein Vertrag für Maschinen, kein Nebenprodukt der Website
Autorisierte Systeme lesen den veröffentlichten Stand über eine eigene Read API. Sie liefert die Liste veröffentlichter Inhalte und einzelne Dokumente als JSON und bindet jeden Zugriff an den Scope content:read. Einen zweiten anonymen JSON-Ausgabekanal auf der öffentlichen Domain gibt es bewusst nicht: Die öffentliche Website bedient Browser, serverseitige Abnehmer erhalten über die geschützte Lese-API JSON. Beide Wege zeigen ausschließlich veröffentlichte Revisionen.
Ein Agent, der freigegebene Anleitungen als Grounding liest, braucht keinen Publisher- oder Admin-Schlüssel. Für die Read API ist content:read erforderlich; ob schon das Lesen ein relevanter Schaden wäre, hängt davon ab, wie vertraulich die geschützten Inhalte sind. Für reine Lesezugriffe gehört deshalb ein eigener Schlüssel mit content:read angelegt, kein wiederverwendeter Publisher-Schlüssel.
Drei Zugriffsmuster: Liste, Einzeldokument, Suche
Die Read API kennt drei wesentliche Zugriffsmuster. Die Liste verschafft einen Überblick über die veröffentlichten Inhalte, das Einzeldokument liefert einen konkreten Inhalt, die serverseitige Suche findet Inhalte aus demselben veröffentlichten Stand. Liste und Einzeldokument kommen als JSON, der Zugriff ist an den Scope content:read gebunden.
curl "$CONTENTKIT_URL/v1/sites/$SITE/published" \
-H "Authorization: Bearer $CONTENTKIT_READ_API_KEY"
Welche Filter, Felder und Grenzwerte im Einzelnen gelten, definiert der API-Vertrag im Repository. Architektonisch entscheidend ist eine andere Zusage: Alle drei Zugriffsmuster zeigen ausschließlich den veröffentlichten Stand; Entwürfe und historische Revisionen bleiben ausgeschlossen.
Das Einzeldokument: nur die veröffentlichte Revision
Ein einzelnes Dokument liefert die Read API ausschließlich in seiner veröffentlichten Fassung. Ein Entwurf oder eine beliebige historische Revision lässt sich darüber nicht abrufen, selbst wenn ein Client gezielt danach fragt. Entwürfe gehören nicht zu diesem Lesevertrag. Für Abnehmer bedeutet diese Grenze Verlässlichkeit: Was aus der Read API kommt, ist freigegeben – ein Agent kann es als Grounding verwenden, ohne versehentlich einen halbfertigen Text zu erwischen.
ETags: schwach für die Liste, stark für das Dokument
Die beiden Repräsentationen erhalten unterschiedliche ETag-Semantiken, und der Unterschied ist architektonisch begründet. Die Listenrepräsentation verwendet eine schwache ETag-Bindung an die Veröffentlichungs-Epoche: Die Liste ist eine abgeleitete Sicht, deren Gültigkeit sich mit jedem neuen Veröffentlichungsstand ändert, auch wenn einzelne Einträge unverändert bleiben. Ein einzelnes Dokument erhält dagegen einen starken ETag über den Quellhash seiner Revision – seine Identität hängt an genau dieser unveränderlichen Quelle, nicht an der Epoche der Website.
Für Abnehmer sind diese ETags die Validatoren, mit denen sich Änderungen erkennen lassen, ohne Inhalte erneut zu übertragen und zu verarbeiten. Ein sinnvoller Ablauf prüft zuerst den Listen-ETag; eine geänderte Liste ist das Signal für einen Abgleich. Der Client sendet dann in bedingten Einzelabrufen den jeweils gespeicherten Dokument-ETag als If-None-Match mit und verarbeitet nur die Antworten, deren Inhalt sich tatsächlich geändert hat. Unveränderte Dokumente beantwortet der Server mit 304 Not Modified ohne Response-Body.
sequenceDiagram
participant P as Abnehmer
participant A as ContentKit Read API
participant C as Lokaler Cache
P->>A: Liste mit gespeichertem ETag prüfen
alt Liste unverändert
A-->>P: kein neuer Stand
P->>C: vorhandenen Stand behalten
else Liste geändert
A-->>P: neue Liste und neuer ETag
loop je gelistetes Dokument
P->>A: Einzelabruf mit If-None-Match (gespeicherter Dokument-ETag)
alt Dokument unverändert
A-->>P: 304 Not Modified, kein Response-Body
else Dokument geändert
A-->>P: veröffentlichtes Dokument als JSON
P->>C: Eintrag ersetzen
end
end
end
Suche aus demselben Stand
Die serverseitige Suche verbindet ausschließlich veröffentlichte Revisionen; Entwürfe bleiben durch die Datenbankfunktion unsichtbar – eine strukturelle Garantie, kein Filter im Anwendungscode. Öffentliche Sites behalten daneben ihren statisch gebauten Suchindex: Der Website-Besucher sucht ohne Management-Zugang im statischen Index, ein Supportdienst oder Agent sucht serverseitig mit einem begrenzten Leseschlüssel. Beide Suchwege schließen Entwürfe aus und basieren auf veröffentlichten Revisionen; Ranking und Suchtechnik können abweichen.
JSON, gerendertes HTML, Suche und Cockpit müssen denselben veröffentlichten Stand abbilden. Weichen die Stände ab, können nachgelagerte Systeme veraltete Revisionen indexieren oder ausliefern.
Das Cockpit liest dieselbe Domäne
Das Cockpit unter /cockpit ist der menschliche Zugang zu genau diesem Modell. Es verwendet dieselbe API und dasselbe Inhaltsmodell wie die maschinellen Zugänge – kein separates Inhaltsmodell für die veröffentlichte Sicht, das mit der API abgeglichen werden müsste. Was ein Mensch im Cockpit als veröffentlicht sieht, ist derselbe Zustand, den die Read API als JSON liefert und den die öffentliche Website als HTML zeigt.
Headless heißt bei ContentKit also nicht, dass die Oberfläche fehlt oder die Website abgeschaltet wäre. Es heißt, dass keine Projektion – weder Cockpit noch Website noch JSON – die Quelle ist. Quelle ist der veröffentlichte Stand; jede veröffentlichte Sicht ist daraus abgeleitet.
Synchronisation braucht einen gespeicherten Stand
Aus Client-Sicht empfiehlt es sich, neben den Inhalten auch den letzten bestätigten Synchronisationsstand zu speichern: den Listen-ETag und die Dokument-ETags der übernommenen Revisionen. Erst wenn alle geänderten Dokumente erfolgreich verarbeitet wurden, wird dieser Synchronisationsstand verschoben. Bricht ein Lauf nach der Hälfte ab, bleibt der alte Stand bestehen; der nächste Versuch fragt dieselben Änderungen erneut an und überspringt bereits gespeicherte Dokumente.
Auch der Umgang mit Fehlern ist eine Client-Entscheidung. Vorübergehende Fehler wie 429 oder 5xx lassen sich mit begrenztem Abstand wiederholen; 401 und 403 deuten auf Konfigurationsfehler und sind kein Wiederholungsfall. Bleibt ein zuvor gelisteter Inhalt beim Einzelabruf aus, kann er zwischen Liste und Abruf zurückgezogen worden sein – der lokale Eintrag wird dann beim nächsten vollständigen Listenabgleich entfernt.
Trotzdem bleibt ein gelegentlicher Vollabgleich sinnvoll, weil ein fehlerhafter Client oder ein verlorener Synchronisationsstand sonst dauerhaft unbemerkt bliebe. Ein nächtlicher oder wöchentlicher Lauf liest die vollständige Liste, vergleicht sie mit dem lokalen Bestand, entfernt nicht mehr veröffentlichte Einträge und muss unveränderte Dokumente dank bedingter Einzelabrufe mit den starken ETags nicht erneut übertragen und verarbeiten. Stößt ein Ereignismechanismus einen Lauf an, bleibt die anschließend gelesene Liste die maßgebliche Sicht – wer den Gesamtzustand allein aus Ereignissen rekonstruiert, verliert ihn beim ersten verpassten Ereignis.
JSON oder gerendertes HTML bewusst auswählen
Welche Projektion ein Abnehmer verarbeitet, ist eine bewusste Entscheidung. Verarbeitet der Abnehmer Struktur und Inhalt selbst – ein Agent, ein weiterer Generator, ein Textvergleich –, ist die strukturierte JSON-Sicht der Read API geeigneter als das HTML der öffentlichen Website, aus dem Navigation und Fußzeile erst wieder herausgelöst werden müssten. Das gerenderte HTML ist die richtige Wahl, wenn ein Abnehmer die fertige Darstellung direkt zeigen will und deren Stil und erlaubte Elemente kennt.
Für einen Vektorindex vermeidet die strukturierte Sicht zusätzliches Navigations- und Layout-Markup; ob sie im konkreten Projekt die bessere Quelle ist, hängt von Renderer und Zielsystem ab. Bei einem Wechsel des Embedding-Modells werden nur die Vektoren neu berechnet, während die Inhalte unverändert aus der Read API stammen – eine technische Indexmigration wird so nicht zur redaktionellen Migration.
Ein abgeleiteter Cache – Vektorindex, mobile Datenbank, fremder Renderer – sollte den starken Dokument-ETag und die öffentliche Quellenadresse mitführen, damit Antworten und Anzeigen auf die Veröffentlichung zurückverweisen können. Die Read API bleibt die Quelle für den aktiven Veröffentlichungsstand; der Cache ist Ableitung, nicht die maßgebliche Sicht.
Praxisbeispiel: eine Support-App aus demselben Veröffentlichungsstand
Eine mobile Support-App soll auch bei schlechter Verbindung die letzten freigegebenen Anleitungen bereithalten. Ein serverseitiger Synchronisationsdienst prüft die veröffentlichte Liste über die Read API, sendet für die gelisteten Dokumente seine gespeicherten ETags in bedingten Einzelabrufen mit If-None-Match mit und verarbeitet nur die Antworten mit geändertem Inhalt. Das mobile Backend verteilt daraus ein kompaktes Paket an die Geräte; der Leseschlüssel verlässt die Serverseite nie.
Die Konsequenz für die Redaktion: Eine Korrektur wird einmal veröffentlicht und kann danach von den jeweiligen Build- und Synchronisationsprozessen der Website, der App und jeder weiteren Projektion übernommen werden – ohne zweite Redaktion, ohne HTML-Scraping, ohne Abgleichskripte zwischen zwei Datenhaltungen. Dieselbe Read API kann auch ein interner Agent verwenden, der ausschließlich freigegebene Betriebsanleitungen als Grounding liest, ebenso eine Partnerwebsite mit eigenem Design oder eine CI-Pipeline, die nach jeder Veröffentlichung die Vollständigkeit der Dokumentation prüft.
Fazit
Stand August 2026 gilt für das aktuelle Repository: Maschinen lesen den veröffentlichten Stand über die geschützte Read API mit content:read. Wer einen weiteren Abnehmer anbindet, entscheidet über Format, minimalen Scope und Synchronisationsstrategie, nicht über eine neue Quelle.
Weiterführende Quellen
- ContentKit Read API in der Gesamtdokumentation
- ContentKit Architektur: Read API und Suche
- ContentKit OpenAPI-Vertrag
Wie fandest du diesen Beitrag?
Kommentare