diff --git a/docs/README.md b/docs/README.md index 20d7809..36e2c68 100644 --- a/docs/README.md +++ b/docs/README.md @@ -6,3 +6,4 @@ Diese Dokumentation unterstützt dich bei der täglichen Nutzung von FEDEO. - [Bedienung](./bedienung/README.md) - [Kommunikationslösung auf Basis des Matrix-Standards](./kommunikationslösung-matrix.md) +- [Zentraler Push-Server für Selfhost-Instanzen](./zentraler-push-server.md) diff --git a/docs/zentraler-push-server.md b/docs/zentraler-push-server.md new file mode 100644 index 0000000..491b5fb --- /dev/null +++ b/docs/zentraler-push-server.md @@ -0,0 +1,429 @@ +# Zentraler Push-Server für FEDEO + +Dieser Entwurf beschreibt einen zentral betriebenen FEDEO-Push-Server, über den selbst gehostete FEDEO-Instanzen Push Notifications zustellen können. Ziel ist, dass Selfhost-Betreiber keine eigenen Apple-, Google- oder Web-Push-Zugangsdaten verwalten müssen und FEDEO trotzdem mandantenfähig, datensparsam und zuverlässig benachrichtigen kann. + +## Zielbild + +FEDEO erhält einen zentralen Zustelldienst unter einer FEDEO-kontrollierten Domain, zum Beispiel `https://push.fedeo.cloud`. Jede selbst gehostete Instanz registriert sich dort einmalig als Push-Client. Danach sendet die Instanz nur noch neutrale Zustellaufträge an den zentralen Server. Der zentrale Server übernimmt: + +- Verwaltung öffentlicher Push-Konfigurationen für Web, iOS und Android +- Annahme signierter Zustellaufträge von Selfhost-Instanzen +- Zustellung über Web Push, APNs und FCM +- Retry, Rate Limits, Fehlerklassifizierung und Deaktivierung ungültiger Geräte +- technische Zustellmetriken ohne fachliche Inhaltsdaten + +Die fachliche Entscheidung, wer welche Benachrichtigung erhält, bleibt in der jeweiligen FEDEO-Instanz. Der zentrale Server ist nur Transport- und Zustellkomponente. + +## Grundprinzipien + +- **Datenminimierung:** Der zentrale Server speichert keine FEDEO-Nutzerdaten, keine Mandantendaten und möglichst keine lesbaren Nachrichtentexte. +- **Instanzhoheit:** Selfhost-Instanzen behalten Nutzerverwaltung, Benachrichtigungseinstellungen und Eventlogik. +- **Zentrale Provider-Schlüssel:** APNs-, FCM- und VAPID-Schlüssel liegen ausschließlich im zentralen Push-Server. +- **Signierte Aufträge:** Jede Instanz authentifiziert sich per rotierbarem Instanzschlüssel. +- **Mandantentrennung:** Instanzen und deren Geräte werden strikt getrennt, auch wenn sie denselben zentralen Dienst nutzen. +- **Ausfallsicherheit:** Der lokale FEDEO-Betrieb darf nicht blockieren, wenn der Push-Server temporär nicht erreichbar ist. + +## Architektur + +```text +FEDEO Web / Mobile App + | + | registriert Gerät bei lokaler Selfhost-Instanz + v +Selfhost-FEDEO-Instanz + | + | signierte API-Aufrufe + v +Zentraler FEDEO Push-Server + | + +-- PostgreSQL für Instanzen, Geräte, Zustellaufträge, Audit-Metadaten + +-- Redis oder Queue für Retry und Backpressure + +-- Worker für Web Push, APNs und FCM + +-- Provider-Schlüsselverwaltung + | + +--> Web Push Endpoints + +--> Apple Push Notification service + +--> Firebase Cloud Messaging +``` + +### Komponenten + +| Komponente | Aufgabe | +| --- | --- | +| Selfhost-FEDEO-Backend | entscheidet Empfänger, erzeugt Notification Items, übermittelt Push-Aufträge | +| Zentrale Push API | registriert Instanzen und Geräte, nimmt Push-Aufträge an | +| Push Worker | sendet an Web Push, APNs und FCM, verarbeitet Provider-Fehler | +| Geräte-Registry | speichert technische Push Tokens oder Web-Push-Subscriptions | +| Zustelljournal | speichert technische Statuswerte, keine fachliche Historie | +| Admin-Konsole | zeigt Instanzstatus, Fehlerquoten, Rate Limits und Schlüsselrotation | + +## Rollenverteilung + +### Lokale FEDEO-Instanz + +Die Selfhost-Instanz bleibt führend für: + +- Nutzer und Mandanten +- Benachrichtigungseinstellungen +- Event-Typen und Kanäle +- fachliche Nachrichtentexte +- In-App-Benachrichtigungen +- lokale Audit- und Lesestatusdaten + +Sie speichert weiterhin `notifications_items` und entscheidet, ob für ein Ereignis der Kanal `push` aktiv ist. Statt selbst über `web-push` zu senden, ruft sie den zentralen Push-Server auf. + +### Zentraler Push-Server + +Der zentrale Server ist zuständig für: + +- öffentliche Push-Konfiguration für Clients +- Entgegennahme von Geräte-Registrierungen +- Mapping von lokalen Geräte-IDs auf Provider Tokens +- Zustellung an externe Push-Netze +- technische Fehlerbehandlung +- Missbrauchsschutz, Quotas und Sperren + +Er kennt keine FEDEO-Berechtigungen. Wenn eine lokale Instanz einen gültig signierten Push-Auftrag sendet, wird dieser technisch zugestellt. + +## Datenschutzmodell + +Für die erste Ausbaustufe sollte der Push-Server Push-Inhalte nur transitär verarbeiten und nicht dauerhaft speichern. Dauerhaft gespeichert werden: + +- Instanz-ID +- Geräte-ID +- Plattform: `web`, `ios`, `android` +- Provider-Endpunkt oder Provider-Token, verschlüsselt gespeichert +- Zeitpunkte: erstellt, zuletzt gesehen, deaktiviert +- Zustellstatus pro Auftrag: angenommen, gesendet, fehlgeschlagen +- technische Fehlercodes + +Nicht dauerhaft gespeichert werden: + +- Name, E-Mail oder lokale Nutzer-ID +- Mandantenname +- fachliche Objekt-IDs, sofern nicht zwingend notwendig +- vollständige Nachrichtentexte + +Für Benachrichtigungen mit sensiblen Inhalten empfiehlt sich ein Payload-Modell mit generischem Text: + +```json +{ + "title": "Neue FEDEO-Benachrichtigung", + "body": "Öffne FEDEO, um die Details anzusehen.", + "data": { + "notificationId": "lokale-notification-id", + "instanceBaseUrl": "https://app.example.com" + } +} +``` + +Die lokale App lädt Details anschließend authentifiziert von der Selfhost-Instanz. + +## Instanzregistrierung + +Jede Selfhost-Instanz erhält beim Setup eine zentrale Instanzidentität. + +### Ablauf + +1. Betreiber öffnet in FEDEO den Bereich `Administration > Push Notifications`. +2. FEDEO erzeugt eine lokale Instanz-ID, falls noch keine existiert. +3. Betreiber meldet die Instanz am zentralen Push-Portal an oder nutzt einen geführten Aktivierungscode. +4. Der zentrale Server erzeugt einen Client-Schlüssel. +5. Die Selfhost-Instanz speichert `FEDEO_PUSH_INSTANCE_ID` und `FEDEO_PUSH_CLIENT_SECRET`. +6. Die Instanz ruft regelmäßig einen Healthcheck auf. + +### Benötigte Umgebungsvariablen + +```env +FEDEO_PUSH_MODE=central +FEDEO_PUSH_GATEWAY_URL=https://push.fedeo.cloud +FEDEO_PUSH_INSTANCE_ID=inst_... +FEDEO_PUSH_CLIENT_SECRET=... +FEDEO_PUSH_PAYLOAD_MODE=minimal +``` + +`FEDEO_PUSH_MODE=local` bleibt für Entwickler und Betreiber möglich, die eigene VAPID-Schlüssel nutzen möchten. + +## Geräte-Registrierung + +Die lokale FEDEO-Instanz sollte weiterhin der erste Ansprechpartner für den Client bleiben. Dadurch kann sie Authentifizierung und Mandantenzuordnung prüfen. + +### Web Push + +1. FEDEO Web fragt lokal `/api/notifications/push/config` ab. +2. Die lokale Instanz liefert bei `central` die öffentliche VAPID-Konfiguration des zentralen Servers. +3. Der Browser erzeugt eine Web-Push-Subscription. +4. FEDEO Web sendet die Subscription an die lokale Instanz. +5. Die lokale Instanz registriert die Subscription beim zentralen Push-Server. +6. Der zentrale Server gibt eine `centralDeviceId` zurück. +7. Die lokale Instanz speichert diese ID in `notification_push_subscriptions.meta`. + +### Mobile Push + +Für iOS und Android registriert die FEDEO-App das Gerät über die native Push-Schicht und sendet den Provider Token an die lokale Instanz. Die lokale Instanz leitet ihn an den zentralen Push-Server weiter. Der zentrale Server speichert den Token verschlüsselt und liefert eine `centralDeviceId` zurück. + +## API-Entwurf + +Alle Instanz-API-Aufrufe nutzen HTTPS und eine HMAC-Signatur oder kurze JWTs mit rotierbarem Secret. + +### `GET /v1/public-config` + +Liefert öffentliche Push-Parameter. + +```json +{ + "webPushPublicKey": "...", + "iosBundleId": "cloud.fedeo.app", + "androidSenderId": "..." +} +``` + +### `POST /v1/instances/heartbeat` + +Meldet Status und Version der Selfhost-Instanz. + +```json +{ + "instanceId": "inst_123", + "fedeoVersion": "2026.05.22", + "baseUrl": "https://app.example.com", + "capabilities": ["web_push", "minimal_payload"] +} +``` + +### `POST /v1/devices` + +Registriert oder aktualisiert ein Gerät. + +```json +{ + "localDeviceId": "sub_456", + "platform": "web", + "subscription": { + "endpoint": "https://...", + "keys": { + "p256dh": "...", + "auth": "..." + } + }, + "meta": { + "browser": "Firefox", + "os": "macOS" + } +} +``` + +Antwort: + +```json +{ + "centralDeviceId": "dev_789", + "status": "active" +} +``` + +### `DELETE /v1/devices/{centralDeviceId}` + +Deaktiviert ein Gerät. + +### `POST /v1/push` + +Nimmt einen Zustellauftrag entgegen. + +```json +{ + "idempotencyKey": "notification-item-id:dev_789", + "devices": ["dev_789"], + "priority": "normal", + "ttlSeconds": 3600, + "collapseKey": "communication.message.new", + "notification": { + "title": "Neue FEDEO-Benachrichtigung", + "body": "Öffne FEDEO, um die Details anzusehen." + }, + "data": { + "notificationId": "lokale-notification-id", + "link": "/communication" + } +} +``` + +Antwort: + +```json +{ + "accepted": 1, + "rejected": 0, + "deliveryJobId": "job_abc" +} +``` + +### `GET /v1/push/{deliveryJobId}` + +Liefert technische Zustellinformationen für Debugging und Support. + +```json +{ + "status": "completed", + "sent": 1, + "failed": 0, + "disabledDevices": [] +} +``` + +## Lokale Backend-Anpassung + +Der bestehende `NotificationService` kann um einen Provider ergänzt werden: + +```text +NotificationService + | + +-- InAppDeliveryProvider + +-- EmailDeliveryProvider + +-- LocalWebPushDeliveryProvider + +-- CentralPushDeliveryProvider +``` + +`CentralPushDeliveryProvider` nutzt die lokalen Einträge aus `notification_push_subscriptions`, sucht aktive Geräte mit `centralDeviceId` und sendet Zustellaufträge an den zentralen Push-Server. Ohne `centralDeviceId` versucht er eine Nachregistrierung oder markiert den lokalen Eintrag als fehlerhaft. + +Für Kompatibilität sollte die lokale Tabelle erweitert oder die vorhandene `meta`-Spalte genutzt werden: + +```json +{ + "centralDeviceId": "dev_789", + "platform": "web", + "registeredVia": "central", + "lastGatewaySyncAt": "2026-05-22T10:00:00Z" +} +``` + +## Zustellverhalten + +### Idempotenz + +Jede lokale Instanz sendet pro Notification Item und Gerät einen stabilen `idempotencyKey`. Der zentrale Server verarbeitet denselben Key innerhalb eines konfigurierbaren Fensters nur einmal. + +### Retry + +Der zentrale Server unterscheidet: + +- temporäre Fehler: Netzwerk, Provider-Timeout, Rate Limit +- dauerhafte Fehler: ungültiges Token, abgelaufene Web-Push-Subscription +- Konfigurationsfehler: gesperrte Instanz, ungültige Signatur, Quota überschritten + +Temporäre Fehler werden mit exponentiellem Backoff wiederholt. Dauerhafte Gerätefehler deaktivieren das zentrale Gerät und werden an die lokale Instanz zurückgemeldet. + +### Rückmeldung an lokale Instanzen + +Für die erste Ausbaustufe reicht Pull-basiertes Debugging über `GET /v1/push/{deliveryJobId}`. Später kann ein Webhook ergänzt werden: + +```text +POST /api/notifications/push/gateway-callback +``` + +Damit kann die lokale Instanz ungültige Geräte automatisch deaktivieren. + +## Sicherheit + +### Authentifizierung + +Empfohlen ist ein HMAC-Schema: + +```text +X-Fedeo-Instance-Id: inst_123 +X-Fedeo-Timestamp: 2026-05-22T10:00:00Z +X-Fedeo-Signature: hmac-sha256(...) +``` + +Signiert werden Methode, Pfad, Timestamp, Body-Hash und Instanz-ID. Requests mit zu altem Timestamp werden abgelehnt. + +### Secret-Rotation + +Der zentrale Server sollte pro Instanz zwei aktive Secrets unterstützen: + +- `currentSecret` +- `nextSecret` + +Die Selfhost-Instanz kann dadurch ohne Ausfall rotieren. Nach erfolgreichem Wechsel wird das alte Secret deaktiviert. + +### Missbrauchsschutz + +- Rate Limit pro Instanz +- Tageskontingent pro Instanz +- Maximalgröße für Payloads +- Blocklist für kompromittierte Instanzen +- Audit-Log für Admin-Aktionen +- strikte Validierung von Links, damit Pushes nicht zu fremden Domains leiten + +## Betriebsmodell + +### Deployment + +Der zentrale Push-Server sollte als eigener Dienst betrieben werden: + +- `push-api`: HTTP API für Instanzen und Admin-Konsole +- `push-worker`: Queue-Verarbeitung und Provider-Zustellung +- `postgres`: Instanzen, Geräte, Jobs, Audit +- `redis`: Queue, Rate Limits, kurzlebige Idempotenzdaten +- `traefik` oder anderer Reverse Proxy mit TLS + +### Monitoring + +Wichtige Metriken: + +- angenommene Push-Aufträge pro Instanz +- Zustellquote pro Plattform +- Provider-Latenz +- Fehlerrate nach Fehlerklasse +- deaktivierte Geräte +- Queue-Länge und Retry-Alter +- Signaturfehler und Rate-Limit-Treffer + +### Verfügbarkeit + +Wenn der zentrale Push-Server nicht erreichbar ist, markiert die lokale Instanz Push-Zustellungen als temporär fehlgeschlagen und versucht sie später erneut. In-App-Benachrichtigungen bleiben davon unberührt. + +## Migrationspfad + +### Phase 1: Web Push Gateway + +- zentrale VAPID-Schlüssel bereitstellen +- Selfhost-Instanzen gegen Push-Server authentifizieren +- Web-Push-Subscriptions zentral registrieren +- bestehendes lokales Web-Push-Sending optional durch Gateway ersetzen +- minimale Payloads verwenden + +### Phase 2: Mobile Push + +- iOS und Android Tokens registrieren +- APNs und FCM im zentralen Worker anbinden +- Deep Links zur passenden Selfhost-Instanz öffnen +- Token-Rotation und App-Neuinstallation robust behandeln + +### Phase 3: Rückmeldungen und Admin + +- Zustellcallbacks an Selfhost-Instanzen +- Admin-Konsole für Instanzen, Sperren, Quotas und Fehleranalyse +- Secret-Rotation per Oberfläche +- Export technischer Logs für Supportfälle + +### Phase 4: Erweiterte Kanäle + +- mandantenweite Push-Richtlinien +- gebündelte Pushes +- stille Pushes für Synchronisation +- optional Webhook-Zustellung für Integrationen + +## Offene Entscheidungen + +- Sollen Push-Inhalte standardmäßig immer minimal sein oder darf eine Instanz explizit lesbare Titel und Texte erlauben? +- Wird die Instanzregistrierung über ein zentrales FEDEO-Konto oder über offline erzeugte Aktivierungscodes erfolgen? +- Soll der zentrale Push-Server Bestandteil eines größeren FEDEO-Cloud-Portals werden? +- Wie lange sollen technische Zustelllogs maximal aufbewahrt werden? +- Welche Quotas gelten für kostenlose, lizenzierte und interne Instanzen? + +## Empfehlung für den ersten Schnitt + +Für den ersten produktionsnahen Stand sollte FEDEO nur Web Push über den zentralen Server abwickeln. Das passt direkt zum vorhandenen Benachrichtigungssystem und reduziert den Betreiberaufwand für Selfhost-Instanzen deutlich. Die lokale Instanz bleibt fachlich führend, der zentrale Server sieht nur technische Geräte und minimale Zustellaufträge. + +Die wichtigste Architekturentscheidung ist dabei, den zentralen Server nicht als Benachrichtigungslogik zu bauen, sondern als schlanken Push-Transport. Dadurch bleibt Selfhosting souverän, während FEDEO trotzdem die komplizierten Provider-Schlüssel und mobilen Push-Integrationen zentral beherrschen kann.