Skip to content

PWA Web Push for persistent (mobile) notifications #11

Description

@RagingRedRiot

Summary

Add PWA Web Push so users get persistent notifications for new messages even when the tab is closed, backgrounded, or the browser has suspended the page — especially on mobile.

The existing real-time path (WebSocket fan-out via the hub) only delivers while a session is connected. Once a browser suspends the page or the tab is closed, the socket drops and nothing can pop a notification. Web Push is the standard mechanism that works in that state, via a service worker that receives push events independently of any open page.

This issue is documentation/planning only — capturing scope to return to later, not a commitment to implement now.

Why not WebSocket alone

WebSocket delivery requires a live connection. Mobile browsers aggressively suspend background pages, so a WebSocket cannot reliably wake the device or show a notification once the page is backgrounded. Web Push (service worker + push service) is the reliable path for that case.

Scope

Frontend (frontend/)

  1. PWA setup — add a web app manifest, app icons, and a service worker. The service worker is what receives push events when the page is closed/suspended.
  2. Subscription flow — a notification opt-in control. When enabled, the client:
    • requests notification permission,
    • registers the service worker,
    • calls pushManager.subscribe(...) with the server's VAPID public key,
    • sends the resulting subscription object to the backend.
  3. Notification display — the service worker handles the push event and calls showNotification(...). Clicking the notification opens Relay, ideally deep-linked to the relevant room/thread.

Backend (src/)

  1. Subscription storage — a new DB table (sqlx migration) for push subscriptions, roughly:
    • user_id
    • endpoint (URL)
    • p256dh key
    • auth secret
    • created_at / updated_at
    • optional device/browser label
  2. A command/API to register and remove a subscription — a new ClientCommand (and matching handler) so the frontend can submit/revoke its subscription over the existing protocol, plus VAPID public-key exposure to the client.
  3. Push sender — when a message is created, find subscriptions for the target recipients and send a Web Push request to each. This needs VAPID keys and a Rust crate for Web Push encryption/signing (e.g. web-push). The natural trigger point is the message fan-out path (alongside the existing hub publish in the message actor / src/hub.rs); push is the offline complement to the live WebSocket fan-out.
  4. Cleanup — if a push endpoint returns gone/invalid (e.g. 404/410), delete that subscription. The time-based reaper (src/reaper.rs) could also age out long-dead subscriptions.

Mobile / platform caveats

  • Android (Chrome/Edge/Firefox): generally works well for installed PWAs, sometimes in browser contexts too.
  • iOS/iPadOS: Web Push works only for Home Screen web apps (the user must "Add to Home Screen"), not a normal Safari tab.
  • Production requires HTTPS. localhost is fine for development.

Config / ops

  • Generate and store a VAPID keypair; expose the public key to clients and keep the private key server-side (env config, consistent with the existing config-on-disk model).
  • HTTPS in production is a hard requirement for Web Push to function.

Acceptance sketch (for when this is picked up)

  • A user can opt in, and receives a notification for a new message in a room they're a member of while the tab is closed.
  • Clicking the notification opens Relay to the relevant room.
  • Expired/invalid subscriptions are pruned automatically.

Related

  • Roadmap note in README.md ("First-party client polish … notification") and the notification open-question in NOTES.md.
  • Live delivery model: src/hub.rs fan-out + per-session subscriptions; this feature is the offline complement.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions