technical:features:32_realtime_updates_and_push_notifications
Table of Contents
32 — Realtime updates and push notifications
Technical reference. For task-based help, see the corresponding user guide.
Documentation status: reviewed against implementation · Reviewed: 2026-07-21 · Primary users: family and coach · Surfaces: mobile web, coach portal, native wrapper, Supabase Realtime and platform API
Purpose
Realtime makes an open app refresh sooner; push asks a backgrounded native app to reopen at a useful route. Both are delivery hints. Trusted records and authorization always come from a fresh platform REST request.
Capability map
| Capability | Transport | Payload rule |
|---|---|---|
| Presence | Private Supabase Presence channel | Opaque signed presence key/token and surface only. |
| Resource refresh | Private user topic user:<Supabase auth UUID>:events | Small resource.changed invalidation with kind and optional ID; never the record. |
| Activity push | FCM/APNs through registered native device | Generic title/body plus activity ID and /coach/notifications route. |
| Chat push | FCM/APNs through registered native device | Generic title/body plus conversation ID and recipient role. |
Realtime presence
- Client gate
VITE_REALTIME_PRESENCE_ENABLEDand server gateREALTIME_PRESENCE_ENABLEDmust be explicitly1/true(server uses1). - Authenticated clients request
GET /realtime/presence/v2/credential; v1 remains compatibility-only. - Credentials are opaque,
Cache-Control: no-storeand expire between 60 seconds and 24 hours (24 hours by default). V2 binds the ticket to the current auth session. - The client tracks only while the document is visible, refreshes before expiry, disconnects when hidden and reconnects with bounded exponential backoff/jitter.
- Owner view-as sessions cannot issue presence, so impersonation never creates a second online identity.
- Only super-admin can resolve opaque presence entries to user identity through
POST /admin/realtime/presence/v2/resolve(maximum 500 entries). Family/coach clients cannot resolve the directory.
Presence is operational “online” state only; it never grants access to a tenant, conversation or family.
Realtime resource refresh
- Client
VITE_REALTIME_BROADCAST_ENABLEDand serverREALTIME_BROADCAST_ENABLEDmust be enabled; Supabase server configuration is also required. - The server publishes a schema-versioned
resource.changedhint to the affected authenticated user's private topic. - Kinds cover resources such as activities, active timers, family/access state, activity-notification state and chat conversations/messages/unread state.
- Mobile and portal validate the exact schema/identifier format, coalesce equivalent hints (normally for 250 ms), then call the appropriate REST loader.
- Reconnect triggers a local resync for interested resources because broadcast is not a durable event log.
- Window focus/visibility refresh and feature-specific polling remain fallbacks when Realtime is disabled, disconnected or misses a hint.
- While a user edits a record, a matching invalidation can surface an external-update conflict rather than overwriting the draft silently.
Push-device lifecycle
Registration
- Push is native iOS/Android only and is disabled for owner view-as.
- Native supplies
fcmToken, stabledeviceId, platform, permission status, wrapper version and app/web version toPOST /push/devices. - Registration upserts the current actor/device and disables an older active token for the same device when the token rotates.
- Family bootstrap may request/register permission. Coach bootstrap checks status without prompting; an explicit activity/chat push toggle requests permission.
- If native permission/token is not immediately available, the app can retry without blocking sign-in.
Disable/logout
- Turning a product preference off suppresses that push type but need not invalidate the whole device.
- Logout first asks native to unregister and calls
DELETE /push/devices/currentby token/device ID, then signs out. Cleanup is best-effort, and every sender also rechecks enabled state before delivery.
Coach activity push
- UI gate
VITE_COACH_ACTIVITY_PUSH_ENABLEDand backendCOACH_ACTIVITY_PUSH_ENABLEDmust permit the feature (both default off in production unless explicitly enabled). - The native wrapper must be SemVer
4.6.0or newer; older versions see update-required copy instead of a usable toggle. - Only a family-created activity is queued. At enqueue and again before send, the platform checks active assignment/current record access, active subscription/support boundary, global activity alerts, per-family mute, push preference, enabled device, permission
granted/provisionaland compatible wrapper. - Copy is deliberately generic: New family activity — Open Littles Baby Tracker to review. Data carries only schema/source, activity ID and
/coach/notifications.
Coach–family chat push
- Chat itself, chat push and organization rollout/canary must all be enabled on server; mobile also requires
VITE_COACH_FAMILY_CHAT_PUSH_ENABLED. - Minimum wrapper is
4.7.0. - Delivery rechecks active coach/family relationship, organization chat rollout, open moderation state, both participants' chat settings, recipient unread/push preference and an enabled device with granted permission.
- Copy is deliberately generic: New message — Open Littles Baby Tracker to read it. It contains no message text, baby name or family name.
- Wrapper validates source/schema and routes by recipient role to
/family/messages/<conversation>or/coach/messages/<conversation>.
User-visible states
| State | Behaviour |
|---|---|
| Realtime disabled/offline | REST screens still work; focus/poll/manual retry provides freshness. |
| Reconnecting | Existing content remains; reconnect resyncs interested resources. |
| Permission not determined | Explicit coach toggle can invoke the OS prompt. |
| Denied/unavailable | Toggle explains notifications are unavailable and does not pretend delivery is active. |
| Wrapper too old | Activity/chat surface asks for app update with the exact minimum. |
| Preference save/register failure | Optimistic toggle rolls back or shows retryable error. |
| Logout cleanup failure | Sign-out proceeds; server-side access/device checks protect later sends. |
Platform API contract
| Method and route | Purpose |
|---|---|
GET /realtime/presence/v2/credential | Issue the current real auth session's opaque Presence ticket. |
POST /admin/realtime/presence/v2/resolve | Super-admin-only identity resolution. |
POST /push/devices | Register/upsert the actor's native device and permission/version metadata. |
DELETE /push/devices/current | Disable the matching current device token/ID for the actor. |
| Feature preference routes | Persist global/per-family activity and participant chat push choices. |
Security and privacy
- Private Realtime channels use the genuine Supabase JWT; application API bearer tokens are not copied into broadcast payloads.
- A resource hint has no trusted business data. Every refresh is authorized by the platform API at read time.
- Presence identity resolution is server-ticket validated and super-admin-only.
- Push payloads are generic because lock-screen text may be visible to other people.
- Sender workers re-evaluate access, preferences, device status and wrapper compatibility at delivery—not only when queued.
Realtime is not a consistency or authorization layer, and push is not proof that a user still has access. New resource kinds and push data fields require privacy review, strict schema validation and an authorized REST refresh/deep-link destination.
Acceptance checklist
- [ ] Presence/broadcast stay inert unless both relevant client/server configuration is enabled.
- [ ] View-as cannot publish genuine presence; only super-admin resolves identities.
- [ ] Broadcast events contain only validated invalidation metadata and trigger authorized REST refresh.
- [ ] Reconnect/focus paths recover freshness after missed events.
- [ ] Token rotation and logout disable stale device registrations best-effort.
- [ ] Activity push enforces 4.6.0, family-created source and every preference/access gate.
- [ ] Chat push enforces 4.7.0, chat rollout/relationship/moderation and participant preferences.
- [ ] Lock-screen copy contains no care record, message text, baby or family identity.
Source evidence
- Mobile web:
mobile-web/src/realtime/,mobile-web/src/App.tsx,mobile-web/src/components/CoachActivityNotificationsPanel.tsx, chat push settings andmobile-web/src/webview/native-messages.ts - Coach portal:
admin-web/src/realtime/,admin-web/src/App.tsx, chat/activity notification integrations - Platform:
platform/packages/shared/src/realtime.ts,push-devices.ts,coach-activity-notifications.ts,coach-family-chat.ts;platform/apps/api/src/routes/realtime-presence-routes.ts,push-device-routes.ts; realtime and push services - Native wrapper:
mobile-wrapper/src/native/chatPushNotifications.tsand notification/bridge handlers
