
Webanwendung

Eine elterngesteuerte Webanwendung, die tägliche Gewohnheiten von Kindern im Alter von 4–12 Jahren durch Gamification unterstützt. Kinder erledigen Aufgaben, sammeln Sterne und XP, entwickeln ihren Charakter weiter und sammeln Abzeichen. Eltern verwalten Aufgaben, führen Genehmigungsprozesse durch und verfolgen den Fortschritt.
Um die täglichen Gewohnheiten ihrer Kinder zu verfolgen, greifen Eltern entweder zur Papierliste oder zu Aufgaben-Apps, die für Erwachsene entworfen wurden. Beide scheitern an derselben Stelle: Das Kind kann seine eigene Belohnung selbst bestätigen, wodurch das System seine Glaubwürdigkeit verliert. Das eigentliche Problem der Anwendung war nicht die Oberfläche, sondern die Trennung der Berechtigungen.
Zwei unterschiedliche Nutzer in einem Produkt. Ein Kind zwischen 4 und 12: eingeschränktes Lesen und Schreiben, große Touch-Ziele, Belohnungs-Feedback muss sofort sichtbar sein. Ein Elternteil: definiert Aufgaben, bestätigt erledigte oder gibt sie mit einer Coaching-Notiz zurück, verfolgt die wöchentliche Entwicklung — und will sicher sein, dass das Kind in diesen Ablauf nicht eingreifen kann.
Alleiniger Entwickler. Produktumfang, Datenmodell, Supabase-Schema und RLS-Policies, die Server-Action-Schicht, zwei getrennte Oberflächen (Kind/Eltern), PWA-Einrichtung, der KVKK-Cookie-Ablauf und das Vercel-Deployment.
Eine einzige Next.js-16-App-Router-Anwendung. Der Lesepfad läuft über Server Component → lib/dal/*, der Schreibpfad über Server Action → Zod-Validierung → Supabase. Die Autorisierung ist dreischichtig: Session-Prüfung in proxy.ts, Postgres-RLS-Policies und ein zusätzlicher Guard innerhalb der Server Action. Der Kind-Kontext (activeChild) liegt in Zustand; Serverdaten werden nicht in den Store kopiert. Der Streak-Abgleich erfolgt über einen geplanten Job unter /api/cron/reconcile-streaks. Der zweite Client des Systems liegt in einem eigenen Repository: eine mit SwiftUI geschriebene iOS-Anwendung. Dieser Client bringt kein eigenes Backend mit — er greift auf dasselbe Supabase-Schema zu, sodass Berechtigungsregeln und RLS-Policies mit dem Web identisch sind. Sein heutiger Reifegrad ist ein Gerüst: Elternanmeldung, Kindauswahl und der Ablauf der Panel-Bildschirme stehen, und die Supabase-Client-Konfiguration liegt als optionaler Loader vor — ohne Konfiguration stürzt die App nicht ab, sie läuft ohne Verbindung, und die Bildschirme füllen sich mit Beispieldaten. Die Architekturentscheidung ist also getroffen und bestätigt; die Datenanbindung ist noch nicht abgeschlossen.
Bestätigte ein Elternteil dieselbe Aufgabe in zwei Tabs oder tippte das Kind zweimal auf die Schaltfläche, konnte die Belohnung doppelt geschrieben werden. Das Update erfolgt nun im Abgleich mit dem erwarteten aktuellen Wert; der zweite Schreibvorgang wird abgewiesen.
KompromissBei einer Kollision muss dem Nutzer ein "Erneut versuchen" angezeigt werden; ein stilles Zusammenführen gibt es nicht. Gegenüber einem einfachen UPDATE bedeutet das mehr Code und mehr Fehlerpfade.
Gelangte die PIN des Kinderprofils in den Browser, käme ein Geschwister oder das Kind selbst leicht daran vorbei. Die verify_child_pin-RPC führt den Hash-Vergleich auf dem Server durch und unterbindet Brute-Force, indem sie aufeinanderfolgende Fehlversuche zählt.
KompromissJeder Profilwechsel kostet einen Netzwerk-Roundtrip, und im Offline-Betrieb lässt sich das Profil nicht wechseln.
Gerätezeit und Zeitzone sind nicht vertrauenswürdig; ein Kind könnte sich durch Ändern des Gerätedatums eine Serie erarbeiten. Der Streak wird serverseitig durch einen geplanten Job neu berechnet.
KompromissDer Streak kann sich verzögert aktualisieren, erst wenn der Cron gelaufen ist; der Nutzer sieht die Änderung nicht unmittelbar.
Supabase wurde gewählt, damit sich der kritische Teil der Autorisierung — dass ein Kind ausschließlich seine eigenen Aufgaben sehen kann — nicht im Anwendungscode, sondern als Datenbank-Policy ausdrücken lässt: Selbst bei einem Fehler in der Anwendungsschicht treten keine Daten aus. Zod dient als einziges Schema und wird sowohl bei der Server-Action-Eingabe als auch auf der Formularseite verwendet. Zustand hält nur kurzlebigen Client-Zustand; die Quelle der Serverdaten ist immer der Server.
Zwei unabhängige Autorisierungsschichten: Postgres-RLS und ein Guard innerhalb der Server Action — wird eine umgangen, stoppt die andere. Die PIN ist gehasht und mit einem Brute-Force-Zähler versehen. Cloudflare Turnstile ist bei Registrierung, Anmeldung und beim Wechsel Kind→Eltern aktiv. Die Eltern-Aktionssperre wird per Passwortprüfung vorübergehend gelöst. Security-Header und eine Report-Only-CSP sind in next.config.ts definiert. Google Analytics lädt erst nach erteilter Cookie-Einwilligung und nur in der Produktionsumgebung (KVKK).
Als PWA installierbar, mit Service Worker und einer /offline-Fallback-Seite. 26 Testdateien mit Vitest, schwerpunktmäßig zum Nebenläufigkeitsverhalten der Bestätigungs- und Belohnungsabläufe. Lighthouse mobil (eigene Messung): 100/100 — September 2026. In derselben Messung liegt der LCP-Median bei 1666 ms, die Gesamtblockierzeit bei 1 ms und CLS bei 0.
Heute würde ich die Nebenläufigkeitsgarantie nicht mit CAS in der Anwendungsschicht, sondern direkt als Datenbank-Constraint umsetzen. Bei Ustura habe ich Terminüberschneidungen mit einem Postgres Exclusion Constraint gelöst; dieser Ansatz erfordert weniger Code und verlagert die Garantie aus der Anwendung heraus in die Datenbank selbst. Zweitens würde ich die CSP nicht im Report-Only-Modus belassen, sondern sie vor dem Go-live auf Enforce umstellen — Berichte zu sammeln stoppt keinen Verstoß.
Sagen Sie mir, was Sie bauen wollen; ich sage Ihnen vorab, wie lange es dauert und wo man anfängt.