Push notifications stop after presence transitions to offline (iOS PWA backgrounded)

(#723) Bug Needs triage mobile notifications self-hosting

Summary

On a self-hosted instance, push notifications work correctly while the iOS PWA is foregrounded and the user's presence is online. However, once the app is backgrounded and presence automatically transitions to offline (the normal/expected behavior after a few seconds), push notifications stop arriving entirely even though the push subscription remains valid and registered.

Steps to reproduce

  1. Install Fluxer as a PWA on iOS via Safari's "Add to Home Screen."
  2. Open the app, grant notification permission, confirm status shows online.
  3. Send a test message from another account/device while the PWA is open to confirm the notification arrives.
  4. Background the PWA (swipe up / leave the app) without force-quitting it.
  5. Wait a few seconds for presence to visibly transition from online to offline (confirmed via another client).
  6. Send a message from a separate account/device.

Environment

  • Self-hosted instance (Docker Compose deployment)
  • Client: iOS Safari, installed as Home Screen PWA

2 comments

Sign in with Fluxer to comment and vote.
Comment by @chowe99
RexSystem 1 vote originally by @chowe99 on GitHub
Same bug on a different client — it's gateway-side, not platform-specific Confirming this on a self-hosted instance (Docker Compose, gateway ghcr.io/fluxerapp/fluxer-gateway:v1, Postgres backend) with a GrapheneOS + Brave (Chromium) PWA — i.e. not iOS-specific. Same symptom exactly: web push works while the app is foregrounded/online, and stops entirely once presence transitions to offline, even though the subscription stays valid and registered. I isolated where it breaks. Short version: the client/delivery side is fully healthy; the gateway never sends the push for an offline recipient.

Ruled out (all verified working)

  • A raw VAPID Web Push sent directly to the stored subscription (via the web-push CLI, bypassing the gateway entirely) is delivered to the device instantly, even with the browser fully closed. So the VAPID keypair, the stored subscription, FCM transport, and OS-level delivery are all fine.
  • The gateway's only push-related config is VAPID (which is all standard Web Push needs, and is the same path the manual send used successfully).

Gateway-internal evidence

Gathered via the release's bounded rpc against the live node (bin/fluxer_gateway.real rpc <Mod> <Fun> <Args>, with FLUXER_ERLANG_NODE_NAME / FLUXER_ERLANG_COOKIE / FLUXER_ERLANG_DIST_PORT set):
  • Recipient confirmed offline: presence_manager:lookup(UserId) → {error, not_found}.
  • push:get_cache_stats() → #{push_subscriptions_size => 0, user_guild_settings_size => 4, badge_counts_size => 0, blocked_ids_size => 0} — the guild-settings sync path populates, but push subscriptions never do.
  • push_token_cache:get(UserId) → undefined.
  • push_dispatcher:stats() → #{queued => 0, inflight => 0} — even immediately after sending both a DM and a guild @mention to the offline recipient. Nothing is ever enqueued.

Where it breaks

A message to an offline recipient never reaches push_subscriptions:fetch_and_send_subscriptions/8 — the dispatcher stays empty. So the break is upstream of any subscription fetch/send, in the push:handle_message_create/1 → push_eligibility:is_eligible_for_push/8 path. Note the module surface: there are sync_user_guild_settings / sync_user_blocked_ids paths (which work — hence user_guild_settings_size => 4), but there is no load/sync for push subscriptions, only invalidate_user_subscriptions. That's consistent with subscriptions being fetched on-demand at send time — which means the eligibility/dispatch step simply never fires for offline recipients, so the fetch never happens. Happy to run more targeted rpc probes on the live node if a maintainer can point at which eligibility branch or dispatch call to instrument. (Investigation and diagnosis performed with Claude Code.)
Comment by @cproudlock
RexSystem 1 vote edited originally by @cproudlock on GitHub
For anyone landing here via the native Android app (FCM) rather than a web-push PWA: there is a separate silent-drop bug on that path — the gateway sends an invalid group field in message.android.notification, so FCM rejects every message with HTTP 400 (and push_fcm swallows non-2xx with no log). Details + fix: #745. This issue (web-push / VAPID, offline) appears to be distinct — the web-push payload builder does not hit that field.
Deleted comment
Removed by moderator Rex: Removed a general status note that was posted on many GitHub threads. It no longer applies here.