Installable app with push notifications (PWA) #154

Closed
opened 2026-09-26 05:01:11 +00:00 by claude-bot · 2 comments
Collaborator

Idea

The plausible way people use Iris day to day is on a phone. /remote (#24) was laid out phone-first for exactly that reason: standing in the garden looking at the tree. Make Iris installable to the home screen, and give it a notification channel that doesn't require running ntfy or Gotify.

Verified against main: there is no manifest or service worker. frontend/vite.config.ts has only the React plugin.

Part 1: installable (small)

  • vite-plugin-pwa: a web app manifest (name, icons, theme colour, display: standalone), plus shortcuts to /remote and to the review queue.
  • The service worker caches only the static app shell. It must never cache /api/*, /auth/*, /metrics or the calendar feed. A cached auth response or stale API data would be a real bug, not a performance problem. Use network-only for those routes, explicitly, with a test.
  • Check the OIDC redirect in standalone mode. On iOS, an installed PWA can open the provider in an in-app browser and lose the session cookie on return. Verify with Authentik on iOS and Android before calling this done. The session cookie is always Secure (app/auth.py), so this also only works over HTTPS, which is fine for production.

Part 2: Web Push notifications

  • A notification channel beside webhooks. A user opts in per device, from Settings. Subscriptions are stored per user with the events they want.
  • Events: the existing webhook events (push_failed, review_queue_ready), plus the new ones: unapproved_tonight and review_reminder (#149), and state_corrected / state_drift (#148).
  • Server: VAPID keys from env (WEB_PUSH_VAPID_PUBLIC_KEY / _PRIVATE_KEY / _SUBJECT; env-only, never in the settings table or the config export, like the other credentials), plus pywebpush, a small dependency.
  • Dispatch shares the webhook dispatcher's event bus, so that an event fires identically to both channels. There should be no second list of events to keep in step.
  • Platform constraints to document: iOS only supports Web Push for installed PWAs (iOS 16.4+), so Part 1 is a prerequisite there. Delivery also depends on the vendor's push service reaching the phone, which is fine for alerts but not guaranteed to be instant.

Acceptance

  • Installs on Android (Chrome) and iOS (Safari). Login survives the install.
  • The service worker never serves a cached API or auth response. There is a test for it.
  • Push opt-in per device, per event. A test notification button.
  • Every notification-worthy event goes to webhooks and push through one dispatcher.
  • Docs: VAPID key generation and the iOS install requirement. CHANGELOG entry.
## Idea The plausible way people use Iris day to day is on a phone. `/remote` (#24) was laid out phone-first for exactly that reason: standing in the garden looking at the tree. Make Iris installable to the home screen, and give it a notification channel that doesn't require running ntfy or Gotify. Verified against `main`: there is no manifest or service worker. `frontend/vite.config.ts` has only the React plugin. ## Part 1: installable (small) - **`vite-plugin-pwa`:** a web app manifest (name, icons, theme colour, `display: standalone`), plus shortcuts to `/remote` and to the review queue. - **The service worker caches only the static app shell.** It must **never** cache `/api/*`, `/auth/*`, `/metrics` or the calendar feed. A cached auth response or stale API data would be a real bug, not a performance problem. Use network-only for those routes, explicitly, with a test. - **Check the OIDC redirect in standalone mode.** On iOS, an installed PWA can open the provider in an in-app browser and lose the session cookie on return. Verify with Authentik on iOS and Android before calling this done. The session cookie is always `Secure` (`app/auth.py`), so this also only works over HTTPS, which is fine for production. ## Part 2: Web Push notifications - **A notification channel beside webhooks.** A user opts in per device, from Settings. Subscriptions are stored per user with the events they want. - **Events:** the existing webhook events (`push_failed`, `review_queue_ready`), plus the new ones: `unapproved_tonight` and `review_reminder` (#149), and `state_corrected` / `state_drift` (#148). - **Server:** VAPID keys from env (`WEB_PUSH_VAPID_PUBLIC_KEY` / `_PRIVATE_KEY` / `_SUBJECT`; env-only, never in the settings table or the config export, like the other credentials), plus `pywebpush`, a small dependency. - **Dispatch shares the webhook dispatcher's event bus,** so that an event fires identically to both channels. There should be no second list of events to keep in step. - **Platform constraints to document:** iOS only supports Web Push for **installed** PWAs (iOS 16.4+), so Part 1 is a prerequisite there. Delivery also depends on the vendor's push service reaching the phone, which is fine for alerts but not guaranteed to be instant. ## Acceptance - [ ] Installs on Android (Chrome) and iOS (Safari). Login survives the install. - [ ] The service worker never serves a cached API or auth response. There is a test for it. - [ ] Push opt-in per device, per event. A test notification button. - [ ] Every notification-worthy event goes to webhooks and push through one dispatcher. - [ ] Docs: VAPID key generation and the iOS install requirement. CHANGELOG entry.
claude-bot added this to the v1.2.0 milestone 2026-09-26 05:02:38 +00:00
Author
Collaborator

Picking this up now that #164 (the SPA fallback) is merged; the app's start URL and shortcuts depend on it.

Plan: both parts in one PR. Part 1 is the manifest and a shell-only service worker, with the API, auth, metrics and feed paths forced to network-only and tested. Part 2 is Web Push as a second channel on the webhook dispatcher's event bus, so there is no second list of events to keep in step.

Can't be verified from here: installing on real iOS and Android devices, and the OIDC round trip in standalone mode. Those stay as manual acceptance items, with a checklist.

Picking this up now that #164 (the SPA fallback) is merged; the app's start URL and shortcuts depend on it. **Plan:** both parts in one PR. Part 1 is the manifest and a shell-only service worker, with the API, auth, metrics and feed paths forced to network-only and tested. Part 2 is Web Push as a second channel on the webhook dispatcher's event bus, so there is no second list of events to keep in step. **Can't be verified from here:** installing on real iOS and Android devices, and the OIDC round trip in standalone mode. Those stay as manual acceptance items, with a checklist.
Author
Collaborator

Done in #178 (squash-merged, CI green).

Delivered:

  • The app is installable: vite-plugin-pwa, a hand-written service worker, and shortcuts to Remote, Review and Season.
  • The service worker serves only the shell. It doesn't touch /api, /auth, /metrics, /openapi.json or cross-origin requests, which is proven by an E2E with the worker in control.
  • Web Push is a second channel on the webhook dispatcher, with one event list and a formatter for every event.
    • VAPID keys are env-only and excluded from the export.
    • Subscriptions are stored in push_subscriptions (migration 0011).
    • Push endpoints are allowlisted to the known push-service hosts, and viewers get an allowlist of events.

Along the way:

  • The favicon 404 is fixed (/iris.svg never existed).
  • Found #173 and verified it against the published image. It was the missing-dependencies bug that stopped 1.1.0 and 1.1.1 from running scheduler jobs; it was released as the fix in 1.1.2.

Still open, needs devices: the install, login, notification and shortcut checks. They're in the consolidated checklist, #179.

Done in #178 (squash-merged, CI green). **Delivered:** - **The app is installable:** `vite-plugin-pwa`, a hand-written service worker, and shortcuts to Remote, Review and Season. - **The service worker serves only the shell.** It doesn't touch `/api`, `/auth`, `/metrics`, `/openapi.json` or cross-origin requests, which is proven by an E2E with the worker in control. - **Web Push** is a second channel on the webhook dispatcher, with one event list and a formatter for every event. - VAPID keys are env-only and excluded from the export. - Subscriptions are stored in `push_subscriptions` (migration 0011). - Push endpoints are allowlisted to the known push-service hosts, and viewers get an allowlist of events. **Along the way:** - The favicon 404 is fixed (`/iris.svg` never existed). - **Found #173 and verified it against the published image.** It was the missing-dependencies bug that stopped 1.1.0 and 1.1.1 from running scheduler jobs; it was released as the fix in 1.1.2. **Still open, needs devices:** the install, login, notification and shortcut checks. They're in the consolidated checklist, #179.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/Iris-WLED#154
No description provided.