Ein Cockpit, das stündlich schaut
In neun Stunden entstand aus vier unabhängigen Produkt-APIs, einer Orchestrierungsschicht, privaten JSON-Artefakten, ContentKit und WikiKit ein kleines Reporting-System – samt der Fehler, die den Ansatz besser gemacht haben.
Neun Stunden, fünf Repositories, 211 geänderte Dateien und gut 10.000 neue Zeilen: Das klingt zunächst nach einem viel zu großen Umbau für einen Samstag. Das sichtbare Ergebnis ist dagegen klein. Ein privates Cockpit zeigt einmal pro Stunde, was in vier eigenständigen Produkten passiert ist. Es ordnet dieselben Daten zusätzlich als Tages-, Wochen-, Monats- und Jahresreport ein. Fehlt eine Quelle, steht dort eine Lücke. Liefert eine Quelle null Ereignisse, bleibt die Null eine Null. Das System versucht nicht, mehr zu wissen, als seine Daten hergeben.
Genau darin liegt der eigentliche Fortschritt. Der erste Entwurf wollte zu früh ein eigenes Reporting-Produkt werden: besondere Endpunkte, besondere API-Keys, lokale Aggregatdateien und ein vermeintlicher Status-Host, den es gar nicht gab. Nach mehreren Korrekturen blieb eine allgemeinere Konstruktion übrig. Jedes Produkt berichtet über seine eigene API. Eine Orchestrierungs- und Ausführungsschicht ruft diese APIs auf. Die Ergebnisse landen als versionierte JSON-Artefakte in einem privaten S3-kompatiblen Bucket. ContentKit macht daraus geschützte Reports. WikiKit nimmt die Findings auf, damit aus wiederkehrenden Beobachtungen überprüfbares Wissen werden kann.
Das ist der Auftakt einer Serie über diesen Ansatz. Es geht um Technik, aber mindestens ebenso um Methode: Warum die ersten Lösungen falsch zugeschnitten waren, weshalb stündliche Reports hier besser passen als Echtzeitmetriken und wie kleine Produkte zusammenarbeiten können, ohne eine Suite zu werden.
Der Bericht gehört nicht in die Produkte
ContentKit veröffentlicht Inhalte. WikiKit kuratiert Wissen. SlideKit baut Präsentationen. Das vierte Produkt führt Abläufe aus und verbindet APIs. Keines davon ist ein „Cockpit-Modul“, und keines sollte von diesem Anwendungsfall abhängig werden. Ein solcher Report ist schließlich nur ein möglicher Verbraucher derselben allgemeinen Produktstatistiken.
Die wichtige Architekturentscheidung lautet deshalb: Ein Produkt besitzt seine Daten und bietet eine verdichtete Sicht über seine API an. Es gibt keine fremden SQL-Abfragen auf seine PostgreSQL-Datenbank. Der Sammler muss weder Tabellen noch Migrationen noch interne Statuswerte kennen. SlideKit hat gar keine eigene Datenbank und passt trotzdem in denselben Vertrag.
Eine Antwort sieht im Kern unspektakulär aus:
{
"bucket": "hour",
"from": "2026-07-18T13:00:00.000Z",
"to": "2026-07-18T14:00:00.000Z",
"totals": {
"builds": 0,
"failed": 0
}
}
bucket, from und to bedeuten in allen Produkten dasselbe. Die Fachwerte unterscheiden sich: ContentKit kennt Releases, Reader, Audio und Engagement; WikiKit kennt Ingests, Wissen und LLM-Nutzung; SlideKit kennt Builds; die Ausführungsschicht kennt Workflows, Connectoren und Laufqualität. Allgemein ist der zeitliche Vertrag, fachlich bleibt die Nutzlast.
flowchart LR
C["ContentKit API<br/>Publishing-Statistiken"] --> O["Orchestrierung<br/>stündlicher Lauf"]
W["WikiKit API<br/>Wissens-Statistiken"] --> O
S["SlideKit API<br/>Build-Statistiken"] --> O
X["Ausführungs-API<br/>Workflow-Statistiken"] --> O
O --> J["Private JSON-Artefakte<br/>Supabase Storage"]
J --> R["ContentKit<br/>private Reports"]
O --> K["WikiKit<br/>Findings und Wissen"]
Der Trick ist die dünne Mitte. Die Orchestrierung kennt HTTP, Zeitfenster, Schemas und Wiederholungsregeln. Sie kennt keine ContentKit-Tabellen und keine WikiKit-Interna. Die Produkte kennen das Cockpit nicht. Dadurch kann jedes unabhängig veröffentlicht, zurückgerollt oder ersetzt werden.
Warum einmal pro Stunde reicht
Das Cockpit ist kein Incident-System. Für Ausfälle, laufende Anfragen und Alarmierung existiert weiterhin ein getrennter Weg. Hier geht es um Fragen wie: Wurden Inhalte veröffentlicht? Sind Workflows erfolgreich gelaufen? Wie entwickelt sich die Nutzung? Welche Datenquelle fehlt noch? Was hat sich gegenüber der Vorwoche verändert?
Für solche Fragen ist ein abgeschlossenes Zeitfenster wertvoller als ein flackernder Sekundenwert. Der stündliche Lauf beginnt sieben Minuten nach der vollen Stunde und wertet ausschließlich die vorherige, vollständig abgeschlossene Stunde aus. Er wartet nicht auf „jetzt“, sondern arbeitet mit einer klaren Ober- und Untergrenze.
schedule:
cron: "7 * * * *"
timezone: UTC
input:
window: previous_closed_hour
output:
source_snapshots: 4
fact_set: 1
analysis: 1
manifest: 1
Das spart eine eigene Metrikdatenbank, einen Scraper und eine ständig laufende Dashboard-Abfrage. Es nimmt außerdem Druck aus dem Datenmodell. Ein Produkt muss keine hochauflösende Zeitreihe anbieten, wenn für eine längerfristige Entscheidung zwölf Monatswerte genügen.
Prometheus löst eine andere Aufgabe sehr gut: Zeitreihen werden typischerweise per HTTP-Pull eingesammelt und mit PromQL ausgewertet. Grafana kann viele Datenquellen abfragen, transformieren und interaktiv visualisieren. Wer Sekundenauflösung, Alerting, frei kombinierbare Abfragen oder tiefe Ursachenanalyse braucht, sollte diese Fähigkeiten nicht nachbauen. Mein Cockpit hat bewusst weder PromQL noch 150 Datenquellen-Plugins, keine interaktiven Ad-hoc-Queries und keine Alarmierungsengine.
Was es dafür gewinnt, ist Überschaubarkeit. Die Eingabe besteht aus vier HTTP-Antworten. Der Übergabevertrag ist JSON Schema. Die Darstellung ist Markdown mit begrenzten Report-Bausteinen. Ein Report ist ein unveränderlicher Release und lässt sich wie anderer Content prüfen, archivieren und wieder öffnen.
Was ein Leser tatsächlich sieht
Die erste brauchbare Cockpit-Seite war nicht das erste Deployment. Zunächst erschien nur eine große Überschrift mit viel Weißraum. Private Seiten hatten zwar Inhalt, aber keine Navigation. Danach zeigte die Startseite Stunden-, Wochen-, Monats- und Jahresreports – der Tagesreport fehlte. Später sortierten gemischte Zeitstempel manche Karten falsch. Jede dieser Pannen war sichtbar, klein genug zum Beheben und wichtig genug für einen Vertragstest.

Heute beginnt die Seite mit fünf Zeiträumen. „Stündlich“ beantwortet die Frage nach dem jüngsten abgeschlossenen Fenster. „Täglich“ glättet einzelne Stunden und eignet sich für den morgendlichen Überblick. Der Wochenreport zeigt eine Entwicklung, ohne dass Wochenenden oder nächtliche Ruhe sofort wie ein Problem wirken. Monat und Jahr schaffen Abstand für Entscheidungen über Produkt, Betrieb und tatsächliche Nutzung.
Darunter bleibt der Reportverlauf erhalten. Das ist keine dekorative Historie. Wer eine neue Zahl sieht, kann den vorherigen Stand öffnen. Ein späterer Report überschreibt keinen früheren. Die neuesten Zeiger machen den aktuellen Stand bequem, die unveränderlichen Artefakte halten ihn nachvollziehbar.
Der Einzelreport erklärt jeden Abschnitt mit drei Fragen: Was ist hier zu sehen? Warum ist es wichtig? Wie lässt sich der Wert lesen? Eine Null bei technischen Fehlern ist gut. Eine Null beim Engagement kann schlicht bedeuten, dass noch kein Connector für diese Datenquelle existiert. Ein fehlender Wert darf nicht durch eine plausible Schätzung ersetzt werden.

ContentKit rendert die Diagramme intern mit Apache ECharts serverseitig als SVG. Der Autor liefert nur eine begrenzte Markdown-Tabelle. Im Browser läuft keine Chart-Runtime, und die Quelldaten bleiben unter dem Diagramm lesbar. Das ist weniger interaktiv als Grafana, lässt sich aber drucken, diffen und ohne JavaScript ausliefern.
Fünf Zeiträume, fünf verschiedene Fragen
Der zunächst vergessene Tagesreport war mehr als ein fehlender Zeitraum. Er deckte auf, dass die Intervalle zu sehr als technische Aggregationen gedacht worden waren. Für Leser haben sie unterschiedliche Aufgaben.
Der Stundenreport prüft den Datenfluss. Sind alle vier Quellen angekommen? Ist der Lauf vollständig? Gibt es ein technisches Signal, das zeitnah untersucht werden sollte? Er ist die kürzeste Rückmeldung, ohne in Incident-Analyse abzurutschen.
Der Tagesreport ist die erste betriebliche Verdichtung. Ein Team kann ihn am Morgen lesen, ohne 24 einzelne Stunden zu vergleichen. Für Kommunikation und Reichweite wird sichtbar, ob Nutzungsdaten überhaupt vorhanden sind. Produktverantwortliche erkennen Releases und neue Inhalte. Technik sieht Ausführungen und Fehler im selben Dokument, ohne dass diese Perspektive die anderen Bereiche verdrängt.
Woche und Monat beantworten Fragen nach Richtung. Eine ungewöhnlich ruhige Stunde ist dort kaum relevant; wiederholt fehlende Daten oder sinkende Aktivität werden dagegen sichtbar. Der Jahresreport ist kein größer gezeichneter Stundenreport. Er soll Entscheidungen über Schwerpunkte, Kosten und fehlende Messfähigkeit stützen.
Diese Unterscheidung schützt vor einer häufigen Dashboard-Falle: dieselben Kennzahlen nur mit einem anderen Datumsfilter zu wiederholen. Eine sinnvolle Verdichtung darf Metriken weglassen, andere hervorheben und ihre Berechnung erklären. Der Vertrag hält deshalb neben dem Zeitraum auch Abdeckung und Datenqualität fest.
{
"cadence": "daily",
"period_start": "2026-07-17T00:00:00.000Z",
"expected_windows": 24,
"observed_windows": 24,
"coverage_ratio": 1,
"missing_sources": []
}
Für Einsteiger ist coverage_ratio entscheidend: Der Wert sagt nicht, ob das Unternehmen einen guten Tag hatte. Er sagt, ob der Report genügend Eingaben besitzt, um überhaupt belastbar gelesen zu werden. Datenqualität steht damit nicht im Kleingedruckten, sondern neben den Ergebnissen.
Was im Alltag wegfällt
Der erste Gewinn ist bodenständig: weniger Suchen, weniger Copy-and-paste und weniger Rückfragen danach, welcher Stand aktuell ist. Das Team verwendet seine Zeit auf Auffälligkeiten und Entscheidungen, nicht auf das Zusammentragen einer Lage, die sich aus den Systemen ableiten lässt.
Ein technischer Verantwortlicher muss nicht vier Dienste und ihre Logs öffnen, um die letzte abgeschlossene Stunde einzuordnen. Für noch fehlende Analytics-Connectoren erscheint eine sichtbare Leerstelle statt einer stillschweigend unvollständigen Tabelle. Produktverantwortliche sehen Veröffentlichungen neben Betriebsqualität. Wer später eine Entscheidung prüfen möchte, öffnet den damaligen unveränderten Report und nicht ein Dashboard, das inzwischen dieselbe URL mit neuen Daten füllt.
Probleme in Veröffentlichung oder Datenfluss fallen früher auf. Fehlende Messwerte werden nicht als Gewissheit weitergereicht. Erkennt ein Team eine wiederkehrende Ursache, bleibt sie als geprüftes Wissen erhalten und muss beim nächsten Mal nicht neu rekonstruiert werden. Menschen, die die Produkte verwenden, merken davon vor allem das Ergebnis: Inhalte sind verlässlicher, auf Probleme wird schneller reagiert und bereits verstandene Fehler werden seltener wiederholt.
Auch die Entwicklung profitiert. Ein neuer Datenlieferant braucht einen allgemeinen Statistik-Endpunkt und einen Connector. Das Cockpit erhält dadurch weitere Fakten, ohne dass die Produktdatenbank geöffnet oder die Reportseite neu programmiert werden muss. Eine neue Darstellung entsteht als Markdown und kontrollierte Direktive. Das hält Änderungen klein und ihre Wirkung überprüfbar.
Die Fehler waren Architekturarbeit
Der wertvollste Fehler stand ganz am Anfang: Ein Hostname wurde als existent behandelt, obwohl er nie angelegt worden war. Damit war auch die gedankliche Trennung falsch. „Status“ klang nach öffentlicher Betriebsseite und Echtzeit, gewünscht war ein privater Report. Der Name verführte zu einer Lösung, bevor die Aufgabe sauber beschrieben war.
Ein zweiter Entwurf legte besondere private Reporting-Endpunkte und eigene Schlüssel nahe. Das hätte den Anwendungsfall in jedes Produkt gedrückt. Die Korrektur bestand aus allgemeinen /stats-APIs mit normalen, eng begrenzten Leserechten. Die Ausführungsschicht greift ausschließlich über diese Produkt-APIs zu.
Weitere Fehler waren weniger konzeptionell, aber ebenso lehrreich:
- Eine Caddy-Konfiguration verlor beim Umbau einen bestehenden Wiki-Host und musste wiederhergestellt werden.
- Ein schneller Hotfix war live, stimmte aber byteweise nicht mit dem veröffentlichten Release-Artefakt überein. Ein korrigierendes Release stellte die Reproduzierbarkeit wieder her.
- Gemischte Datumsformate sortierten Reports nach Zeichenketten statt nach ihrer fachlichen Zeit.
- Ein Workflow konnte beim Wiederholen doppelte Content-Releases erzeugen, bis ein eindeutiger Deduplizierungsschlüssel Teil des Vertrags wurde.
- Alte Backups belegten unnötig Speicher. Die neue Aufbewahrung löscht nur nach einem vollständigen, geprüften Backup-Lauf und behält genau den jüngsten kompletten Satz.
Diese Punkte erklären, warum lokale Tests allein nicht genügten. Alle Produkt-Repositories erhielten Unit-, Vertrags-, Integrations- und Dokumentationstests. OpenAPI, llms.txt, llms-full.txt, README und Fachdokumentation wurden mitgezogen. Danach folgten reale Binaries, veröffentlichte Release-Artefakte, Smoke-Tests gegen PROD und Runtime-Journale. Der letzte Schritt ist ein strenger 24-Stunden-Lauf mit mindestens 24 geplanten Ausführungen.
Aus Daten wird noch kein Wissen
Ein Report beantwortet, was in einem Zeitfenster messbar war. Er erklärt nicht automatisch, ob eine Abweichung bekannt, gewollt oder schon entschieden ist. Dafür endet die Kette nicht bei ContentKit.
Findings werden zusätzlich an WikiKit übergeben. Die Quelle bleibt unverändert archiviert. Neue Konzepte oder Claims sind zunächst Vorschläge und werden erst nach Prüfung sichtbares Wissen. Ein Team kann dadurch später fragen, ob ein wiederkehrendes Muster schon einmal auftrat, welche Quelle es belegt und was damals entschieden wurde.
flowchart TD
D["Produktdaten<br/>abgeschlossenes Fenster"] --> F["Fakten<br/>schema-validiert"]
F --> A["Analyse<br/>Findings und Lücken"]
A --> P["Privater Report<br/>lesen und entscheiden"]
A --> Q["Wissensvorschlag<br/>Quelle bleibt erhalten"]
Q --> H["Menschliche Prüfung"]
H --> K["Kuratierter Wissensstand"]
K --> A
So entsteht eine Rückkopplung, ohne dem Modell die Entscheidung zu überlassen. Die Maschine kann sammeln, vergleichen und vorschlagen. Ein Report kann erläutern, dass Daten fehlen. Verbindliches Wissen entsteht erst durch Quellen und Freigabe.
Was sich seit damals geändert hat
Der ältere Beitrag Markdown Reports statt Dashboard-Zwang hat einen festen, diffbaren Report einer flüchtigen Live-Ansicht gegenübergestellt. Das Mission Cockpit setzt diese Idee nun regelmäßig und über mehrere Produkte um. Vor dem Markdown liegen validierte Quell-Snapshots, ein gemeinsamer Faktensatz und ein gehashtes Manifest. Danach folgen private Revision, Release und Reader-Zugriff. Aus einem einzelnen nachvollziehbaren Report ist eine wiederholbare Kette mit Stunden-, Tages-, Wochen-, Monats- und Jahresständen geworden.
Was dadurch einfach bleibt
Dauerhafte Plattformarbeit fällt weg: kein zusätzlicher Prometheus-Server, keine Grafana-Instanz, kein fünftes Produkt mit eigener Datenbank, keine direkten Fremdzugriffe auf vier Datenmodelle und keine handgebauten Reportseiten. Vorhandene Fähigkeiten werden verbunden: HTTP-Connectoren und Scheduler für die Ausführung, Supabase Storage für private JSON-Artefakte, ContentKit für Authentifizierung, Releases und Charts, WikiKit für Herkunft und Verdichtung.
Einfach bleibt auch die Trennung der Fachbereiche. Finanzen könnten eine eigene Produkt-API, eigene Credentials, einen eigenen Storage-Prefix oder Bucket, eine eigene private ContentKit-Site und einen eigenen WikiKit-Space erhalten. Der gemeinsame Ablauf bliebe gleich, die Rechte und Daten würden sich nicht vermischen. Dazu folgt ein eigener Beitrag.
Die Grenze ist ebenso klar. Dieses Cockpit ersetzt keine hochauflösende Observability, kein Log-Exploration-System und keine Incident-Analyse. Es ist ein ruhiger, überprüfbarer Berichtskanal für Produkt, Technik, Nutzung und Datenqualität. Seine Stärke liegt nicht darin, alles zu können. Sie liegt darin, dass jede Schicht genau eine verständliche Aufgabe behält – und dass eine fehlende Zahl ehrlich einfacher bleibt als eine erfundene Antwort.
Weiterführende Quellen
- ContentKit – Markdown-first Publishing und Reports
- WikiKit – kuratiertes Wissen mit Quellen und Review
- SlideKit – Präsentationen als versionierte Artefakte
- W3C Trace Context
- OpenTelemetry Service Semantic Conventions
- OpenTelemetry Deployment Attributes
- Supabase Storage: S3 Compatibility
- Apache ECharts: Server-Side Rendering
- Prometheus Overview
- Grafana Dashboards
- Markdown Reports statt Dashboard-Zwang
Wie fandest du diesen Beitrag?
Kommentare