source: Klonkt/docs/webpush-design.md@ dc4aa08

main
Last change on this file since dc4aa08 was dc4aa08, checked in by Robin <roboburr@…>, 7 weeks ago

Docs: web-push design (slice 0)

The build reference for background notifications: RFC 8030 Web Push + VAPID,
Mastodon's model on Klonkt conventions. Decisions: web-push dependency
(lazy import, boot-safe), VAPID keys env-wins-else-storage/.vapid (never
regenerate), minimal payloads, subscriptions tied to the logged-in owner,
S2S-inbox triggers (follow/reply/like/boost/DM). FEP-3ab2 (SSE) noted as the
live-while-open counterpart that doesn't fit cookie-less Klonkt.

New file:
docs/webpush-design.md

  • decisions, data model, flow, trigger points, caveats, slices

-robo
Co-Authored-By: Claude Opus 4.8 <noreply@…>

  • Property mode set to 100644
File size: 3.9 KB
Line 
1# Web Push (VAPID) — ontwerp
2
3Achtergrond-notificaties naar de browser/PWA van de site-eigenaar: nieuwe
4follow, reply/mention, like, boost of DM, ook als de site dicht is. Zelf-gehost
5(RFC 8030 + VAPID, RFC 8292), payload end-to-end versleuteld naar de browser
6(RFC 8291): de push-dienst van de browser (Mozilla/Google/Apple) ziet alleen
7ciphertext.
8
9Dit is het model van Mastodon (`POST /api/v1/push/subscription`), aangepast aan
10Klonkt-conventies. Er is geen FEP voor achtergrond-push; FEP-3ab2 (SSE) dekt
11alleen live-terwijl-open en leunt op een cookie-ticket, wat niet bij Klonkt past.
12
13## Beslissingen
14
15- **Dependency: `web-push` (npm), bewust.** De RFC 8291-payload-encryptie
16 (ECDH + HKDF + aes128gcm) en de VAPID-JWT zijn precies de fiddly,
17 security-gevoelige laag die je niet zelf naschrijft. Lazy import, zoals
18 @simplewebauthn/server: een canary die autofollowt vóór `npm ci` mag nooit
19 op boot crashen.
20- **VAPID-sleutels: env wint, anders auto-gegenereerd bestand.** Zelfde patroon
21 als SESSION_SECRET/PAID_SECRET: `VAPID_PUBLIC_KEY`/`VAPID_PRIVATE_KEY`/
22 `VAPID_SUBJECT` in env als gezet, anders `storage/.vapid` (JSON, 0600),
23 eenmalig gegenereerd. NOOIT regenereren zolang het bestand bestaat: nieuwe
24 keys maken alle bestaande abonnementen ongeldig. Back-up = hele storage/-map.
25- **Subject**: `PUBLIC_BASE_URL` als die er is (VAPID staat https-URL toe),
26 anders `mailto:` fallback uit SMTP_FROM, anders een placeholder-mailto.
27- **Abonnement hangt aan de ingelogde gebruiker** (sessie op het eigen domein).
28 Web Push zelf is cookieloos; alleen het aan/uitzetten is een ingelogde actie.
29- **Payload minimaal**: titel + korte body + doel-URL. Geen volledige teksten
30 van DM's (de push-dienst ziet metadata, nooit meer inhoud dan nodig).
31
32## Datamodel (additief)
33
34```
35push_subscriptions
36 endpoint TEXT PRIMARY KEY -- push-dienst-URL van de browser
37 user_id TEXT NOT NULL -- wie dit abonnement aanzette
38 p256dh TEXT NOT NULL -- client public key (RFC 8291)
39 auth TEXT NOT NULL -- client auth secret (RFC 8291)
40 alert_types TEXT -- JSON: {"follow":1,"reply":1,"like":0,"boost":0,"dm":1}
41 ua_label TEXT -- vrije apparaat-omschrijving voor de lijst
42 created_at DATETIME
43 last_ok_at DATETIME -- laatste geslaagde push
44```
45
46## Flow
47
481. Eigenaar opent Beheer → Notificaties, klikt "Zet aan op dit apparaat".
492. Browser vraagt permissie (moet via user-gesture), `pushManager.subscribe`
50 met de publieke VAPID-key → `POST /push/subscribe` slaat endpoint+keys op.
513. Er gebeurt iets (follow/reply/like/boost/DM in de S2S-inbox):
52 `PushService.notifySite(slug, event)` → per abonnement van de eigenaar,
53 gefilterd op alert_types, `web-push sendNotification` met versleutelde
54 payload.
554. Service worker (`push`-event) toont de notificatie; klik opent de doel-URL.
565. 404/410 van de push-dienst → abonnement verwijderd (device weg/ingetrokken).
57
58## Triggerpunten (S2S-inbox, ActivityPubService)
59
60- `Follow` (na Accept): "X volgt je nu".
61- `Create` Note/reply → interaction 'reply': "X reageerde op <post>".
62- `Like` / `Announce` op eigen post: "X vond <post> leuk" / "X boostte <post>".
63- Prutter-DM (direct note): "Nieuw bericht van X" (zonder inhoud).
64
65Elke trigger is fire-and-forget (`.catch` → log), mag delivery nooit blokkeren.
66
67## Caveats
68
69- **iOS**: alleen voor een geïnstalleerde PWA (Add to Home Screen, iOS 16.4+),
70 en alleen na een user-gesture. De UI toont die hint op iOS-Safari.
71- **Key-rotatie breekt alles**: storage/.vapid is heilig, zie boven.
72- **Shaer-native** (APNs/FCM/UnifiedPush-relay) valt buiten dit pad; de
73 payload-vorm (JSON title/body/url/type) is er alvast op voorbereid.
74
75## Slices
76
77- 0: dit document
78- 1: web-push dep + VAPID-sleutelbeheer + GET /push/vapid
79- 2: tabel + subscribe/unsubscribe + SW-handlers + Beheer-pagina + testknop
80- 3: echte triggers (follow/reply/like/boost/DM) met per-type voorkeuren
81- 4: pruning-bevestiging, throttle, iOS-hint-polish
Note: See TracBrowser for help on using the repository browser.