Files
Florian-netz/docs/superpowers/specs/2026-06-08-floriannetz-design.md
Claude f9e045ebed Add FlorianNetz design specification
Login-only mutual-aid platform for Austrian volunteer fire brigades to
list vehicles/equipment, searchable by other brigades and sorted by
fastest-arriving (drive-time ETA). Next.js + PostgreSQL/Drizzle + Auth.js
(Authentik OIDC + local argon2id), dynamic admin-curated Merkmal system,
self-hosted OSRM/Nominatim, Docker Compose behind external Traefik.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 14:11:50 +02:00

10 KiB

FlorianNetz — Design-Spezifikation

Datum: 2026-06-08 Status: Entwurf (zur Freigabe) Sprache der Anwendung: Deutsch (österreichischer Feuerwehr-Standard, ÖBFV)


1. Zweck

FlorianNetz ist eine ausschließlich für authentifizierte Benutzer zugängliche Web-Plattform, auf der österreichische Freiwillige Feuerwehren ihre Fahrzeuge und Geräte/Ausrüstung erfassen. Andere Wehren können diese durchsuchen, um für die überörtliche Hilfe die am schnellsten eintreffende Ressource zu finden.

Oberstes Prinzip: Kein anonymer Zugriff — kein einziger Seiten- oder API-Endpunkt ist ohne gültige Sitzung erreichbar (default-deny, serverseitig erzwungen).

Nicht-Ziele (vorerst)

  • Kein In-App-Ausleih-/Anfrage-Workflow (nur Kontaktaufnahme out-of-band; für später vorgesehen, Datenmodell offen halten).
  • Keine 2-Faktor-Authentifizierung in v1 (später leicht ergänzbar).
  • Keine Kopplung mit feuerwehr_dashboard in v1 (Architektur hält die spätere Kopplung offen).
  • Kein Mehrmandanten-SaaS — eine selbst gehostete Instanz mit vielen Wehren.

2. Rollen & Zugriff

Rolle Login über Berechtigungen
Platform-Admin Authentik SSO (OIDC) Verwaltet die Taxonomie (Merkmal-Katalog, Fahrzeug-Vorlagen inkl. zugeordneter Merkmale & Auswahloptionen, Geräte-Kategorien); gibt von Wehren vorgeschlagene Merkmale frei; legt Wehren an und stellt den ersten Wehr-Admin-Login bereit; sieht Audit-Log
Wehr-Admin App-Konto (erster Login vom Platform-Admin bereitgestellt) Verwaltet das eigene Wehr-Profil, eigene Fahrzeuge & Geräte (Werte, Zuordnung, Status); legt weitere Wehr-Admins und Lese-Benutzer der eigenen Wehr selbst an
Lese-Benutzer App-Konto (vom Wehr-Admin angelegt) Durchsucht und sieht alle Wehren-Daten inkl. Kontaktinfo; keine Bearbeitung
  • Zwei Authentifizierungswege, eine einheitliche Sitzung mit role + brigadeId.
  • Autorisierung serverseitig: Schreibzugriffe sind auf die eigene Wehr beschränkt; Lesezugriff über alle Wehren (offenes Verzeichnis).
  • Login-Seite zeigt ausschließlich den Login — keine weiteren Inhalte.

3. Informationsarchitektur

  • Startseite (nach Login): eine globale Suche mit prominentem „Meinen Standort verwenden" (GPS). Tabs: Fahrzeuge · Geräte · Wehren.
  • Pro Tab: Namens-/Funkrufnamen-Suche + dynamisch aus dem Merkmal-Katalog erzeugte Filter (typabhängig: Schieberegler / Dropdown / Ja-Nein-Schalter) + Status-Filter („nur einsatzbereit").
  • Trefferliste: sortiert nach Eintreffzeit (Fahrzeit-ETA); jede Zeile zeigt Wehr, Eckdaten, ETA/Entfernung, Status und 📞 Kontakt (Telefonnummer der Wehr).
  • Detailseiten:
    • Fahrzeug: Eckdaten-Raster (aus Merkmalwerten) · Beladung als verlinkte Geräteliste · Wehr-Karte · expliziter Button „Wehr kontaktieren".
    • Gerät: Merkmale · zugeordnetes Fahrzeug oder „im Gerätehaus" · Wehr-Karte · Kontakt.
    • Wehr: Profil (Adresse, Kontakt, Funkrufnamen-Schema), eigener Fuhrpark, Kontakt.

Design / Corporate Identity

  • Richtung „Amtlich": seriös, behördlich, ruhig. Navy-Primärfarbe, Signalrot als Akzent/Signal, Serifen-Überschriften, viel Weißraum, tabellarische Ziffern.
  • Logo „Netzknoten": vernetzte Wehren-Knoten um einen roten Mittelpunkt (Florian + Netz).
  • Palette: Navy #1B3A5B, Signalrot #E2231A, Anthrazit #1A2530, Nebelgrau #F6F8FA, Grün/bereit #1F8F5A, Bernstein/Wartung #B5460F.
  • Typografie: Serif-Display (Source Serif / Georgia) für Überschriften, humanistische Sans (Inter / System-UI) für UI, Tabular-Nums für Datenspalten.
  • Bewusst kein generischer „KI-Look" (keine Verlaufsflächen, nicht alles abgerundet, keine Emoji-Dekoration).

4. Dynamisches Merkmal-System (Kernstück)

Ein starres Spaltenschema skaliert nicht — die Vielfalt an Fahrzeugen/Geräten ist zu groß. Stattdessen typisierte, vom Admin gepflegte Merkmale.

  • Merkmal-Katalog (Admin): jedes Merkmal hat Name, Typ (Zahl / Auswahl / Ja-Nein / Text), optional Einheit; bei Auswahl definiert der Admin die erlaubten Optionen.
  • Fahrzeug-Vorlagen (Admin): definieren die österreichischen Standard-Bezeichnungen (z. B. KLFA, LFA, LFB-A, TLFA 2000/4000, RLFA, SRF, DLK 23/12, WLFA, MTF, KDOF) und pro Vorlage, welche Merkmale gelten sowie deren Optionen/Vorgabewerte. Inklusive Standard-Beladung.
  • Geräte-Kategorien (Admin): je Kategorie ein eigener Merkmal-Satz.
  • Anlegen durch die Wehr: Vorlage wählen (füllt Merkmale & Beladung vor, alles editierbar) oder „Eigenes Fahrzeug/Gerät" frei aus dem Katalog zusammenstellen.
  • Hybrid-Governance: Ein von einer Wehr neu angelegtes Merkmal ist sofort am eigenen Objekt nutzbar und auf dessen Detailseite sichtbar; es wird erst nach Admin-Freigabe (prüfen / zusammenführen) zu einem globalen Suchfilter. Hält die Suche über alle Wehren konsistent.
  • Suche: Filter werden dynamisch aus dem Katalog erzeugt; der Typ bestimmt das Eingabeelement und die Abfrage (Range / Gleichheit / Boolean).

5. Geografie & „am schnellsten eintreffend"

  • Jede Wehr hat Koordinaten (aus der Adresse geokodiert).
  • Suchstandort = Geräte-GPS oder eingegebene Adresse (geokodiert).
  • Treffer werden nach Fahrzeit-ETA von der jeweils besitzenden Wehr zum Suchstandort sortiert.
  • Selbst gehostet zur Wahrung der Privatsphäre (keine Standortdaten an Dritte):
    • OSRM für Fahrzeit/Routing (Österreich-OSM-Extrakt), als Distanz-/Zeit-Matrix.
    • Nominatim (oder Photon) für Geokodierung (Österreich-Extrakt).
    • Fallback: Luftlinie (Haversine), falls der Routing-Dienst nicht erreichbar ist — klar gekennzeichnet.
  • Optionale MapLibre-GL-Kartenansicht (Liste bleibt primär).

6. Datenmodell (PostgreSQL, indikativ)

Typisierte Merkmalwerte in einer eigenen Wert-Tabelle (EAV mit typisierten Spalten) für saubere Range-/Enum-/Boolean-Indizes.

  • brigades: id, name, art (FF), strasse, plz, ort, bundesland, lat, lng, funkrufname_schema, telefon, email, wehrführer, aktiv, erstellt_am
  • users: id, brigade_id (NULL für Platform-Admin), rolle (platform_admin | wehr_admin | wehr_read), auth_typ (authentik | local), email, name, passwort_hash (NULL bei authentik), aktiv, erstellt_von, erstellt_am
  • merkmale (Attributdefinitionen): id, name, typ (number|enum|boolean|text), einheit, geltungsbereich (vehicle|equipment|both), status (active|proposed), vorgeschlagen_von_brigade_id, erstellt_am
  • merkmal_optionen (für enum): id, merkmal_id, wert, label, reihenfolge
  • vehicle_templates (Vorlagen): id, code (z. B. "TLFA 4000"), name, beschreibung
  • vehicle_template_merkmale: template_id, merkmal_id, vorgabewert (NULL), pflicht (bool), reihenfolge
  • equipment_categories: id, name
  • equipment_category_merkmale: category_id, merkmal_id, reihenfolge
  • vehicles: id, brigade_id, template_id (NULL bei Eigenbau), name, funkrufname, status (einsatzbereit|wartung|ausser_dienst), notiz, erstellt_am
  • equipment: id, brigade_id, category_id, vehicle_id (NULL = im Gerätehaus), name, status, erstellt_am
  • merkmal_values: id, merkmal_id, entity_typ (vehicle|equipment), entity_id, value_num, value_text, value_bool — Indizes: (merkmal_id, value_num), (merkmal_id, value_bool), (merkmal_id, value_text); sowie (entity_typ, entity_id)
  • audit_log: id, actor_user_id, aktion, ziel_typ, ziel_id, details (jsonb), zeitpunkt

7. Tech-Stack

  • Next.js (App Router, TypeScript) — Full-Stack (Server Components + Route Handlers / Server Actions), wenig Boilerplate.
  • PostgreSQL + Drizzle ORM — typsicher und SQL-nah; besser geeignet als Prisma für die dynamischen Merkmal-Abfragen (Range/Enum-Filter).
  • Auth.js (NextAuth v5) — Authentik-OIDC-Provider (Platform-Admins) + Credentials-Provider mit argon2id (Wehr-Konten); default-deny Middleware auf allen Routen; einheitliche Session mit role + brigadeId.
  • Tailwind CSS + Radix-UI-Primitives, eigens auf die „Amtlich"/Netzknoten-Identität gethemt (volle Kontrolle über den eigenständigen Look; bewusst nicht MUI).
  • Zod (Validierung), MapLibre GL (Karte), OSRM + Nominatim (Docker, Österreich-Extrakt).
  • Tests: Playwright (E2E, insbesondere Auth-Gating-Tests) + Vitest (Units).

8. Sicherheit

  • Default-deny serverseitig auf allen Seiten und API-Routen; keine reine Client-Absicherung.
  • Rollen- + Wehr-Scoping bei jedem Schreibzugriff serverseitig erzwungen.
  • argon2id-Passwort-Hashing; sichere Cookies (httpOnly, secure, sameSite); CSRF-Schutz; Rate-Limiting am Login; Security-Header.
  • Audit-Log für Admin- und Bereitstellungsaktionen (Wehr/Benutzer anlegen, Merkmal-Freigaben).
  • 2FA für Wehr-Konten als spätere, einfache Erweiterung vorgesehen.

9. Deployment

  • Docker Compose: Container für app (Next.js), postgres, osrm, nominatim.
  • Reverse Proxy: vorhandener Traefik (extern). Kein gebündelter Proxy. Der App-Container wird per Traefik-Labels (Router/Service-Port, TLS, ggf. Middlewares) am externen Traefik-Netzwerk angebunden.
  • Die App muss X-Forwarded-Proto/X-Forwarded-Host von Traefik vertrauen, damit sichere Cookies und Auth.js-Callback-URLs korrekt funktionieren; vertrauenswürdige Hosts konfigurieren.
  • Authentik: Redirect-URI der App in Authentik registrieren; Client-ID/-Secret + Issuer-URL via Umgebungsvariablen.

10. Offene Eingaben / Abhängigkeiten

  • Österreichische Standarddokumente (Standard-Fahrzeugbezeichnungen + Beladelisten) zur Befüllung des Merkmal-Katalogs und der Vorlagen — abzulegen unter florian-netz/docs/reference/. Blockiert das Design nicht, aber die Seed-Daten.
  • Bundesland (für Funkrufnamen-Schema und ggf. regionale Bezeichnungen) — noch zu bestätigen.

11. Zukünftige Kopplung mit feuerwehr_dashboard

In v1 eigenständig, aber kopplungsfreundlich: gemeinsamer Authentik-Identitätsanbieter, kompatible PostgreSQL- und Docker/Traefik-Konventionen. Eine spätere Integration (geteilte Identitäten, Querverweise) bleibt dadurch unkompliziert.