Skip to content

Background Notifications (Android)

Hive's in-app notifications only fire while the app is open and connected. On Android, background notifications close that gap: the app briefly wakes on a timer, connects to your cluster, and raises a native notification for anything that happened while it was closed or backgrounded - a finished agent, a scheduled task that succeeded or failed, or a session waiting on you.

This is opt-in and battery-friendly. Hive does not hold a constant connection; it connects for a moment roughly every 15 minutes, checks for missed alerts, and disconnects.

Turning it on

Open Settings -> Notifications on the Android app. Under Background notifications (this device), enable Check for missed notifications while the app is closed.

The toggle only appears on Android, where the native scheduling support exists. On desktop and in the web client the app relies on the normal in-app / OS alerts while it is running (see Agent Waiting Marker), so there is no equivalent toggle there.

What you get notified about

While the app is closed, a background check surfaces the same events the app would have alerted you to live:

  • An agent finished - a session went idle after doing work.
  • A scheduled task succeeded or failed - honouring the per-task Notify on success / Notify on failure opt-ins (see Tasks). A task with neither flag set stays silent.
  • A session is waiting for you - the same "needs input" signal that drives the ? marker (see Agent Waiting Marker).

Each notification carries a title and body and, where relevant, targets the session it relates to so a tap can open it.

Timing: expect up to ~15 minutes

The ~15 minute cadence is a floor imposed by Android, not a setting. Hive uses an inexact repeating alarm, which Android is free to delay and batch, especially under Doze (the OS's idle power-saving mode). So a missed alert can arrive up to about 15 minutes late, sometimes more if the device has been idle for a long stretch.

This is a deliberate trade-off: a constant push connection would drain the battery, while a periodic wake keeps power use negligible. If you need to-the-second alerts, keep the app open in the foreground, where in-app notifications fire immediately.

Cluster-wide coverage

The background check polls a single node - whichever one your app's active profile points at. That node aggregates missed notifications from the whole cluster before answering, so a task failure or an idle agent on any node still reaches your phone through the one node you poll. You do not need to poll every node individually.

How it works

The feature has two halves: a durable notification log the daemon keeps, and a native Android poller that reads it.

Daemon: durable notification log

Every alert the daemon fires is appended to a durable log (a SQLite notifications table, added in schema migration v28) so a client that was offline when the alert fired can still see it on its next poll. The log is bounded: rows older than 7 days, or beyond the newest 500, are pruned after each insert.

Each record carries a stable id (clients dedupe on it across polls), the firing time in Unix milliseconds, a machine-readable kind, an optional session_id (tap-to-open target), the firing node_id, and a title / body.

The kind is one of:

kindMeaning
agent_idleAn agent went idle / a session finished a turn of work
attentionA session is waiting for the user (the ? state)
task_successA scheduled task completed with exit 0
task_failureA scheduled task failed (non-zero exit or timeout)

HTTP endpoint

The Android poller reads the log over a small authenticated HTTP endpoint:

GET /notifications?since_ms=<unix_ms>
  • Authenticate with the daemon token, either as Authorization: Bearer <token> or as a ?token=<token> query parameter. The cluster token is also accepted.
  • The response is { "now_ms": <server_time>, "items": [ ...NotificationRecord ] }, where items are the notifications fired after since_ms, aggregated across the cluster.
  • The client persists the returned now_ms as its next since_ms cursor, so each poll only returns what is new since the previous one.

The same aggregation is available over the WebSocket protocol via ClientMessage::GetNotifications { since_ms } -> ServerMessage::Notifications { items, now_ms }, which the polled node uses to fan out to its peers (see Protocol).

Android native side

The native scheduling is generated into the Android project at build time by crates/hive-app/scripts/patch-android-keyboard.mjs. It wires up:

  • An AlarmManager inexact-repeating alarm that wakes the app roughly every 15 minutes.
  • A BroadcastReceiver (HiveBackgroundNotifyReceiver) that runs on each alarm: it does the GET /notifications request with the stored cursor and posts a native notification for each new item.
  • A boot receiver that re-arms the alarm after the device reboots, so the feature keeps working across restarts without reopening the app.

Configuration (enabled flag, base URL, token, and the since_ms cursor) lives in Android SharedPreferences, written when you toggle the setting and read by the receiver on each wake.

Hive - remote AI coding agents over WebSocket.