← All articles
Jun 12, 20264 min read

A Dead-Man's Switch for Mobile Apps: North's Reconnect Nudges

  • mobile
  • push notifications
  • Supabase
  • Expo
  • reliability
  • North
A Dead-Man's Switch for Mobile Apps: North's Reconnect Nudges

The worst failure mode in a family location app isn't a crash — it's silence. A teenager swipe-kills the app, iOS stops the background tasks, and from the family's perspective the dot just… freezes. The app can't report its own death, because it's dead.

This is a distributed-systems problem wearing a consumer-app costume, and the classic answer applies: a dead-man's switch — a signal that fires unless the system keeps proving it's alive. North implements it in three layers, because each one covers a hole in the others.

Option A: the server notices

A Supabase Edge Function runs on a pg_cron schedule and looks for members who've gone dark. The selection logic is a security-definer database function with rules that matter more than the query itself:

  • Location older than 4 hours → candidate for a reconnect push.
  • No more than one reconnect push per 6 hours — a nag becomes noise instantly, so the throttle timestamp (last_reconnect_at) lives in the database, not in app memory.
  • Only members of circles with at least one other active member. Waking a solo user helps nobody; this guard is equal parts battery courtesy and privacy hygiene.

Candidates get a visible, high-priority push: "open the app to resume sharing." Visible is the point — silent pushes don't work on force-stopped apps, but the notification itself still renders, putting a human back in the loop.

Option B: the phone notices, even with the app dead

The server-side check has up to four hours of latency. The client-side layer is faster and wonderfully low-tech: every successful location upload schedules a local notification two hours in the future — "you've stopped sharing, tap to reconnect."

Then the app spends its life cancelling that notification:

  • Every location push re-schedules it (same static identifier, so it replaces rather than stacks — the dead-man's timer just keeps sliding forward).
  • Foregrounding the app cancels it.
  • Pausing sharing deliberately cancels it — pausing is a choice, and nagging someone about a choice is hostile.

If the app dies, nothing cancels the pending notification, and the OS — which doesn't care that the app is dead — delivers it. The app's last act, scheduled while alive, is reporting its own death.

The platform asymmetry is why Option A still exists: on iOS, scheduled local notifications survive app termination. On Android, a force-stop discards them — so the client-side switch covers iOS swipe-kills with two-hour latency, and the server-side push backstops Android with four.

Option C: design the social layer honestly

The third layer isn't code so much as a stance. Circle nudges ("ask them to reconnect") are always on — the toggle to disable them doesn't exist in settings, and the server normalizes an off preference to default sound, honoring only an explicit silent. That sounds heavy-handed until you think it through: a connection app where you can unknowingly become unreachable while everyone believes you're reachable is broken at the contract level. You can pause sharing — loudly and deliberately. What you can't do is silently rot.

And while all three layers work on reconnection, the UI tells the truth in the meantime: stale members show "last seen 2 hr ago" instead of a confident live dot — the honest staleness rule.

The pattern, generalized

Anything that should run continuously on a phone needs an answer to "who notices when I die?" The shape that works:

  1. A watchdog that outlives you — scheduled local notifications survive your process (on iOS); cron checks on a server survive everything.
  2. Arm on success, not on failure. You can't schedule anything from inside a crash. Every healthy heartbeat sets the next alarm; death is detected by absence.
  3. Throttle in durable storage — reminder state must survive restarts, or you'll double-nag.
  4. Let deliberate absence opt out. A dead-man's switch that fires on intentional pause teaches users to ignore it — the alarm-fatigue death spiral.

None of this makes background execution reliable. It makes unreliability visible, which on mobile is the best contract on offer.

WRITTEN BY

Shahzaib Muhammad Akram

Senior Frontend EngineerCyberjaya, Malaysia