Vault/VAULT/hinunher/PROJECT_DESIGN.md
2026-08-17 10:54:37 +02:00

67 KiB
Raw Permalink Blame History

PROJECT_DESIGN.md — Ehrenamt-Plattform

Status: Verbindliche Architektur- und Produktgrundlage für v1
Stand: 13.08.2026
Arbeitsname: Ehrenamt-Plattform
Ausgangspunkt: bestehende CheckPoint-Ehrenamt-App, künftig als modulare Plattform für mehrere Ehrenamtsteams der Aidshilfe Köln e.V.


0. Zweck dieses Dokuments

Dieses Dokument ist die zentrale Quelle der Wahrheit für Produktumfang, Architektur, Rollen, Berechtigungen, Datenmodell, Module, UI-Grundsätze, Sicherheit, Deployment und Weiterentwicklung der Ehrenamt-Plattform.

Es ersetzt die bisher verteilten bzw. teilweise widersprüchlichen Architekturentscheidungen aus älteren Konzept-, CLAUDE.md- und TASKS.md-Ständen.

Dokument-Hierarchie

  1. PROJECT_DESIGN.md — verbindliche Produkt- und Architekturentscheidung.
  2. Code + Tests — Implementierung dieser Entscheidungen.
  3. TASKS.md — daraus abgeleiteter Arbeits-/Migrationsplan; darf keine eigene Architektur erfinden.
  4. CLAUDE.md / andere Agentenhinweise — wenn weiterhin verwendet, nur als kompakte Arbeitsanweisung mit Verweis auf PROJECT_DESIGN.md, nicht als zweite Architekturquelle.
  5. Alte Konzeptdateien werden als historische Referenz archiviert und nicht parallel gepflegt.

Bei einem Widerspruch zwischen Implementierung und diesem Dokument wird nicht geraten. Die Abweichung wird bewusst entschieden und anschließend entweder der Code oder dieses Dokument angepasst.


1. Produktziel

Die Anwendung ist eine interne, mobile-first Web-App für Ehrenamtsteams. Sie soll alltägliche Teamorganisation einfacher, klarer und datensparsamer machen als eine Mischung aus WhatsApp-/Signal-Gruppen, Tabellen, Umfragen und verstreuten Dateien.

Die erste Organisation ist:

  • Aidshilfe Köln e.V.

Die Plattform soll von Anfang an mehrere Teams dieser Organisation unterstützen. Perspektivisch sind ungefähr mehrere unterschiedliche Ehrenamtsbereiche denkbar, die nicht alle dieselben Funktionen benötigen.

Leitprinzipien

  1. Lesbarkeit und Orientierung vor Funktionsfülle.
  2. Mobile Nutzung ist der Normalfall, nicht die abgespeckte Desktop-Version.
  3. Technisch wenig affine Menschen müssen die App ohne Schulungsbedarf bedienen können.
  4. Ein Team sieht nur sein Team. Organisationsrechte sind kein Generalschlüssel für Team-Inhalte.
  5. Module sind optional pro Team. Nicht jedes Team bekommt Funktionen, die es nicht braucht.
  6. Automatisierung nur bei eindeutigem Ergebnis. Die App unterstützt Entscheidungen; sie entscheidet nicht unnötig für Menschen.
  7. Dashboard zeigt Zustände, Benachrichtigungen melden Ereignisse.
  8. Erweiterbarkeit entsteht durch klare Grenzen, nicht durch ein maximal generisches System.
  9. Deployment, Backup und Wiederherstellung werden von Anfang an mitgebaut.

2. Scope von v1

2.1 Core

Der Core enthält die Plattformfunktionen, ohne die die Anwendung als Mehrteam-System nicht funktionieren kann:

  • Passwordless Auth per E-Mail-Code / Direktlink
  • Accounts
  • Device Sessions
  • Organisation
  • Teams
  • Team-Mitgliedschaften
  • Team-Einladungen
  • Organisationsadministration
  • Rollen und Berechtigungen
  • Brand Profiles / Corporate Design
  • zentraler Datei-/Storage-Service
  • In-App-Benachrichtigungen
  • Team-Dashboard und Verwaltungsdashboard
  • Team-Mitgliederverzeichnis / freiwillige Teamkontakte
  • Profile und Avatare
  • Audit Log
  • Health-/Deployment-Grundlagen

2.2 Fachmodule

v1 enthält genau drei fachliche Module:

  1. Scheduling / Einsatzplanung
  2. Documents / Dokumente
  3. Chat / Teamkommunikation

Nicht jedes Team muss alle drei Module verwenden.


3. Bewusst nicht Teil von v1

Folgende Funktionen werden nicht in v1 gezogen, nur weil sie technisch möglich wären:

  • öffentliche Seiten
  • Klient:innen- oder Beratungsdaten
  • Bezahlfunktionen
  • Kalender-Synchronisierung
  • automatische Dienstverteilung
  • 1:1-Direktnachrichten
  • Bilder oder Dateien im Chat
  • GIFs / Sticker / Meme-Previews
  • Emoji-Reactions
  • @mentions
  • operative Push-Benachrichtigungen
  • operative E-Mail-Benachrichtigungen
  • Dokumentordner
  • Dokumentversionierung
  • vollwertige Dokumentbearbeitung
  • Fotogalerie
  • Inventar-/Materialverwaltung
  • Qualifikations-/Schulungsverwaltung
  • feingranulare Spezialrollen
  • Passkeys / 2FA als regulärer Loginweg
  • PostgreSQL in v1
  • dynamisches Installieren/Entfernen von Python-Modulen zur Laufzeit

Diese Punkte dürfen später ergänzt werden, wenn ein realer Bedarf besteht und der Modulvertrag eingehalten wird.


4. Technischer Rahmen

Bereich Entscheidung
Backend Python + Flask
App-Struktur Flask App Factory + Blueprints
Rendering Jinja2, server-rendered Multi-Page-App
ORM SQLAlchemy / Flask-SQLAlchemy
Datenbank SQLite für v1
Schemaänderungen Flask-Migrate / Alembic ab Beginn der neuen Architektur
CSS eigenes Designsystem, kein CSS-Framework
JavaScript Vanilla JS, nur wo es echten Nutzen bringt
Chat-Aktualisierung kleines Fetch-Polling, keine WebSockets in v1
Produktionsserver Gunicorn
Container Docker / Compose, non-root
Reverse Proxy Caddy
Hosting eigener VPS in der EU
UI-Sprache Deutsch
Zielplattform Browser-App, mobil zum Homescreen hinzufügbar

Architekturregel

Server-rendered HTML bleibt die Basis. JavaScript verbessert Komfort, darf aber nicht unnötig die gesamte App in eine SPA verwandeln.


5. Organisations- und Teammodell

Die zentrale Hierarchie lautet:

Organization
└── Team
    └── TeamMembership
        └── Account

5.1 Organization

Die erste Organisation ist Aidshilfe Köln e.V.

Organization existiert trotzdem als eigenes Modell, damit die Anwendung strukturell nicht auf ein einzelnes Team oder eine einzelne Abteilung fest verdrahtet wird.

Konzeptionelle Felder:

Organization
├── id
├── name
├── status
└── created_at

5.2 Team

Ein Team ist ein klar abgegrenzter Arbeitsbereich innerhalb der Organisation.

Team
├── id
├── organization_id
├── name
├── slug
├── brand_profile_id
├── status
└── created_at

Status mindestens:

  • active
  • archived

Teams werden archiviert statt gelöscht, solange keine zwingende Datenschutz-/Löschanforderung etwas anderes verlangt.

Ein aktives Team muss mindestens eine Team-Verwaltung besitzen.


6. Accounts, Mitgliedschaften und Rollen

6.1 Account ist eine Person, kein Teammitglied

Ein Account gehört nicht direkt einem Team.

Account
├── id
├── email
├── display_name
├── status
├── created_at
└── last_login_at

Die E-Mail-Adresse ist:

  • eindeutig und normalisiert,
  • Login-Identität,
  • nicht automatisch öffentliche Kontaktadresse,
  • nicht Teil des normalen Teamverzeichnisses.

Account-Status mindestens:

  • active
  • disabled

Accounts werden nicht leichtfertig hart gelöscht, damit historische Zuteilungen und andere Referenzen erhalten bleiben können.

6.2 TeamMembership

Die Beziehung einer Person zu einem Team wird separat modelliert:

TeamMembership
├── id
├── team_id
├── account_id
├── role
├── status
├── joined_at
└── ended_at

Rollen in v1:

  • member
  • manager

Status mindestens:

  • active
  • inactive

Pro Account und Team darf nur eine aktive Mitgliedschaft existieren.

Wichtige Semantik

manager ist keine getrennte Benutzerart. Eine Team-Verwaltung ist immer auch normales Mitglied des Teams und kann die Mitgliederoberfläche genauso benutzen wie andere Teammitglieder.


7. Organisationsadministration

Organisationale Rechte werden separat von Team-Mitgliedschaften modelliert:

OrganizationMembership
├── id
├── organization_id
├── account_id
├── role
└── status

Für v1 reicht:

  • organization_admin

7.1 Organisationsadministration darf

  • Teams anlegen und archivieren,
  • Brand Profiles anlegen/zuordnen,
  • Module für Teams aktivieren/deaktivieren,
  • Team-Verwalter:innen ernennen und entfernen,
  • organisationsweite technische/strukturelle Einstellungen verwalten,
  • weitere Organisationsadmins über einen gesicherten Prozess ernennen,
  • sicherheitsrelevante Account-Sperren durchführen.

7.2 Organisationsadministration darf ausdrücklich nicht automatisch

  • Teamchats lesen,
  • Dokumente eines Teams sehen,
  • Einsatzplanung oder Verfügbarkeiten eines Teams sehen,
  • normale Teamkontakte sehen,
  • die vollständigen Mitgliederlisten aller Teams durchsuchen,
  • allein wegen der Org-Rolle Team-Fachmodule öffnen.

Die Organisationsverwaltung sieht Teamstruktur und Team-Verwaltungen, nicht automatisch den internen Teambetrieb.

Wenn eine organisationsadministrierende Person interne Teamdaten sehen soll, braucht sie zusätzlich eine normale TeamMembership in diesem Team.

Sicherheitsinvariante

organization_admin darf niemals eine fehlende TeamMembership für Team-Inhalte umgehen.

Diese Regel erhält eigene automatisierte Tests.


8. Ernennung von Team-Verwaltungen

Nur die Organisationsverwaltung darf Team-Verwalter:innen ernennen oder entfernen.

Team-Verwalter:innen dürfen:

  • normale Mitglieder einladen,
  • normale Teammitgliedschaften verwalten,
  • Inhalte und Modulkonfiguration ihres Teams verwalten,

aber keine weiteren Team-Verwaltungsrechte vergeben.

Zusätzliche Regeln:

  1. Eine Team-Verwaltung kann sich nicht selbst zur normalen Mitgliedschaft herunterstufen.
  2. Ein aktives Team darf nicht ohne Team-Verwaltung zurückbleiben.
  3. Beim Entfernen der letzten Team-Verwaltung muss zuerst eine neue bestimmt werden.
  4. Eine neue Team-Verwaltung kann von der Organisationsverwaltung aus einer bestehenden Teammitgliedschaft befördert oder direkt als Team-Verwaltung eingeladen werden, ohne dass der Org-Admin dazu die komplette Teammitgliederliste sehen muss.

9. Technische Serveradministration ist keine App-Rolle

Zugriff auf VPS, Docker, Caddy, Backups oder Git verleiht keine fachliche Rolle innerhalb der App.

Technische Administration und Anwendungsberechtigungen bleiben konzeptionell getrennt.


10. Passwordless Auth

10.1 Grundprinzip

Es gibt in v1 keine Benutzerpasswörter.

Login erfolgt per E-Mail:

  1. Person gibt ihre E-Mail-Adresse ein oder folgt einer Einladung.
  2. App erzeugt einen zeitlich begrenzten Auth-Challenge.
  3. E-Mail enthält:
    • einen kurzen Einmal-Code,
    • zusätzlich einen Direktlink.
  4. Nach erfolgreicher Verifikation wird eine langlebige, widerrufbare Device Session angelegt.
  5. Beim normalen erneuten Öffnen der Homescreen-/Browser-App ist die Person weiterhin angemeldet.

Es gibt damit keinen Passwort-Reset-Flow.

10.2 AuthChallenge

AuthChallenge
├── id
├── email
├── code_hash
├── link_token_hash
├── created_at
├── expires_at
├── attempts
└── consumed_at

Code und Linktoken werden nicht im Klartext gespeichert.

Ablaufzeit, maximale Fehlversuche und Rate Limits sind Konfiguration, nicht Geschäftslogik.

10.3 DeviceSession

DeviceSession
├── id
├── account_id
├── token_hash
├── created_at
├── last_seen_at
├── expires_at
├── revoked_at
└── device_label

Session-Cookies müssen mindestens:

  • Secure,
  • HttpOnly,
  • sinnvolle SameSite-Einstellung

verwenden.

Gerätesitzungen sind serverseitig widerrufbar. Eine spätere Profilseite „Angemeldete Geräte“ ist mit dem Modell bereits möglich.

10.4 Frische Re-Authentifizierung

Besonders sensible Organisationsaktionen, z. B. Änderung von Org-Admin- oder Team-Manager-Rechten, verlangen eine frische E-Mail-Re-Authentifizierung, auch wenn eine langlebige Device Session besteht.

Passkeys/2FA können später ergänzt werden, sind nicht Teil von v1.


11. Einladungen

11.1 TeamInvitation

TeamInvitation
├── id
├── team_id
├── email
├── role
├── invited_by
├── status
├── created_at
└── expires_at

Status mindestens:

  • pending
  • accepted
  • revoked
  • expired

Normale Team-Verwaltungen dürfen nur role = member einladen.

Ablauf

Team-Verwaltung trägt E-Mail ein
→ Einladung wird erstellt
→ Empfänger:in verifiziert die E-Mail
→ vorhandenen Account finden oder neuen Account anlegen
→ TeamMembership erstellen
→ bei neuem Account: Onboarding für Anzeigename

11.2 OrganizationAdminInvitation

Organisationsadministration erhält einen getrennten, sicherheitssensiblen Einladungsflow:

OrganizationAdminInvitation
├── id
├── organization_id
├── email
├── invited_by
├── status
├── created_at
└── expires_at

Der erste Organisationsadmin wird über einen CLI-Bootstrap angelegt/eingeladen. Es gibt keine hardcodierte Admin-E-Mail.

Nach vorhandenem Org-Admin soll der Bootstrap standardmäßig nicht einfach weitere globale Administratoren erzeugen.

Operational wird empfohlen, mindestens zwei Org-Admins zu haben, damit die Organisation nicht von einer einzelnen Person abhängig ist.


12. Profil und Teamkontakte

12.1 Anzeigename

Die Login-E-Mail ist nicht der öffentliche Name.

Beim ersten erfolgreichen Login wird, falls noch kein Anzeigename existiert, gefragt:

Wie sollen wir dich nennen?

display_name:

  • wird in Chat, Einsatzplanung und Teamverzeichnis verwendet,
  • darf von der Person im Profil geändert werden,
  • muss nicht organisationsweit eindeutig sein.

Team-spezifische Anzeigenamen werden in v1 nicht gebaut.

12.2 Profilbild und Avatare

Ein Account kann optional ein eigenes Profilbild verwenden.

Alternativ stehen drei rein visuelle Standardvarianten zur Auswahl:

  • Avatar A
  • Avatar B
  • Avatar C

Die Avatare werden nicht geschlechtlich beschriftet.

Wenn nichts gewählt wurde, kann Avatar A als neutraler technischer Default verwendet werden.

Das Profilbild gilt accountweit und wird z. B. angezeigt in:

  • Teamverzeichnis,
  • Chat,
  • Einsatzansichten.

Uploadbilder werden serverseitig auf geeignete Größen/Thumbnails verarbeitet. Erlaubte Profilbildformate orientieren sich an den Bildformaten des Dokumenten-Uploads, einschließlich moderner Smartphone-Formate.

12.3 Freiwilliges TeamMemberProfile

Kontaktdaten sind teambezogen und freiwillig:

TeamMemberProfile
├── team_membership_id
├── phone_number            optional
├── contact_by_call         optional
├── contact_by_sms          optional
├── contact_by_signal       optional
├── contact_note            optional
└── share_contact_details

Beispiel:

Andrea
[Profilbild]
Team-Verwaltung

📞 0176 ...
Erreichbar per Anruf / Signal
„Bei kurzfristigen Dienstausfällen gerne anrufen.“

Datenschutzregeln

  • share_contact_details ist standardmäßig aus.
  • Freigegebene Kontaktdaten sehen nur aktive Mitglieder desselben Teams.
  • Eine Person kann in einem Team teilen und in einem anderen nicht.
  • Nicht freigegebene freiwillige Kontaktdaten werden auch Team-Verwaltungen nicht „heimlich“ angezeigt.
  • Beim Ende der Teammitgliedschaft endet der Zugriff für andere Teammitglieder.
  • Es gibt in v1 keinen organisationsweiten Kontaktdatenexport.

Login-E-Mail

Die Login-E-Mail wird nicht normalen Teammitgliedern angezeigt.

Team-Verwaltungen dürfen sie für die Mitgliederadministration ihres Teams sehen.

Organisationsadmins sehen nicht automatisch die Login-E-Mails aller normalen Teammitglieder; ihr struktureller Zugriff bleibt auf ihre Aufgaben begrenzt.

Eine spätere freiwillige contact_email wäre ein separates Feld und niemals automatisch Account.email.


13. Teammitgliederverzeichnis

Normale Teammitglieder sehen im eigenen Team:

  • Anzeigename,
  • Profilbild / Avatar,
  • Kennzeichnung „Team-Verwaltung“, falls zutreffend,
  • freiwillig freigegebene Teamkontaktdaten.

Sie sehen nicht:

  • Login-E-Mail,
  • Device Sessions,
  • Auth-Daten,
  • interne Account-/Einladungsmetadaten.

Team-Verwaltungen sehen zusätzlich für die Mitgliederadministration:

  • Login-E-Mail,
  • Mitgliedsstatus,
  • Einladungsstatus,
  • Beitrittsdatum.

14. Brand Profiles / Corporate Design

Branding ist Core-Infrastruktur, kein Fachmodul.

BrandProfile
├── id
├── organization_id
├── name
├── logo
├── primary_color
├── primary_strong
├── accent_color
├── accent_strong
├── heading_font
└── body_font

Die genaue Tokenmenge darf sich bei der Implementierung verfeinern. Entscheidend ist:

  • Brand Profiles speichern Design-Eingaben, kein beliebiges CSS.
  • Ein Brand Profile gehört der Organisation.
  • Mehrere Teams dürfen dasselbe Brand Profile verwenden.
  • In der Startkonfiguration sind mehrere unterschiedliche Profile vorgesehen, z. B. Aidshilfe-Standard, Café Bach, CheckPoint/Vor-Ort.
  • Layout und Komponenten bleiben gleich; Farben, Schriften und Logo verändern die Identität.
  • Light-/Dark-Darstellung wird über semantische CSS-Tokens abgeleitet.

Der bestehende Ansatz mit getrennten Brand- und semantischen Tokens wird beibehalten, aber auf das neue BrandProfile-Modell ausgerichtet.

14.1 Initiales Brand Profile „Vor-Ort-Arbeit“

Für die Vor-Ort-Arbeit wird die visuelle Basis aus dem vorhandenen CheckPoint-CSS übernommen. Es werden nur die grundlegenden Markenfarben und Schriften als Ausgangsmaterial verwendet; die Ehrenamt-Plattform übernimmt nicht das gesamte Layout der Quell-App.

Typografie

Einsatz Schrift Nutzung
Display / große Überschriften Big Shoulders Display Seitentitel, große Kennzahlen, markante Abschnittsüberschriften
Body / UI Noto Sans Fließtext, Navigation, Formulare, Buttons, Tabellen, Hilfetexte

Grundregel: Big Shoulders Display sparsam und gezielt, Noto Sans trägt den Großteil der Oberfläche. Das hält die App charakteristisch, ohne sie plakativ oder unruhig wirken zu lassen.

Basisfarben

Token / Rolle Basiswert Zweck
Ink / Navy #07182E Haupttext, sehr dunkle Flächen, Navigation
Deep #061126 tiefster Hintergrund / Sidebar-Basis
Teal strong #005F61 dunkle Markenfläche, Sidebar / Header
Teal #00858A primäre Markenfarbe und aktive Navigation
Aqua #52E0CF freundlicher Akzent, Fokus, leichte Highlights
Blue #2596BE sekundärer Informationsakzent
Coral #F04A67 wichtig / Aktion; Rot nur gezielt einsetzen
Yellow #F6E74A sparsame Hervorhebung / Hinweis
Mist #F4FAFC ruhiger Seitenhintergrund
Paper #FFFFFF Karten und Arbeitsflächen

Für helle Arbeitsflächen dürfen kontrastoptimierte semantische Ableitungen verwendet werden, z. B. dunkleres Teal oder Blue für Textlinks und Buttons. Die oben genannten Werte bleiben die Brand-Basis, nicht zwangsläufig der direkte CSS-Wert jedes Elements.

Logo-Nutzung

Das Vor-Ort-Logo darf und soll sichtbar wiederkehren, insbesondere:

  • im mobilen Header bzw. App-Kopf,
  • oben in der Desktop-Sidebar,
  • in Team-/Brand-Kontexten,
  • auf Login-/Einladungsansichten, sofern das Team bereits feststeht.

Das Logo ist ein Identitätsanker, ersetzt aber keine Überschrift und soll nicht als dekoratives Hintergrundmotiv vervielfacht werden.

14.2 Visuelle Referenz — Quick Mockup v1

Die abgestimmte visuelle Richtung ist bewusst clean, ruhig und informationszentriert. Bilder und motivierende Illustrationen sind nicht Teil des normalen Dashboard-Rasters. Die Oberfläche soll freundlich wirken, aber primär beim Arbeiten helfen.

Verbindliche Gestaltungsrichtung:

  • helle Arbeitsfläche mit viel Weißraum,
  • dunkle teal/navy Navigation als klarer Rahmen,
  • weiße Cards mit leichter Kontur bzw. sehr zurückhaltendem Schatten,
  • wenige, klare Statusfarben,
  • Logo gut sichtbar,
  • Icons funktional statt dekorativ,
  • keine großflächigen Motivbilder im Dashboard,
  • keine regelmäßigen Motivationssprüche oder Gamification-Elemente,
  • große Display-Schrift nur für Hierarchie und Orientierung, nicht für jeden Kartentitel.

Die drei Dashboard-Profile teilen dieselbe visuelle Sprache, unterscheiden sich aber in Informationsdichte und Aufgabenfokus:

  1. Mitglieder-Dashboard — persönlich, mobil-first, Fokus auf nächste Einsätze, offene Rückmeldungen, Verfügbarkeit und ungelesenen Teamchat.
  2. Teamverwaltungs-Dashboard — sachlicher und dichter, Fokus auf offene Aufgaben, Rückmeldungen, Freigaben, Einladungen und unbesetzte Positionen.
  3. Organisationsverwaltungs-Dashboard — am nüchternsten; Fokus ausschließlich auf Struktur, Teams, Module, Brand Profiles, Einladungen und organisationsweite Verwaltungsaufgaben. Keine teaminternen Fachinhalte.

Quick-Mockup-Referenz im Projektordner:

vor_ort_arbeit_dashboard_mockups.png

Diese Grafik ist eine visuelle Referenz, kein Pixel-Perfect-Vertrag. Bei der Implementierung haben Barrierearmut, Responsive-Verhalten, reale Inhalte und das zentrale Komponentensystem Vorrang. Die Grundwirkung — Farben, Typografie, Informationsruhe, Logo-Präsenz und klare Dashboard-Hierarchie — soll jedoch erhalten bleiben.


15. Modularchitektur

15.1 Abhängigkeitsregel

Fachmodul → Core   erlaubt
Core → Fachmodul   verboten

Der Core darf nicht wissen, wie ein Einsatz, Dokument oder Chat fachlich funktioniert.

15.2 Verzeichnisstruktur

Zielstruktur, sinngemäß:

app/
├── core/
│   ├── auth/
│   ├── accounts/
│   ├── organizations/
│   ├── teams/
│   ├── memberships/
│   ├── invitations/
│   ├── permissions/
│   ├── branding/
│   ├── storage/
│   ├── notifications/
│   ├── dashboard/
│   ├── profiles/
│   └── audit/
│
└── modules/
    ├── scheduling/
    ├── documents/
    └── chat/

Ein größeres Modul darf intern besitzen:

module/
├── __init__.py
├── models.py
├── routes.py
├── services.py
├── permissions.py
├── forms.py
├── templates/
└── tests/

Nicht jedes Modul braucht jede Datei; die Verantwortlichkeiten bleiben aber getrennt.

15.3 Routes sind dünn

Eine Route erledigt hauptsächlich:

Request
→ Team-Kontext auflösen
→ Berechtigung prüfen
→ Service aufrufen
→ Response / Redirect / Template

Fachlogik gehört in Services, nicht in große Routendateien.

15.4 Permission Helpers statt Rollenabfragen überall

Nicht überall:

if membership.role == "manager":

sondern fachliche Funktionen wie:

can_manage_schedule()
can_review_document()
can_manage_channel()

Dadurch können Rollen später feiner werden, ohne die gesamte Codebasis umzuschreiben.


16. Modulregistrierung

Die Existenz eines Moduls wird im Code definiert, nicht in einer Datenbanktabelle.

Beispiel:

ModuleDefinition
├── key
├── name
├── description
├── version
├── navigation
├── member_dashboard_provider
├── manager_dashboard_provider
└── dependencies

Stabile Modulkeys für v1:

  • scheduling
  • documents
  • chat

Alle installierten Module/Blueprints werden beim App-Start registriert.

TeamModule

Die Datenbank entscheidet nur, ob ein vorhandenes Modul für ein Team aktiv ist:

TeamModule
├── team_id
├── module_key
└── enabled

Die Kombination team_id + module_key ist eindeutig.

Verantwortlichkeiten

Organisationsverwaltung: aktiviert/deaktiviert Module für Teams.
Team-Verwaltung: konfiguriert und nutzt bereits aktivierte Module.

Ein deaktiviertes Modul ist nicht nur aus der Navigation verschwunden. Direkte Routes müssen ebenfalls Zugriff verweigern.

Prüfreihenfolge:

aktive TeamMembership?
→ Modul für Team aktiviert?
→ fachliche Berechtigung vorhanden?
→ Zugriff

16.1 Modulabhängigkeiten

Abhängigkeiten müssen explizit deklariert sein. v1 soll möglichst ohne Fachmodul-zu-Fachmodul-Abhängigkeiten auskommen:

scheduling → core
documents  → core
chat       → core

Module dürfen nicht ungeordnet gegenseitig interne Modelle abfragen. Falls Modul A Funktionalität von Modul B benötigt, geschieht das über eine definierte Service-Schnittstelle oder die gemeinsame Funktion gehört in den Core.

16.2 Moduleinstellungen

Komplexere Moduleinstellungen gehören dem jeweiligen Modul, z. B. SchedulingSettings.

Kein vorschnelles generisches JSON-Einstellungsmonster.


17. Team-Datenbesitz und Scoping

Alle teaminternen Fachobjekte tragen explizit team_id, auch wenn die Teamzugehörigkeit theoretisch über eine Beziehung ableitbar wäre.

Beispiele:

Shift.team_id
Document.team_id
Channel.team_id
Message.team_id

Grundprüfung:

requested_object.team_id == current_team.id

und aktive TeamMembership.

Diese Redundanz ist bewusst: Teamgrenzen sollen in Code, Queries und Tests klar sichtbar bleiben.


18. Dashboard- und Navigationsarchitektur

18.1 Eindeutiger Team-Kontext

Die App arbeitet bei Teamfunktionen immer in einem klaren Teamkontext, vorzugsweise auch sichtbar in der URL:

/team/<slug>/
/team/<slug>/scheduling/
/team/<slug>/documents/
/team/<slug>/chat/

Die Session darf das zuletzt verwendete Team merken, aber keine Berechtigung daraus ableiten.

Loginverhalten

  • nur ein aktives Team → direkt dort starten,
  • mehrere Teams + bekannte letzte Auswahl → dort starten,
  • erster Login mit mehreren Teams → einmalige Teamauswahl.

18.2 Team-Startseite

Die normale Startseite zeigt persönliche/relevante Informationen des aktuellen Teams.

Beispiel:

Guten Morgen, Patsy

Dringend
Heute wird noch ein Springer gesucht.

Deine nächsten Einsätze
...

Verfügbarkeit
4 Termine noch offen

Teamchat
3 neue Nachrichten

Team-Verwalter:innen landen nicht automatisch in einem Backend, sondern benutzen dieselbe Mitgliederstartseite.

18.3 Team-Verwaltungsbereich

Zusätzlich existiert:

/team/<slug>/manage/

Nur Team-Verwaltungen dürfen ihn öffnen.

Dort erscheinen Aufgaben/Zustände wie:

  • unterbesetzte Einsätze,
  • fehlende Verfügbarkeiten,
  • Dokumentfreigaben,
  • offene Einladungen.

18.4 Organisationsverwaltung

Separater Bereich:

/org/<slug>/manage/

Dieser zeigt ausschließlich strukturelle Organisationsinformationen, z. B.:

  • Teams,
  • Team-Verwaltungen,
  • Brand Profiles,
  • Modulaktivierungen,
  • Org-Admin-Verwaltung.

Keine Teamchat-, Dokument- oder Scheduling-Inhalte.


19. Dashboard Extension Contract

Der Core kennt keine Fachmodule, muss deren relevante Hinweise aber darstellen können.

Deshalb registrieren Module strukturierte Dashboard-Beiträge.

DashboardItem
├── title
├── text
├── category
├── count                optional
├── action_label         optional
├── action_endpoint      optional
└── module_key

Erlaubte Kategorien:

  • URGENT
  • ACTION
  • UPCOMING
  • INFO

Sortierung zentral:

  1. Dringend
  2. Handlungsbedarf
  3. Bevorstehend
  4. Information

Das Modul liefert Daten, nicht eigenes Dashboard-HTML oder eigene Farben. Der Core rendert standardisierte Komponenten und wendet das Brand Profile an.

Es gibt in v1 keine Tabellen wie DashboardWidget oder personalisierbare Dashboard-Layouts. Karten werden aus dem aktuellen Systemzustand berechnet.


20. Core Notifications

Notifications sind Infrastruktur, kein aktivierbares Fachmodul.

Fachmodule rufen einen zentralen Notification-Service auf.

Konzeptionelles Modell:

Notification
├── id
├── account_id
├── team_id              optional
├── type
├── priority
├── title
├── text
├── action_endpoint      optional
├── created_at
└── read_at              optional

Prioritäten:

  • normal
  • important
  • urgent

Grundregel

Dashboard zeigt Zustände. Benachrichtigungen melden Ereignisse.

Beispiel:

  • Benachrichtigung: „Andrea hat ihren Einsatz abgesagt.“
  • Dashboard: „1 Position ist aktuell unbesetzt.“

Wenn der Platz besetzt ist, verschwindet der Dashboardzustand; die historische Benachrichtigung darf bestehen bleiben.

v1 nutzt operative Benachrichtigungen nur in der App. E-Mail wird in v1 für Auth und Einladungen verwendet, nicht für normalen Teamverkehr.


21. Modul 1: Scheduling / Einsatzplanung

21.1 Ziel

Scheduling bleibt das umfangreichste Fachmodul. Es soll unterschiedliche Teammodelle unterstützen, ohne die Bedienung komplizierter zu machen.

Die bisherige feste Regel „2 Haupt + 1 Springer“ wird nicht Plattformregel. Sie wird eine Team-/Vorlagenkonfiguration.

21.2 Workflow

Vorbereitung
→ Verfügbarkeit offen
→ Plan wird erstellt
→ Veröffentlicht

Statuswerte:

  • draft
  • availability_open
  • scheduling
  • published

Ein vergangener Zeitraum kann später archiviert werden, ohne dafür einen komplizierten zusätzlichen Arbeitsstatus zu benötigen.

Semantik

Vorbereitung: Verwaltung legt Zeitraum, Einsätze und Positionen an.
Verfügbarkeit offen: Mitglieder geben Ja/Nein pro Einsatz ab und dürfen ändern.
Plan wird erstellt: Verfügbarkeit ist eingefroren; Verwaltung teilt ein.
Veröffentlicht: Plan ist für Mitglieder verbindlich sichtbar; Änderungen laufen über Absage/Nachbesetzung.

21.3 PlanningPeriod

PlanningPeriod
├── id
├── team_id
├── name
├── start_date
├── end_date
├── status
├── availability_deadline    optional
├── created_at
└── archived_at              optional

Keine erzwungene Zweimonatsdauer.

21.4 PositionDefinition

PositionDefinition
├── id
├── team_id
├── name
├── description              optional
├── sort_order
├── active
└── replacement_for_id       optional

Beispiele:

Vor-Ort:
Haupt
Springer → Ersatz für Haupt

Café Bach:
Theke
Springer → Ersatz für Theke

Das Ersatzmodell bleibt absichtlich einfach. Keine komplexen Vertretungsketten in v1.

21.5 ShiftTemplate

ShiftTemplate
├── id
├── team_id
├── name
├── default_title
├── default_start_time
├── default_end_time
├── default_location
├── active
└── Template-Positionen
ShiftTemplatePosition
├── shift_template_id
├── position_definition_id
├── required_count
└── sort_order

Vorlagen sind Komfort, keine Pflicht. Ein konkreter Einsatz darf nach Erzeugung verändert werden.

21.6 Shift

Shift
├── id
├── team_id
├── planning_period_id
├── title
├── date
├── start_time
├── end_time
├── location
├── description             optional
└── created_at

21.7 ShiftPosition

ShiftPosition
├── id
├── team_id
├── shift_id
├── position_definition_id
├── required_count
└── sort_order

Beispiel:

Haupt      required_count = 2
Springer   required_count = 1

Ein offener Platz wird aus required_count - aktive Assignments berechnet. Es braucht keinen künstlichen „offenen Assignment-Datensatz ohne Person“.

21.8 Availability

Mitglieder melden nur Ja oder Nein zum gesamten Einsatz, keine Positionspräferenzen.

Availability
├── id
├── team_id
├── shift_id
├── team_membership_id
├── status
└── updated_at

Status:

  • available
  • unavailable

Kein Datensatz = noch nicht beantwortet.

Beim Wechsel des Planungszeitraums zu scheduling werden Verfügbarkeiten eingefroren.

21.9 Assignment

Assignment
├── id
├── team_id
├── shift_id
├── shift_position_id
├── team_membership_id
├── status
├── assigned_at
└── assigned_by             optional/system

Status mindestens:

  • assigned
  • cancelled
  • promoted

Die Erstverteilung bleibt manuell durch Team-Verwaltung.

21.10 Fairness-Hilfe

Bei der Auswahl einer verfügbaren Person zeigt die App mindestens:

  • Anzahl bisheriger Einsätze,
  • Datum des letzten Einsatzes.

Keine automatische Fairness-Verteilung und keine Gewichtungsformeln in v1.

21.11 Unterbesetzung

Unterbesetzung wird aus Daten berechnet:

aktive Assignments < benötigte Positionsanzahl

Kein manuell zu pflegender Wahrheitswert, wenn er zuverlässig ableitbar ist.


22. Scheduling: Ausfall und Nachbesetzung

22.1 Grundregel

Nach Veröffentlichung wird eine Zuteilung nicht durch nachträgliches Ändern der ursprünglichen Availability korrigiert.

Wenn jemand ursprünglich available war und später absagt:

Availability = available
Assignment   = cancelled

Damit bleibt die historische Information korrekt.

22.2 Automatisches Nachrücken

Wenn eine Zielposition ausfällt und eine passende Ersatzposition definiert und besetzt ist:

Haupt fällt aus
→ Springer rückt auf Haupt
→ Springerplatz wird offen

Automatisches Nachrücken findet nur statt, wenn das Ergebnis eindeutig ist. Bei mehreren möglichen Ersatzpersonen oder anderen Mehrdeutigkeiten entscheidet die Team-Verwaltung.

22.3 Springer fällt aus

Es wird lediglich die Springerposition offen. Niemand wird künstlich umverteilt.

22.4 Kein Springer vorhanden

Die ausgefallene Zielposition selbst wird offen.

22.5 Mehrere Ausfälle

Automatisierung endet nach den eindeutig möglichen Nachrückschritten. Die App versucht nicht, selbstständig eine komplette neue Dienstplanung zu errechnen.

22.6 Selbstübernahme offener Positionen

Pro Team kann konfiguriert werden, ob offene Positionen nach Veröffentlichung selbst übernommen werden dürfen.

Eine Person darf übernehmen, wenn:

  • aktive TeamMembership,
  • Scheduling aktiviert,
  • Einsatz veröffentlicht,
  • Position wirklich offen,
  • Person nicht bereits in diesem Einsatz eingeteilt,
  • Platz nicht im selben Moment bereits vergeben wurde.

Der letzte Punkt muss transaktional / durch DB-Constraint bzw. atomare Logik abgesichert werden, nicht nur durch den Buttonzustand im Browser.

Eine ursprünglich als unavailable gemeldete Person darf später trotzdem einen offenen Dienst übernehmen, falls sich ihre Situation geändert hat.

Auch ein übernommener Ersatzdienst kann später wieder abgesagt werden.

22.7 Dringlichkeit

Eine offene Position wird in v1 24 Stunden vor Einsatzbeginn als urgent behandelt.

Das verändert nicht die Fachlogik, nur Priorität und Darstellung.

Beispiel:

Dringend: Heute 19:00 Springer gesucht.

Der 24-Stunden-Wert ist in v1 ein fester Produktstandard und kann später teamkonfigurierbar werden.


23. Scheduling: Benachrichtigungen

Mitglieder

Ereignis Empfänger Priorität
Verfügbarkeit geöffnet aktive Teammitglieder normal
Rückmeldung unvollständig, ca. 48 h vor Frist nur betroffene Mitglieder normal
Plan veröffentlicht / eigene neue Einsätze betroffene Mitglieder, sinnvoll gebündelt wichtig
eigene Einteilung geändert betroffene Person wichtig
automatisches Nachrücken nachrückende Person wichtig
offener Dienst berechtigte Teammitglieder normal
offener Dienst <24 h berechtigte Teammitglieder dringend
Einsatzzeit/-ort geändert eingeteilte Personen wichtig
Einsatz abgesagt eingeteilte Personen wichtig

Team-Verwaltung

Ereignis Priorität
Mitglied sagt Zuteilung ab wichtig
Person übernimmt offenen Platz normal
kurzfristige Unterbesetzung dringend

Dauerhafte Zustände wie „6 Rückmeldungen fehlen“ gehören auf das Dashboard, nicht als dauernder Notification-Spam in die Glocke.

Benachrichtigungen sollen bei Veröffentlichung sinnvoll dedupliziert/gebündelt werden, z. B. „Der Dienstplan ist da du hast 3 neue Einsätze“ statt unmittelbar mehrerer redundanter Meldungen.


24. Scheduling: Verlauf

Das Scheduling-Modul soll fachlich nachvollziehbar machen, wie eine aktuelle Besetzung entstanden ist, ohne den globalen Security-Audit mit jedem Fachereignis zu überladen.

Ein kleiner Scheduling-Verlauf darf Ereignisse wie diese dokumentieren:

Andrea hat abgesagt.
Chris ist automatisch von Springer auf Haupt nachgerückt.
Springerposition wurde geöffnet.
Marco hat die offene Springerposition übernommen.

Konkrete Implementierung kann über ein schlankes SchedulingEvent-Modell erfolgen.


25. Modul 2: Documents / Dokumente

25.1 Ziel

Das Dokumentenmodul ist keine Nextcloud und kein komplexes DMS.

Die sichtbare Struktur bleibt:

Aktuell
Archiv

25.2 Document

Document
├── id
├── team_id
├── uploader_membership_id
├── title
├── description                 optional
├── original_filename
├── storage_key
├── mime_type
├── file_size
├── approval_status
├── library_status
├── reviewed_by_membership_id   optional
├── rejection_reason            optional
├── created_at
├── reviewed_at                 optional
└── archived_at                 optional

25.3 Freigabestatus

pending
approved
rejected

25.4 Bibliotheksstatus

current
archived

Freigabe und Archivierung sind getrennte Dimensionen:

  • Freigabe = darf das Team die Datei sehen?
  • Archiv = ist die Datei noch aktuell?

25.5 Uploadrechte

Normale Mitglieder dürfen hochladen. Diese Uploads starten mit pending.

Sichtbarkeit im Pending-Status:

  • Uploader:in,
  • Team-Verwaltung.

Team-Verwaltungsuploads sind direkt freigegeben.

Normale Mitglieder dürfen eigene noch wartende Uploads zurückziehen.

25.6 Dateiformate

Normale Teammitglieder dürfen in v1 mindestens hochladen:

  • PDF
  • Word (.doc, .docx)
  • JPG / JPEG
  • PNG
  • HEIC / HEIF

HEIC/HEIF wird aufgenommen, damit Smartphone-Fotos — insbesondere aus verbreiteten iPhone-Konfigurationen — praktisch nutzbar bleiben.

HTML-Sonderfall

HTML darf ausschließlich durch Team-Verwaltungen hochgeladen/bereitgestellt werden.

Ziel: kleine statische, gut lesbare Anleitungen, z. B. für Café-Abläufe.

HTML wird nicht als vertrauenswürdige Mini-Web-App behandelt. Darstellung erfolgt isoliert und restriktiv:

  • kein JavaScript,
  • keine Formulare,
  • keine frei laufenden aktiven Inhalte,
  • keine unkontrollierten externen Embeds,
  • sandboxed iframe / restriktive CSP bzw. äquivalente Schutzmechanismen.

HTML ist Top-down-Inhalt; normale Mitglieder können es nicht einreichen.

25.7 Dateiprüfung

Serverseitig prüfen:

  • erlaubte Erweiterung,
  • plausibler MIME-/Dateityp,
  • Dateigrößenlimit,
  • Dateiname wird nie als Speicherpfad verwendet.

Maximale Uploadgröße ist konfigurierbar, nicht im Fachcode fest verdrahtet.

25.8 Storage

Dateien werden nicht als SQLite-BLOB gespeichert.

Beispielstruktur im persistenten App-Volume:

/app/instance/
├── app.db
└── uploads/
    ├── documents/
    └── profiles/

Physische Dateinamen/Storage Keys sind opaque (z. B. UUIDs). original_filename ist nur Metadatum.

Caddy darf private Uploads nicht einfach öffentlich als statische Dateien ausliefern.

Download/Preview läuft durch kontrollierten Zugriff:

Login
→ TeamMembership
→ team_id prüfen
→ Dokument-/Statusberechtigung
→ Datei ausliefern

25.9 Bildvorschau

Bilder erhalten serverseitig erzeugte Thumbnails/Vorschauen in einem Browser-kompatiblen Format. Das Original bleibt erhalten.

So können auch HEIC-/HEIF-Ursprungsbilder zuverlässig in der App als Vorschau dargestellt werden.

25.10 Freigabe

Team-Verwaltung kann:

  • freigeben,
  • ablehnen,
  • optional Ablehnungsgrund nennen,
  • archivieren,
  • wiederherstellen,
  • dauerhaft löschen.

Dauerhaftes Löschen entfernt Datei und Domain-Datensatz. Im Security-/Admin-Audit kann eine minimale Löschaktion verbleiben, ohne unnötig sensible Dateinamen fortzuschreiben.

25.11 Foto-Hinweis

Beim Bild-Upload wird freundlich darauf hingewiesen, nur Fotos hochzuladen, für deren teaminterne Nutzung keine ungeklärten Rechte/Einwilligungen Dritter entgegenstehen.

Keine Checkbox-Orgie; der Freigabeprozess ist die zweite Kontrollstufe.

25.12 Benachrichtigungen

  • Mitglied lädt hoch → Team-Verwaltung erhält Freigabe-Ereignis.
  • Dokument freigegeben → Uploader:in erhält normale/wichtige Info.
  • Dokument abgelehnt → Uploader:in erhält Info inkl. Grund, falls vorhanden.
  • Nicht jedes neue freigegebene Dokument erzeugt eine Nachricht an das gesamte Team.

Wartende Freigaben erscheinen auf dem Verwaltungsdashboard.


26. Zukünftige Fotogalerie

Eine echte Galerie wird nicht in das Dokumentenmodul hineingebaut.

Sie kann später als eigenes optionales Modul entstehen, z. B.:

gallery/
├── Fotoübersicht
├── Alben / Veranstaltungen
├── große Bildansicht
└── Downloadoptionen

Sie darf denselben Core-Storage-Service nutzen. Ob Galerie und Dokumente dieselben Domain-Datensätze verwenden, wird erst entschieden, wenn das Galerie-Modul wirklich gebaut wird.


27. Modul 3: Chat / Teamkommunikation

27.1 Ziel

Der Chat ist Arbeitskommunikation, kein WhatsApp-Ersatz für Unterhaltung.

Er soll Teamabsprachen erleichtern, ohne zum Meme-, GIF- oder Dateiaustauschkanal zu werden.

27.2 Channel

Channel
├── id
├── team_id
├── name
├── description
├── type
├── sort_order
├── status
└── created_at

Typen:

  • announcement
  • discussion

Beim Aktivieren des Chat-Moduls entstehen standardmäßig:

  • Ankündigungen
  • Team

Team-Verwaltungen dürfen weitere teamweite Discussion-Channels anlegen und archivieren.

Keine privaten Untergruppen in v1.

Channel-Status:

  • active
  • archived

27.3 Ankündigungen

  • Team-Verwaltung schreibt.
  • Alle aktiven Teammitglieder lesen.
  • Mitglieder antworten dort nicht.
  • Neue Ankündigung erzeugt eine In-App-Benachrichtigung.

27.4 Teamchat / Discussion

Alle aktiven Teammitglieder dürfen:

  • lesen,
  • schreiben,
  • auf einzelne Nachrichten antworten,
  • eigene Nachrichten bearbeiten,
  • eigene Nachrichten löschen.

Team-Verwaltungen dürfen zusätzlich moderieren/entfernen.

27.5 Message

Message
├── id
├── team_id
├── channel_id
├── author_membership_id
├── text
├── reply_to_message_id      optional
├── created_at
├── edited_at                optional
└── deleted_at               optional

Gelöschte Nachrichten zeigen in der UI einen neutralen Platzhalter. Bei Moderation darf sichtbar sein, dass die Team-Verwaltung entfernt hat.

Es gibt kein öffentliches Edit-Versionsarchiv in v1.

27.6 Keine Medien/Dateien

Im Chat sind ausdrücklich nicht erlaubt:

  • Bilder,
  • Dokumente,
  • Videos,
  • Audio,
  • GIFs,
  • Sticker,
  • andere Dateianhänge.

Fotos gehören in das Dokumenten-/spätere Galerie-System, nicht in den Chat.

Externe Links werden bewusst eingeschränkt:

  • nur https://,
  • maximal ein externer Link pro normaler Chatnachricht,
  • keine reinen Link-only-Nachrichten; kurzer Kontexttext ist erforderlich,
  • keine Link-Vorschau,
  • kein Thumbnail,
  • kein YouTube-/Social-Embed,
  • bekannte URL-Shortener werden blockiert,
  • externe Links werden als extern kenntlich gemacht.

Interne Links innerhalb der eigenen App dürfen normal verwendet werden.

Später kann bei echtem Bedarf eine Domain-Allowlist ergänzt werden.

27.8 Replies

Ein einfaches reply_to_message_id ist Teil von v1.

Threads oder verschachtelte Diskussionen werden nicht gebaut.

Eine direkte Antwort auf die eigene Nachricht darf eine normale In-App-Benachrichtigung auslösen.

27.9 Ungelesen / Zustellung / Gelesen

ChannelReadState
├── team_membership_id
├── channel_id
├── last_delivered_message_id
└── last_read_message_id

Anzeige für eigene Nachrichten:

  • grau = erfolgreich auf dem Server gespeichert,
  • ✓✓ grau = von allen relevanten aktuellen Empfänger:innen technisch abgerufen,
  • ✓✓ in der Brand-Akzentfarbe = von allen relevanten Empfänger:innen gelesen.

Mitglieder, die erst später einem Team beitreten, zählen nicht rückwirkend für alte Nachrichten.

Optionaler Detailhinweis darf z. B. „Gelesen von 8/10“ zeigen, aber keine detaillierte Überwachungsliste mit individuellen Lesezeitpunkten.

27.10 Polling

Neue Nachrichten werden mit kleinem Vanilla-JS-fetch-Polling nachgeladen.

  • kein WebSocket-System in v1,
  • Polling-Intervall konfigurierbar,
  • Chat bleibt grundsätzlich auch durch normales Neuladen funktional.

27.11 Chat-Benachrichtigungen

Normale neue Chatnachrichten erzeugen keine Glockenbenachrichtigung pro Nachricht.

Stattdessen:

  • Channel zeigt Ungelesen-Zähler,
  • Ankündigung → Notification,
  • direkte Antwort auf eigene Nachricht → Notification.

28. Core Storage Service

Da Dokumente, Profilbilder und spätere Medienmodule Dateien benötigen, gibt es einen gemeinsamen Storage-Service im Core.

Aufgaben:

  • sichere opaque Storage Keys erzeugen,
  • Pfade kontrollieren,
  • Dateien schreiben/lesen/löschen,
  • Thumbnails/Varianten verwalten,
  • Dateigrößenlimits und erlaubte Speicherbereiche unterstützen,
  • keine öffentliche Pfadableitung aus User-Dateinamen zulassen.

Domain-Modelle behalten ihre eigenen Metadaten und Berechtigungen; der Storage-Service entscheidet nicht selbst, wer ein Dokument sehen darf.


29. Audit Log

Der globale AuditLog ist für sicherheits- und administrationsrelevante Änderungen gedacht, nicht für jeden Klick.

AuditLog
├── id
├── organization_id
├── team_id                 optional
├── actor_account_id
├── action
├── target_type
├── target_id
└── created_at

Beispiele:

  • Team erstellt/archiviert,
  • Brand Profile geändert,
  • Modul aktiviert/deaktiviert,
  • Team-Verwaltung ernannt/entfernt,
  • Org-Admin-Rechte geändert,
  • Account gesperrt,
  • Mitgliedschaft administrativ beendet,
  • sensibles Dokument dauerhaft gelöscht.

Chatnachrichten, normale Verfügbarkeitsklicks oder jeder Seitenaufruf gehören nicht in diesen Audit Log.


30. Berechtigungsmatrix v1

Funktion Mitglied Team-Verwaltung Organisationsadmin ohne TeamMembership
eigenes Team öffnen
Teamverzeichnis sehen
freiwillig geteilte Teamkontakte sehen
Login-E-Mails normaler Mitglieder sehen
Mitglieder einladen
normale Mitgliedschaft verwalten
Team-Verwaltung ernennen
Team-Verwaltung entfernen
Team erstellen/archivieren
Brand Profile zuordnen
Module aktivieren/deaktivieren
aktivierte Module konfigurieren
Teamchat lesen
Dokumente sehen ✓*
Scheduling sehen ✓*
Teaminterne Inhalte quer über alle Teams sehen

* nur wenn das entsprechende Modul für das Team aktiviert ist und die konkrete Objektberechtigung erfüllt ist.


31. UI- und UX-Grundsätze

31.1 Mobile first

Primärer Testbereich:

  • ca. 360460 px App-Breite.

Desktop ist vollständig nutzbar, aber nicht der Ausgangspunkt für die Informationsarchitektur.

31.2 Barrierearmut

Pflicht:

  • Touch-Ziele mindestens ca. 44 px,
  • sichtbarer Tastaturfokus,
  • sinnvolle Labels,
  • Status nie nur über Farbe,
  • ausreichender Kontrast,
  • Fehlermeldungen mit konkreter Handlungsanweisung,
  • semantisches HTML,
  • keine zentrale Funktion ausschließlich per JavaScript.

31.3 Sprache

  • Deutsch,
  • klare aktive Verben,
  • ein Begriff bleibt im Flow gleich,
  • keine unnötige Verwaltungs-/IT-Sprache in der Mitgliederoberfläche.

31.4 Komponenten

Zentrale, wiederverwendbare Komponenten, z. B.:

  • Buttons,
  • Cards,
  • Badges,
  • Form Fields,
  • Confirmation Pattern,
  • Empty State,
  • Tabellen,
  • Listen,
  • Navigation,
  • Dashboard Cards.

Fachmodule dürfen fachspezifische Komponenten ergänzen, aber keine eigenen Grund-Buttons oder eigene Designwelt erfinden.

31.5 Visuelle Dichte und Bildsprache

Die Ehrenamt-Plattform ist kein Kampagnenauftritt, sondern ein Arbeitswerkzeug. Deshalb gilt für alle Brand Profiles, insbesondere für Vor-Ort:

  • Illustrationen und Fotos nur dort einsetzen, wo sie inhaltlich helfen.
  • Dashboards bleiben grundsätzlich bildarm.
  • Motivierende Texte sind optional und selten; sie dürfen keine Arbeitsinformation verdrängen.
  • Status, nächste Schritte und Handlungsbedarf stehen visuell vor Dekoration.
  • Wiederkehrende Markenidentität entsteht primär durch Logo, Typografie, Farben, Navigation und Komponentenrhythmus.

Für Vor-Ort gilt der Quick Mockup aus Abschnitt 14.2 als Referenz für diese Balance.

31.6 Navigation

Die genaue mobile Belegung wird in der UI-Implementierungsphase anhand der aktivierten Module festgelegt, aber:

  • Start, Team und Profil sind Core-Ziele,
  • Module registrieren Navigationseinträge,
  • Navigation muss auch bei mehreren aktiven Modulen übersichtlich bleiben,
  • Verwaltungsbereiche bleiben klar von der Mitgliederoberfläche getrennt.

Diese Detailfrage darf bei der UI-Implementierung entschieden werden; sie ändert die Architektur nicht.


32. Sicherheit

32.1 Invite-only

Es gibt keine offene Selbstregistrierung. Accounts entstehen über Einladung bzw. verifizierte E-Mail im Zusammenhang mit einer bestehenden Einladung/Organisation.

32.2 Autorisierung

Jeder teaminterne Zugriff prüft serverseitig:

  1. gültige Account-/Device-Session,
  2. aktive TeamMembership,
  3. Teamzugehörigkeit des Objekts,
  4. Modulaktivierung,
  5. konkrete Permission.

Keine UI-Ausblendung ersetzt eine serverseitige Prüfung.

32.3 Auth-Schutz

  • Auth-Codes/-Tokens nur gehasht speichern,
  • Ablaufzeiten,
  • Rate Limits,
  • Fehlversuchslimit,
  • konsumierte Challenges nicht wiederverwenden,
  • generische Antworten vermeiden unnötige E-Mail-Enumeration,
  • sichere Cookies.

32.4 Web-Sicherheit

Mindestens berücksichtigen:

  • CSRF-Schutz für zustandsändernde Requests,
  • XSS-Vermeidung durch Jinja Autoescaping und gezielte Sanitization,
  • Uploadpfade nie aus ungeprüften Dateinamen bauen,
  • restriktive HTML-Einbettung,
  • keine direkte öffentliche Auslieferung privater Uploads,
  • sichere Redirects,
  • Proxy-Headers nur bei aktivierter vertrauenswürdiger Proxy-Konfiguration.

32.5 Datenschutz

  • keine Klient:innendaten,
  • Datensparsamkeit,
  • Teamgrenzen strikt,
  • freiwillige Kontaktdaten opt-in,
  • Login-E-Mail nicht als Teamkontakt missbrauchen,
  • Foto-/Einwilligungsregel,
  • Datenschutzinformationen und Verarbeitungsverzeichnis müssen vor echtem Betrieb organisatorisch mit der Aidshilfe abgestimmt werden.

33. Datenbankstrategie

33.1 SQLite bleibt v1-Entscheidung

Für die geplante Größenordnung bleibt SQLite bewusst bestehen.

Trotzdem:

  • ausschließlich über SQLAlchemy arbeiten,
  • Flask-Migrate/Alembic ab Beginn der neuen Architektur,
  • keine unnötigen SQLite-Spezialkonstruktionen,
  • Datenmodell so halten, dass ein späterer PostgreSQL-Wechsel möglich bleibt.

Ein PostgreSQL-Umstieg wird nicht vorweggebaut.

33.2 Persistenter Pfad

Produktion:

/app/instance/app.db

und Uploads ebenfalls unter dem persistenten /app/instance-Mount.

Der konkrete Host-Volume-/Named-Volume-Name wird im Deployment-Stack dokumentiert.


34. Migrationen

Es gibt einen gemeinsamen Alembic-/Flask-Migrate-Verlauf für die gesamte App:

migrations/

Module besitzen keine separaten, konkurrierenden Migrationssysteme.

Migrationen werden mit der jeweiligen Featureänderung erzeugt, getestet und versioniert.

Deployment führt ausstehende Migrationen bewusst aus; Schemaänderungen werden nicht durch db.create_all() in Produktion „magisch“ erledigt.


35. Tests

Jedes Core-Teil und jedes Fachmodul bringt Tests für seine Regeln mit.

35.1 Pflicht-Sicherheitstests für jedes Teammodul

Mindestens:

  1. keine TeamMembership → Zugriff verboten,
  2. falsches Team → Zugriff verboten,
  3. Modul deaktiviert → Zugriff verboten,
  4. normales Mitglied → nur Mitgliederfunktionen,
  5. Team-Verwaltung → Verwaltungsfunktionen,
  6. Organisationsadmin ohne TeamMembership → Team-Inhalte verboten,
  7. Objekt aus anderem Team → niemals sichtbar/manipulierbar.

35.2 Auth-Tests

  • Code nur einmal verwendbar,
  • abgelaufener Code abgelehnt,
  • Fehlversuchslimit,
  • widerrufene Device Session ungültig,
  • frische Re-Auth für sensible Aktionen.

35.3 Scheduling-Tests

  • Statusübergänge,
  • Verfügbarkeit nach Freeze nicht änderbar,
  • manuelle Zuteilung,
  • Unterbesetzung,
  • eindeutiges Springer-Nachrücken,
  • kein automatisches Raten bei Mehrdeutigkeit,
  • offene Position übernehmen,
  • gleichzeitige Übernahme nur einmal erfolgreich,
  • ursprüngliches unavailable blockiert spätere Selbstübernahme nicht,
  • Dringlichkeit <24 h.

35.4 Documents-Tests

  • Pending-Sichtbarkeit,
  • Freigabe/Ablehnung,
  • normale Mitglieder können kein HTML hochladen,
  • HTML nur isoliert dargestellt,
  • unerlaubte Dateitypen abgelehnt,
  • Storage Path Traversal verhindert,
  • Teamgrenzen bei Download/Preview,
  • Rückzug eigener Pending-Uploads.

35.5 Chat-Tests

  • Announcement-Schreibrecht,
  • Discussion-Schreibrecht,
  • Teamgrenzen,
  • Reply,
  • Edit/Delete nur eigene Nachrichten bzw. Moderation,
  • keine Dateianhänge,
  • Linkregeln,
  • Read/Delivered-State,
  • neue Teammitglieder zählen nicht rückwirkend für alte Read Receipts.

35.6 UI-/A11y-Tests

Mindestens kritische Flows in mobilen Breiten prüfen:

  • Login,
  • Teamwechsel,
  • Verfügbarkeit,
  • Dienstübernahme,
  • Dokumentupload,
  • Chat,
  • Teamverwaltung.

36. Deployment-Vertrag

Deployment ist Teil der Architektur.

36.1 VPS-Struktur

Bestehendes Betriebsprinzip:

~/apps/<name>/      → Git-Arbeitskopie der Anwendung
~/stacks/<name>/    → Compose-/Stack-Konfiguration
Caddy               → Reverse Proxy / TLS

Der endgültige App-/Repo-/Containername ist noch festzulegen; danach wird derselbe Name konsistent für Repo, App-Verzeichnis, Stack-Verzeichnis und Container verwendet.

36.2 Versioniert

Mindestens:

  • Dockerfile
  • compose.yaml bzw. versionierte Stack-Vorlage
  • .env.example
  • requirements.txt
  • Python-Versionsfestlegung
  • Migrationen
  • INSTALL.md / Recovery-Anleitung
  • PROJECT_DESIGN.md

36.3 Nicht versioniert

  • echte .env
  • SECRET_KEY
  • SMTP-Passwort / Credentials
  • Device-/Auth-Secrets
  • Produktionsdatenbank
  • Uploads
  • Backups

36.4 Docker

  • fester Python-Major/Minor-Stand,
  • Gunicorn vorhanden,
  • Prozess läuft als non-root User,
  • persistentes /app/instance,
  • keine Secrets im Image,
  • Healthcheck bzw. /health.

36.5 Proxy

Caddy terminiert TLS und proxyt zur Flask/Gunicorn-App.

ProxyFix wird nur aktiviert, wenn eine explizite Konfiguration wie TRUST_PROXY_HEADERS=true dies verlangt und der Proxy vertrauenswürdig ist.

36.6 Health Endpoint

/health:

  • sehr leichtgewichtig,
  • ohne personenbezogene oder sensible Daten,
  • geeignet für Container-/Deploymentprüfung.

37. E-Mail als Produktionsabhängigkeit

Passwordless Login macht E-Mail zu einer kritischen Infrastrukturkomponente.

SMTP-/Mailkonfiguration ausschließlich über Environment, z. B. sinngemäß:

MAIL_HOST
MAIL_PORT
MAIL_USERNAME
MAIL_PASSWORD
MAIL_FROM

Konkrete Variablennamen können bei Implementierung finalisiert werden, müssen dann in .env.example dokumentiert sein.

Keine Provider-Credentials im Code.

Auth-/Einladungsmails sollen funktionieren, bevor v1 als betriebsbereit gilt.


38. Backup- und Restore-Vertrag

Ein vollständiges Backup besteht mindestens aus einem zusammengehörenden Stand von:

  1. SQLite-Datenbank,
  2. Upload-/Storage-Verzeichnis.

Nur app.db zu sichern ist kein vollständiges Backup.

Backups müssen außerhalb des laufenden Container-Dateisystems verfügbar sein.

Ein Restore-Prozess gilt erst als belastbar, wenn er mindestens einmal praktisch getestet wurde.

Restore-Check:

  • Account/Org/Teams vorhanden,
  • TeamMemberships vorhanden,
  • Dokumentdateien funktionieren,
  • Profilbilder funktionieren,
  • Migrationen auf Zielstand laufen,
  • Login-Mail funktioniert,
  • Teamgrenzen weiterhin korrekt.

39. Reinstall-/Recovery-Vertrag

Eine Neuinstallation auf einem frischen Server soll reproduzierbar sein:

1. Host-Voraussetzungen installieren
2. Caddy / Docker vorbereiten
3. App-Repo nach ~/apps/<name> klonen
4. Stack nach ~/stacks/<name> bereitstellen
5. .env aus .env.example erstellen und Secrets eintragen
6. persistentes Datenvolume anlegen bzw. Backup wiederherstellen
7. Image bauen / Compose starten
8. Migrationen ausführen
9. Caddy-Route aktivieren
10. /health prüfen
11. E-Mail-Login testen
12. Team-/Modulzugriff als Smoke Test prüfen

Die App soll nicht davon abhängen, dass eine einzelne Person „noch weiß, wie das damals eingerichtet war“.


40. Bootstrap des ersten Org-Admins

Es gibt einen CLI-Befehl bzw. äquivalenten kontrollierten Bootstrap:

flask ... bootstrap-org-admin <email>

Der genaue Commandname wird bei Implementierung festgelegt.

Er:

  • nimmt eine E-Mail entgegen,
  • erzeugt keinen hardcodierten Masteraccount,
  • startet einen verifizierten Org-Admin-Einladungsflow,
  • verweigert standardmäßig unkontrollierte weitere Bootstrap-Admins, wenn bereits Organisationsadministration existiert.

Nach Restore kommen Org-Admin-Rechte aus der Datenbank zurück; Bootstrap ist kein normaler täglicher Verwaltungsweg.


41. Vorgeschlagene Projektstruktur

<repo>/
├── PROJECT_DESIGN.md
├── CLAUDE.md                 # optional: kurze Agentenregeln + Verweis hierher
├── TASKS.md                  # neuer Build-/Migrationsplan
├── README.md
├── INSTALL.md
├── .env.example
├── .python-version           # oder gleichwertige Versionierung
├── requirements.txt
├── Dockerfile
├── compose.yaml              # oder Stack-Vorlage
├── migrations/
│
├── app/
│   ├── __init__.py
│   │
│   ├── core/
│   │   ├── auth/
│   │   ├── accounts/
│   │   ├── organizations/
│   │   ├── teams/
│   │   ├── memberships/
│   │   ├── invitations/
│   │   ├── permissions/
│   │   ├── branding/
│   │   ├── storage/
│   │   ├── notifications/
│   │   ├── dashboard/
│   │   ├── profiles/
│   │   └── audit/
│   │
│   ├── modules/
│   │   ├── scheduling/
│   │   ├── documents/
│   │   └── chat/
│   │
│   ├── templates/
│   │   ├── core/
│   │   └── components/
│   │
│   └── static/
│       ├── css/
│       ├── js/
│       └── avatars/
│
├── tests/
└── instance/                 # lokal; nicht versioniert

Die genaue Unterteilung kann pragmatisch angepasst werden. Nicht verhandelbar sind die fachlichen Grenzen Core ↔ Module und Services ↔ Routes.


42. Migration vom bestehenden CheckPoint-Prototyp

Der vorhandene Stand hat bereits große Teile des alten Einzelteam-Konzepts implementiert. Deshalb soll der Umbau nicht als unkontrolliertes „weiter draufprogrammieren“ erfolgen.

Phase 0 — Bestand sichern

  • aktuellen funktionierenden Stand committen,
  • Legacy-Tag/Branch anlegen,
  • aktuelle SQLite-/Uploaddaten sichern, falls reale Daten existieren,
  • relevante bestehende Tests/Flows dokumentieren.

Phase 1 — Dokumente entwirren

  • dieses PROJECT_DESIGN.md als Source of Truth einführen,
  • alte Konzeptdateien als historisch markieren,
  • CLAUDE.md auf Arbeitsregeln + Verweis reduzieren,
  • TASKS.md nicht weiter nach altem Architekturmodell abarbeiten.

Phase 2 — Plattform-Core

In sinnvoll kleinen Migrationen:

  1. Organization
  2. Account
  3. Team
  4. OrganizationMembership
  5. TeamMembership
  6. Einladungsmodelle
  7. BrandProfile
  8. TeamModule
  9. AuditLog
  10. Core Storage / Profile

Bestehende Daten nur migrieren, wenn sie erhaltenswert/real sind. Bei reinem Entwicklungsdatenbestand darf ein sauberer Neu-Seed einfacher und sicherer sein als künstliche Datenrettung.

Phase 3 — Passwordless Auth

  • alten Benutzername-/Passwort-Flow entfernen,
  • Mailkonfiguration,
  • AuthChallenge,
  • DeviceSession,
  • Invite-only Onboarding,
  • Bootstrap Org Admin,
  • Re-Auth für sensible Aktionen.

Phase 4 — Team-/Org-Oberflächen

  • Teamkontext in URLs,
  • Teamwechsler,
  • Mitgliederstartseite,
  • Teamverwaltung,
  • Organisationsverwaltung,
  • Mitgliederverzeichnis,
  • freiwillige Teamkontakte,
  • Avatar/Profile.

Phase 5 — Module Registry + Dashboard

  • ModuleDefinition
  • TeamModule
  • Navigation Contribution
  • Member-/Manager-Dashboard Provider
  • standardisierte DashboardItem-Komponente.

Phase 6 — Scheduling refactor

  • flexible Planungszeiträume,
  • PositionDefinitions,
  • Templates,
  • ShiftPositions,
  • neuer Statusflow,
  • Availability Freeze,
  • Assignments,
  • Springerlogik,
  • dringende Nachbesetzung,
  • Scheduling Notifications/History.

Phase 7 — Documents refactor

  • Core Storage,
  • Teamscoping,
  • Approval/Archive trennen,
  • HEIC/HEIF,
  • Thumbnails,
  • HTML nur für Team-Verwaltung und isoliert,
  • Backup-/Restore-Test mit echten Testdateien.

Phase 8 — Chat refactor

  • generische Channels,
  • Announcement + Discussion,
  • Replies,
  • Moderation,
  • Linkrestriktionen,
  • Polling,
  • delivered/read states,
  • Notification-Ausnahmen.

Phase 9 — Produktionshärtung

  • CSRF / Cookies / Rate Limits,
  • Permission-Invariant-Tests,
  • /health,
  • Gunicorn,
  • non-root Docker,
  • ProxyFix-Konfiguration,
  • Logging,
  • Backup-/Restore-Dokumentation,
  • SMTP Smoke Test.

Phase 10 — Deployment

Erst wenn die neue Architektur, Auth und Teamgrenzen getestet sind, erfolgt der produktive VPS-Deploy nach dem Reinstall-Vertrag.


43. Erweiterung um ein neues Modul

Ein neues Fachmodul folgt immer demselben Rezept:

1. echten Bedarf beschreiben
2. stabilen module_key festlegen
3. ModuleDefinition registrieren
4. Datenmodell mit team_id anlegen
5. Migration erstellen
6. Services implementieren
7. Permissions definieren
8. Routes dünn halten
9. Templates aus zentralen UI-Komponenten bauen
10. Navigation/Dashboard-Beiträge registrieren
11. Team-Scoping- und Permission-Tests schreiben
12. erst dann für ausgewählte Teams aktivieren

Ein neues Modul darf nicht verlangen, den Core fachlich umzubauen, nur um sich selbst sichtbar zu machen.


44. Future Log

Bewusst vorbereitete, aber nicht vorweg implementierte Erweiterungen:

Galerie

Eigenständiges Modul, ggf. gemeinsamer Core Storage.

Inventory

Eigenständiges Modul für Material-/Bestandsverwaltung, falls ein Team echten Bedarf meldet.

Qualifications

Spätere Erweiterung, z. B. erforderliche Schulungen für Positionen. Das Scheduling-Positionsmodell lässt dafür Platz, ohne Qualifikationen heute einzubauen.

Kalenderintegration

Später denkbar, aber nicht Teil des Scheduling-Kerns von v1.

Push / operative E-Mail

Kann später den Notification-Service als Ausgabekanal ergänzen.

Passkeys / 2FA

Vor allem für sensible Organisationsadministration später denkbar.

feinere Rollen / Permissions

Der Code benutzt schon Permission Helpers, damit später neue Rollen möglich sind, ohne Rollenstrings überall zu ändern.

PostgreSQL

Falls Last, Parallelität oder Betrieb es später sinnvoll machen. Kein aktuelles Ziel.


45. Noch bewusst offene Konfigurationsdetails

Diese Punkte sind keine offenen Architekturfragen, sondern dürfen bei Implementierung/Deployment konkretisiert werden. Sie sollen nicht stillschweigend erfunden, sondern als kleine Produkt-/Betriebsentscheidung dokumentiert werden:

  1. endgültiger Produkt-/Repo-/Containername,
  2. exakte Auth-Code-Gültigkeit und Device-Session-Dauer,
  3. konkrete Upload-Größenlimits,
  4. konkrete Liste blockierter URL-Shortener,
  5. endgültige mobile Navigation bei vielen aktivierten Modulen,
  6. konkreter SMTP-Provider und Env-Variablennamen,
  7. genaue Backup-Aufbewahrungsdauer,
  8. ob normale Mitglieder in v1 ihre Teammitgliedschaft selbst verlassen dürfen oder ob eine Team-Verwaltung sie beendet,
  9. genaue Regeln für erneutes Senden/Verlängern abgelaufener Einladungen.

Für diese Details gilt: klein entscheiden, dokumentieren, testen — keine neue Plattformarchitektur daraus machen.


46. Definition of Done für v1

v1 ist nicht fertig, nur weil die Seiten funktionieren.

Sie gilt als bereit, wenn:

  • Passwordless Login zuverlässig funktioniert,
  • Einladung + Onboarding funktionieren,
  • mehrere Accounts und mehrere Teams getestet sind,
  • dieselbe Person in mehreren Teams funktionieren kann,
  • Teamwechsel klar ist,
  • Org-Admin ohne TeamMembership keine Team-Inhalte lesen kann,
  • Team-Verwaltungen keine weiteren Manager ernennen können,
  • Brand Profiles teamweise funktionieren,
  • Module pro Team aktivierbar/deaktivierbar sind,
  • Scheduling komplett im neuen Positionsmodell funktioniert,
  • Documents inkl. Freigabe/Storage/Preview funktionieren,
  • Chat die festgelegten Arbeitskommunikationsregeln einhält,
  • Dashboard-Providers modulunabhängig funktionieren,
  • mobile Hauptflows auf ca. 360460 px gut bedienbar sind,
  • kritische A11y-Grundsätze eingehalten sind,
  • DB-Migrationen reproduzierbar laufen,
  • /health funktioniert,
  • Docker/Gunicorn/Caddy-Setup dokumentiert ist,
  • Backup von DB + Uploads funktioniert,
  • ein Restore praktisch getestet wurde,
  • SMTP-Loginmail im Produktionssetup getestet wurde,
  • Reinstall-Anleitung von einer frischen Umgebung aus nachvollziehbar ist.

47. Kurzfassung der nicht verhandelbaren Architekturregeln

  1. Account ist teamunabhängig.
  2. Teamzugehörigkeit läuft ausschließlich über TeamMembership.
  3. Team-Verwaltung ist zusätzliches Recht eines normalen Teammitglieds.
  4. Nur Organisationsadministration ernennt/entfernt Team-Verwaltungen.
  5. Org-Admin ist kein Generalschlüssel für Teamdaten.
  6. Teaminterne Fachobjekte tragen team_id.
  7. Module dürfen den Core nutzen; der Core kennt keine Fachlogik einzelner Module.
  8. Code definiert vorhandene Module, TeamModule definiert Aktivierung pro Team.
  9. Org-Verwaltung aktiviert Module; Team-Verwaltung konfiguriert sie.
  10. Routes bleiben dünn; Businesslogik lebt in Services.
  11. Permission Helpers statt verstreuter Rollenchecks.
  12. Dashboard zeigt Zustände; Notifications melden Ereignisse.
  13. Keine Passwörter in v1 — E-Mail-Code + langlebige widerrufbare Device Session.
  14. Private Uploads werden nie unkontrolliert statisch ausgeliefert.
  15. SQLite + Migrationen von Anfang an; PostgreSQL ist kein v1-Projekt.
  16. Backup bedeutet Datenbank + Uploads.
  17. Deployment und Restore sind Teil des Produkts, nicht Nacharbeit.
  18. Neue Funktionen werden als klar begrenzte Module ergänzt, nicht in bestehende Module hineingestopft.

48. Nächster Schritt

Aus diesem Dokument wird als nächstes ein neues TASKS.md für den Umbau/Migrationspfad abgeleitet.

Dabei gilt:

  • kleine Sessions,
  • jede Session hat ein klar prüfbares Ergebnis,
  • zuerst Core und Sicherheitsgrenzen,
  • erst danach Fachmodule,
  • bestehende funktionierende Teile werden nur übernommen, wenn sie zum neuen Modell passen,
  • keine alte Architektur erhalten, nur weil bereits Code dafür existiert.

PROJECT_DESIGN.md bleibt während dieses Umbaus die verbindliche Grundlage.