Source: apps/oshun/web/public/manifest.json, apps/oshun/web/public/sw.js,
apps/oshun/web/src/components/PwaBootstrap.tsx,
apps/oshun/web/src/components/PwaInstallPrompt.tsx,
apps/oshun/web/src/components/PwaInstallTriggerButton.tsx,
apps/oshun/web/src/components/PwaUpdatePrompt.tsx,
apps/oshun/web/src/components/PwaOfflineFallback.tsx,
apps/oshun/web/src/components/OfflineBanner.tsx,
apps/oshun/web/src/lib/pwa-runtime.ts,
apps/oshun/web/src/lib/pwa-service-worker.ts,
apps/oshun/web/src/lib/offline-shell-routes.ts, and
apps/oshun/web/src/lib/pwa-recent-content.ts.
The PWA story - install, offline, update, sync, relaunch, and push routing - is
the spine of "PWA" in the product name. Walk this whenever the manifest,
sw.js, or PwaBootstrap changes.
Manifest#
/manifest.json
-
nameandshort_nameareOSHUN. -
descriptionnames the one-shell/six-domain product promise. -
idis/,start_urlis/?surface=pwa, andscopeis/. -
displayisstandalone;display_overrideis["window-controls-overlay","standalone"]. -
theme_colorandbackground_colorare#f1ebdd. - Categories include health, education, lifestyle, and productivity.
- Icons include 192 and 512 pixel
anyandmaskablePNGs. - Screenshots include two narrow 390x844 and two wide 1440x960 assets.
- Shortcuts cover Tara, Veritas, Nyx, Arete, Nisaba, and Metis.
- Install assets are resolved by
pwa-smoke.spec.ts.
Install prompt#
PwaInstallPrompt.tsx + PwaInstallTriggerButton.tsx
- Eligibility is event-driven: the custom prompt appears when the browser
fires
beforeinstallprompt. - The component calls
preventDefault(), stores the deferred browser event, and renders[data-pwa-prompt][data-pwa-state="available"]. - The manual
/welcome/downloadtrigger dispatchesoshun-trigger-pwa-install, which calls the saved prompt event. - Accepted prompt outcome records install state and renders the installed guidance state.
- Explicit dismiss or dismissed browser outcome writes
oshun_pwa_install_dismissed; reloads suppress the prompt during the 14-day cooldown. - Standalone runtime detection records install state from
(display-mode: standalone)and suppresses the available prompt. - iPhone/iPad Safari is documented through the manual Share -> Add to Home
Screen copy on
/welcome/download; Safari does not exposebeforeinstallprompt. - There is no V1 app-side "N qualifying interactions" threshold; the browser
decides when
beforeinstallpromptfires.
Service worker lifecycle#
sw.js + PwaBootstrap.tsx + lib/pwa-service-worker.ts
-
registerServiceWorker()registers/sw.jsat scope/on mount when the browser exposesnavigator.serviceWorker. - Registration failures are caught, logged, and reset the one-shot guard so a later mount can retry.
- With service workers blocked by the browser, the authenticated shell still renders instead of white-screening.
- A waiting worker surfaces
PwaUpdatePromptonly after cookie consent is resolved. - The update prompt distinguishes standard routes from reading routes and uses route-specific copy.
- "Refresh now" posts
OSHUN_SKIP_WAITING; controllerchange emitspwa_update_appliedand reloads. - "Later" / "Keep reading" dismisses only the current prompt token; a cold launch with the same waiting worker re-prompts.
-
OSHUN_SW_UPDATEmessages move the prompt to the ready state. - A lost skip-waiting response surfaces the manual reload escape before the silent fallback reload.
- Multi-tab update application reloads each open route once and preserves route identity.
-
activatedeletes staleoshun-*caches while preserving current version and foreign caches.
Cache strategies#
sw.js defines static, runtime, BFF, and media caches with
CACHE_VERSION = 'v7'.
- Static assets include the app shell, public install routes, manifest, icons, and screenshots.
- Runtime navigation fallback uses cached shell HTML for supported offline shell paths.
- BFF responses use stale-while-revalidate with the general BFF max-age policy.
- Nisaba offline reads use
NISABA_OFFLINE_BFF_MAX_AGE_MS(24 hours) for offline-pinned scholarly content. - Media uses
MEDIA_CACHEwithMAX_MEDIA_ENTRIES = 200; runtime cache eviction usesMAX_RUNTIME_ENTRIES = 100. - Auth/session paths are denied from cache and fail closed while offline.
- Cache misses on offline deep navigations render the static service-worker
fallback and hydrate into
PwaOfflineFallbackwhen the client is available.
Offline behavior#
OfflineBanner.tsx, PwaOfflineFallback.tsx, and offline-shell-routes.ts
-
navigator.onLine === falseor a failed genuine/healthzprobe surfaces<OfflineBanner>. - Captive-portal
200 text/htmlhealth responses are rejected and keep the banner visible. - A genuine BFF health payload clears the banner on retry/online.
- Warmed Home, Explore, Activity, and Library shells stay navigable offline.
- Warmable prefixes include
/library,/domains,/d,/app, and/workspace. - Cached BFF and Nisaba offline records render when within their freshness windows.
- Uncached reads render
PwaOfflineFallbackwith retry, cached shell shortcuts, and recent cached records. - Recent content is persisted by
persistPwaRecentContentfor authenticated route visits. - Offline local edits and queueable writes show explicit local/sync-pending states; they do not silently fail.
Background sync#
-
queueBackgroundAction()postsOSHUN_QUEUE_ACTIONto the controlling service worker. -
sw.jspersists queued actions in IndexedDBoshun-sync-queue/pending-actions. - Chromium Background Sync dispatch replays queued actions in order.
- Successful replay posts
OSHUN_SYNC_COMPLETE, updates queue count, writes the live BFF, and drains IndexedDB. - Queue status posts
OSHUN_SYNC_QUEUE_STATUS, which drivespwa_sync_queuedtelemetry. - A pending queue count renders the update prompt's queued-write note so the user knows refresh will preserve held writes.
Relaunch target#
lib/pwa-runtime.ts
-
OSHUN_PWA_RELAUNCH_TARGET_KEY -
OSHUN_PWA_RELAUNCH_TARGET_COOKIE -
persistPwaRelaunchTarget -
readPwaRelaunchTarget -
resolveStandaloneLaunchTarget -
Authenticated route changes persist a sanitized relaunch target in localStorage and the
oshun-pwa-relaunchcookie. -
Signed-in standalone launches from
/?surface=pwarestore the saved target. -
Anonymous standalone launches route to
/welcomewith the saved redirect preserved. -
Expired or unauthenticated protected relaunches remain under the auth redirect contract.
-
Sign-out/session-clear removes the relaunch target and asks the service worker to clear member-owned caches and queued writes.
Offline shell warmup#
lib/offline-shell-routes.ts + lib/pwa-recent-content.ts
-
buildOfflineShellWarmRoutes()always includes Home, Explore, Activity, and Library. - Current and recent warmable routes are normalized with
sanitizeRedirectPath. -
?surface=pwais stripped before offline route normalization. -
warmOfflineShellRoutes()waits for service-worker readiness/controller when possible, then fetches normalized HTML routes withx-oshun-surface. - Warmup failures are best-effort and collapse to zero warmed routes instead of breaking the shell.
Standalone PWA detection#
-
(display-mode: standalone)is detected. - iOS
navigator.standalone === trueis detected. -
getBrowserSurface()returnspwaonly for standalone runtime, otherwiseweb. -
document.documentElement.dataset.oshunSurfacetracks the current browser surface. - Standalone launch restoration is session-safe and route-safe.
Push notifications#
sw.js, profile notification settings, and device-token BFF routes.
- Profile notification settings register this browser through the Push API
boundary and POST a stringified web-push subscription as a
web-pushtoken. - Unregister DELETE carries the same subscription token.
- Registration failure leaves the row in error state with retry available.
- Missing VAPID config reports unavailable and hides the register action.
-
sw.jspushcallsshowNotification()with title, body, icon, badge, tag, vibration, and action data. -
notificationclickcloses the notification, focuses an existing OSHUN window and postsOSHUN_NOTIFICATION_TAP, or cold-opens the target URL.
States#
- First visit in browser: public welcome shell, manifest, mobile metadata, and service-worker asset resolve.
- Browser install prompt available: custom install banner renders after
beforeinstallprompt. - Installed home-screen launch: standalone detection and relaunch target are honored.
- Update available in foreground:
PwaUpdatePromptis visible after consent. - Update deferred: route remains stable and prompt can re-surface next launch.
- Update applied: prompt clears, route/scroll are preserved where applicable, and stale caches are purged.
- Offline cached page: cached shell renders and banner is visible.
- Offline uncached page: service-worker fallback and hydrated
PwaOfflineFallbackrender. - Offline write attempt: action is queued or kept locally with user-visible pending copy.
- Push tap: existing window focus or cold-open route is exercised at the service-worker runtime layer.
Telemetry to verify#
-
pwa_update_appliedfor refresh-now and silent cold-start paths. -
pwa_sync_queuedfor service-worker queue status. - Install state is recorded in
oshun.pwa.install-state; install analytics names are not currently the E2E assertion boundary. - Offline banner/fallback assertions use visible DOM and service-worker marker contracts.
- Relaunch target assertions use URL restoration and auth redirect behavior.
Coverage#
apps/oshun/web/e2e/pwa-smoke.spec.ts- manifest/assets, SW asset, mobile metadata, download/Safari install copy, install prompt trigger, standalone relaunch, and reading-route update flow.
apps/oshun/web/e2e/pwa-install-update-offline.spec.ts- install dismissal cooldown, standard update deferral, update consent gate, offline recent-content fallback, and uncached deep-route fallback controls.
apps/oshun/web/e2e/pwa-lifecycle-deepening.spec.ts- apply handshake, multi-tab update, real cache purge, silent cold start, in-flight writes, queued-write update note, offline banner, and reduced-motion spinner.
apps/oshun/web/e2e/pwa-failure-modes.spec.ts- SW blocked fallback, update/offline co-mount, captive portal, stuck apply, and reload-loop guard.
apps/oshun/web/e2e/offline-shell-routes.spec.ts,offline-write-deepening.spec.ts, andoffline-background-sync.spec.ts- offline shell availability, real service-worker fallback, health probes, local edits, sync hints, and Background Sync replay.
apps/oshun/web/e2e/push-registration.spec.tsandapps/oshun/web/e2e/nyx-service-worker-notification.spec.ts- push device registration and service-worker push/click routing.
Known boundaries#
- OS-level install/uninstall and iOS home-screen confirmation are outside Playwright; tests assert the browser/PWA boundary and manual copy.
- Cross-browser storage-quota exhaustion is not deterministic enough for an E2E gate; cache eviction policies are covered through targeted runtime and unit/policy checks.
- Lighthouse PWA score thresholds are not currently a release gate; the manifest, install, SW, offline, relaunch, update, sync, and push contracts above are the automated signoff.
Cross-references#
- 01-app-shell.md - where
PwaBootstrapmounts. - 02-routing-layouts.md - SW navigation matching and auth redirect behavior.
- 05-notifications.md - notification center and push preferences.
- Journey: journeys/install-as-pwa.md
- Architecture:
V1/ARCHITECTURE.md - Feature spec:
V1/features.md