/projekte/partner-flutter-app/Mobile/Live/01/2026 → laufend/Alleinentwickler

Partner Flutter App

Flutter-App für Paare auf self-hosted Supabase, Sichtbarkeit über Row-Level-Security geregelt.

In der Store-Abnahme. Backend läuft seit 08/2026 auf eigener Infrastruktur statt in der Cloud.

Privates Projekt

Repository ist privat — Code zeige ich auf Nachfrage.

Private App für eine geschlossene Nutzergruppe — kein öffentlicher Zugang, keine Web-Demo.

FlutterDartRiverpodSupabasePostgreSQLRow-Level-SecurityDeno Edge FunctionsAPNsFCMDockerCoolifySwiftKotlin
project dossier

Snapshot

partner-flutter-app

Alleinentwickler

category

Mobile

status

LIVE

stack

13 tech

code

200.478 Zeilen
FlutterDartRiverpodSupabasePostgreSQLRow-Level-SecurityDeno Edge Functions
01

Zahlen zum Projekt

Code200.000Zeilen in 744 Dateien
Tabellen mit RLS83self-hosted PostgreSQL
Migrationen245SQL-Dateien
Edge Functions5Push und Abrechnung
Plattformen2iOS und Android
02

Story

Problem

Chat, Aufgaben und geteilte Medien für zwei Personen, ohne die Daten bei Drittanbietern abzulegen.

Lösung

Flutter-App auf self-hosted Supabase: RLS-Regeln je Paar, Medien-Upload mit signierten URLs, Push über FCM und APNs, Deployment per Docker und GitHub Actions.

Learnings

Partnerbasierte Sichtbarkeit gehört in die RLS-Regeln, nicht in die App-Logik.

03

Key Features

features.md
  • →Sichtbarkeit je Paar über Row-Level-Security
  • →Push über FCM und APNs, Entsperren per Biometrie
  • →Medien-Upload über signierte URLs
  • →Deployment über Docker, CI/CD über GitHub Actions
04

Über das Projekt

README.md

Flutter-App für Chat, Aufgaben, geteilte Medien und Erinnerungen zwischen zwei Personen. Läuft auf self-hosted Supabase, die Sichtbarkeit je Paar wird über Row-Level-Security in der Datenbank durchgesetzt. Push über FCM und APNs, Deployment über Docker und GitHub Actions.

05

Meine Rolle

rolle
rolle:Alleinentwickler
  • Alleinentwickler: App, Datenmodell, Backend, Store-Abgabe
  • Umstellung des Rechtemodells von Zweierbeziehung auf Gruppen
  • Row-Level-Security über alle 83 Tabellen
  • Migration von gehostetem auf selbst betriebenes Supabase
  • Push-Zustellung über APNs und FCM inklusive Outbox für Fehlversuche
  • Abo-Verifikation mit signierten Webhooks von Apple und Google
  • Build- und Release-Automatisierung für iOS und Android
06

Architektur

architecture.md

Monorepo: die Flutter-App unter app/, das Backend unter supabase/, Werkzeuge im Root hinter einem Makefile.

Das tragende Konzept im Datenmodell ist die Gruppe. Sichtbarkeit hängt nicht an einer Beziehung zwischen zwei Konten, sondern an einer Gruppenmitgliedschaft: partner_groups plus partner_group_members, und jede Feature-Tabelle führt eine group_id. Row-Level-Security prüft gegen diese Mitgliedschaft — damit gilt dieselbe Regel für Chat, Galerie, Kalender und jedes spätere Modul, ohne dass jedes Modul eigene Rechtelogik bekommt.

Eine Ausnahme ist bewusst geblieben: die Schlüssel für die gerätebasierte Ende-zu-Ende-Verschlüsselung hängen weiter an der Zweierbeziehung, nicht an der Gruppe. Ein Schlüsseltausch ist Sache genau zweier Geräte; ihn auf eine Gruppe auszuweiten würde die Zusicherung aufweichen.

Das Backend ist eine selbst betriebene Supabase-Instanz mit 83 Tabellen. Fünf Edge Functions decken Push und Abrechnung ab; die JWT-Prüfung wird pro Funktion in der config.toml gesetzt, nicht pauschal abgeschaltet — die Webhooks von Apple und Google prüfen stattdessen ihre eigene Signatur.

Konfiguration kommt über --dart-define aus einer .env, die das Makefile lädt. Es liegen keine Secrets im Dart-Code und keine in der Dokumentation.

Gruppen- und Rechtemodell

partner_groups und partner_group_members als einzige Sichtbarkeitsquelle; jede Feature-Tabelle hängt an einer group_id, geprüft über Row-Level-Security.

PostgreSQLRow-Level-Security

Chat und geteilte Inhalte

Chat mit Text, Medien, Audio, Reaktionen und Lesestatus; Galerie, Alben, privater Vault, gemeinsame Timeline und Kalender.

FlutterRiverpodSupabase

Ende-zu-Ende-Verschlüsselung

Gerätebasierte Schlüssel für die dafür vorgesehenen Inhalte, bewusst an der Zweierbeziehung statt an der Gruppe verankert.

Dart

Benachrichtigungen

Push über APNs und FCM, dazu ein Outbox-Worker als Edge Function, damit ein fehlgeschlagener Versand nicht verloren geht.

APNsFCMDeno Edge Functions

Tarife und Abrechnung

Ein monatlicher Tarif je aktiver Gruppe, verifiziert über die Stores. Webhooks von Apple und Google mit eigener Signaturprüfung statt abgeschalteter JWT-Kontrolle.

Deno Edge Functions

Admin-Bereiche

Kataloge, Moderation, Push-Versand und Nutzungsstatistiken — in der App, nicht als zweite Anwendung.

Flutter

Build- und Release-Werkzeug

Makefile für Debug- und Release-Runs, IPA- und APK-Bau, optionalen TestFlight-Upload sowie Schema- und Vollbackups der Datenbank.

DockerCoolify
07

Herausforderungen

Von der Zweierbeziehung zum Gruppenmodell
Problem

Anfangs hängte jede Feature-Tabelle an einer `relation_id` — also fest an genau zwei Konten. Jede Erweiterung hätte an jeder Tabelle einzeln nachgezogen werden müssen, und die Rechteregeln waren über die Module verteilt.

Lösung

Umstellung auf ein Gruppenmodell: eine Gruppe, Mitgliedschaften, und überall `group_id`. Row-Level-Security prüft nur noch gegen die Mitgliedschaft. Die Krypto-Tabellen blieben absichtlich bei `relation_id` — dort ist die Zweierbindung die Zusicherung, nicht ein Altlast-Detail.

Raus aus der Cloud, ohne Datenverlust
Problem

Das Backend lag in der gehosteten Supabase-Cloud. Der Umzug auf eigene Infrastruktur betraf 83 Tabellen mit Row-Level-Security, Storage, Edge Functions und die laufende Anmeldung — alles gleichzeitig.

Lösung

Umzug auf eine self-hosted Instanz hinter Coolify, mit Schema- und Vollbackup als Makefile-Ziel und der alten Cloud-Referenz als Rückfallebene. Datenbankzugriff seitdem über SSH und `docker exec`, der Ablauf steht in der Betriebsdoku.

Public Edge Functions ohne offene Tür
Problem

Store-Webhooks können kein Nutzer-JWT mitschicken. Der bequeme Weg ist `--no-verify-jwt` beim Deploy — dann ist die Funktion aber für jeden offen, der die URL kennt.

Lösung

Die JWT-Pflicht wird pro Funktion in der `config.toml` gesetzt, nicht pauschal per Flag. Die drei öffentlichen Funktionen prüfen dafür ihre eigene Signatur beziehungsweise ein Secret; die Verifikationsfunktion bleibt JWT-pflichtig. Jede neue öffentliche Funktion braucht eine dokumentierte Ersatz-Authentifizierung.

Funktionsumfang wieder verkleinern
Problem

Über die Zeit waren Bereiche dazugekommen, die weder zum Kern gehörten noch Store-Abnahmen leichter machten — KI-Funktionen, ein zweiter Speicher, mehrere Spielereien.

Lösung

Bewusster Rückbau statt Mitschleppen: KI-Dienste und die zugehörige Edge Function entfernt, zweite Speicherinstanz aufgelöst, Nebenfunktionen gestrichen. Was nicht enthalten ist, steht ausdrücklich in der Produktdoku — auch das ist eine Zusage.

08

Technologie-Entscheidungen

09

Codebasis

codebase
Codezeilen200.478
Dateien744
Sprachen
  • Dart80%
  • SQL18%
  • Shell/TypeScript2%
10

Verwandte Projekte