# 40 — Web Push (VAPID) ## Goal Subscribe browsers to web push and deliver notifications to them. ## Depends on - 06 (auth), 50 (PWA shell — needed for iOS to allow push). If 50 isn't done, this task can land but iOS won't fire until then. ## Scope - `pnpm add web-push` for server, use built-in `PushManager` on client. - VAPID keys generated once (script: `pnpm vapid:generate`), stored in `.env` as `VAPID_PUBLIC_KEY` / `VAPID_PRIVATE_KEY`. Document in README. - Schema: `push_subscriptions` (`id`, `user_id`, `endpoint` unique, `p256dh`, `auth`, `user_agent`, `created_at`). - Service worker registers on app load; on user opt-in (button in `/settings`), prompts permission and stores subscription via server action. - Server helper `sendPush(userId, { title, body, url })` iterates subscriptions, removes any returning 404/410. ## Out of scope - Notification preferences per category (defer). - ntfy fallback (task 42). ## Acceptance criteria - [ ] Opt-in flow works in Chrome desktop and mobile, plus iOS Safari (when PWA installed). - [ ] Stale subscriptions are pruned on send failure. - [ ] Sending a test notification from `/settings` reaches all of the current user's subscribed devices.