Skip to content

Notification privacy and shared state

A failed attempt to turn notifications off keeps the last known setting and shows Could not check notification delivery. Try turning them off again when connected. Muting a computer cancels its pending alerts, just like muting a session; unmuting allows future alerts only.

Turn off -> confirmed removal -> off
         -> failed removal    -> keep setting + show problem

Temporary problems reading saved names do not prevent an alert appearing; it uses roamd as its title instead. A failed alert does not stop the other alerts in the same delivery. Retrying an old completion does not clear a newer request for input.

Dismissals are saved on this device before they are sent. If you are offline, Saved on this device. Will sync when connected. means the change is queued. It stays tied to your account and retries when the app reconnects, reopens, or receives another push. Until the server receives it, other devices can still show the alert. Clearing website storage removes pending local changes.

Dismiss -> save here -> reconnect -> server confirms -> other devices sync

Signing out, remotely revoking a browser, or letting its login expire stops new alerts to that browser. Locking the vault keeps notifications enabled. Browser delivery-address renewals are retried without asking for permission again. Slow delivery to one device does not hold up all the others.

Notifications distinguish Sent to push service, Reached device, Requesting display, Display requested, and Delivery failed. A display request is not proof that you saw a banner. The encrypted push also contains a generic fallback, so a failed background fetch need not hide the alert. Computer and session names are still added only on your device. Android delivery has been confirmed on a real device; the revised Apple path still needs a physical background-and-lock-screen test.

Session status is detected on the remote computer and passed through the hub encrypted. The remote computer alone decides whether a session is working, waiting for you or idle; your browser shows what it last said and never times that out. A faint hollow grey ring appears only while the computer is offline, or before this page has heard from it. Remembered session names stay visible while that first report is awaited; restoring them does not create notifications. Opening, closing or resizing a terminal on it doesn't change a session's light. A completion estimate requires an observed new work cycle, not an initial working label. Where an AI app's hooks are installed, the app itself says when it is working or waiting for you; otherwise the computer judges from the screen, which is not proof an agent has finished.

Your computer watches each session -> decides: working / waiting / idle
  -> sealed report -> hub -> your browser shows it, as it was said
Computer offline, or not heard from since the page loaded -> faint grey ring

Opening Notifications checks this browser's current setting again. While that check is pending, roamd does not offer to turn notifications on. An existing subscription with permission granted stays Notifications on, even if the server cannot be reached. Could not check notification delivery. and Check again report that separate problem. Status unavailable means the app could not determine the setting; it does not mean you turned it off. No permission question is repeated for an already-authorised browser.

Open Notifications -> check this browser's setting
                              |
                     check delivery separately

The notification panel shows recent activity as cards with the computer and session name, time, read state and delivery status. Unread cards have a blue outline and an Unread label. Mark all read sits above the list. The header keeps Close and this device's notification status visible while you scroll. Expand Notification settings for delivery tests and the optional agent setup steps. Turning notifications on is also offered there.

Notifications + device status + Close
-------------------------------------
Notification settings (expand for tests)
Recent activity             Mark all read
Alert cards                 (scroll)

The notifications line in a session's ⋯ menu (top right of the tab bar) turns notifications for that session on or off across your devices. Alerts start on. The crossed-out bell means that session is muted; turning it on still requires notifications enabled on each receiving device. A computer's Mute alerts setting takes priority. The choice is saved in your encrypted vault and follows the session when you close and reopen its tab. A newly named session has its own setting. Muting cancels pending alerts and prevents new ones being sent. Turning alerts back on only allows future events. Alerts already on your devices are taken away shortly after.

⋯ > notifications -> save choice -> all devices use that choice

Notifications need a small shared record so a read alert stays read on your other devices. The server records which computer raised an event, its type, when it happened, and its sent, displayed, read or handled state. It uses opaque identifiers for sessions and events: it does not receive their names or conversation text.

Computer: private session -> opaque event
                                 |
Server: event + shared state -----+
            |
     encrypted generic push
            |
Device: fetch event -> show short notification

The short message is Input required or Session may need you. A delivery test says Test notification. Tests and normal event pushes carry an encrypted generic fallback and opaque routing identifiers. They carry no terminal text or computer/session names. The signed-in app fetches current event state from roamd. If it cannot fetch the event, it can show the generic Session may need you The title uses computer and session names remembered on this device. Session names come from the encrypted machine status and stay on the device, separately from the public offline app files. No vault or terminal-login key is kept for notifications. Disabling notifications or locking/signing out removes these saved names. Unknown sessions use the computer name or roamd. A push whose event has already been read is taken away again at once.

The inbox keeps recent events for up to a month, subject to its storage limit. The delivery test reconnects this app when notification permission is still allowed but its delivery registration has gone missing. It does not ask again. The server reuses its push-service identity between alerts instead of replacing it for every message. This does not change what the push provider can see. A machine temporarily keeps undelivered events locally and retries them without creating new alerts each time. Opening another app copy shows the shared history; it does not send that history again as new alerts.

Activity detection works without agent hooks. A quiet screen is only an estimate that a session stopped; it can also mean the agent is thinking. Hooks optionally improve detection. Tapping or dismissing an alert consumes it across your account; another device will not receive it as new when it opens.

Dealt with anywhere clears it everywhere. When you tap, dismiss or mark an alert read, open its session, or answer it on the computer, roamd takes that alert away from your other devices too, even ones where roamd is closed. Apple devices must show something for every push, so there the alert's words change to Dealt with on another device for a moment, without a sound, and then it is gone. If an alert reaches a device after it was dealt with, it is not shown. A device that is off or offline is cleared when it next gets the message.

Dealt with on one device -> server -> "take it away" push -> other devices
                                                               |
                                 close it (Apple: quiet words in its place, then close)

See notification setup for optional hook approval, the device delivery test, and the limits of clearing system notifications.

If a sent test does not appear, Test on this device asks the device to show an alert directly. This separates local display from remote push delivery. The app can report that a request was accepted; only you can confirm seeing it.

Disable checks both server removal and browser unsubscription before reporting success. Failures retain the last known enabled state and expose a delivery check error. Computer mute and pending-event consumption share a transaction; events raised while muted start consumed. Completion insertion and input resolution are atomic, so a replayed completion cannot handle newer input.

New completion -> insert once + resolve prior input
Duplicate      -> no change to newer input

Optional worker client enumeration and cached-label reads are bounded and failures do not abort display. Individual display failures do not stop later events; successfully displayed events still receive receipts. Cached names fall back to the application name. These checks exercise worker handlers with controlled browser boundaries, not actual operating-system notification trays.

Dismissals and delivery-stage receipts use transactional browser storage, separately from shell assets and display labels. Only opaque account/event identifiers, delivery addresses, stages and timestamps are stored. Reads are retried for their original account, and are acknowledged explicitly. Local consumed markers remain through the event-retention period, preventing a late push or stale inbox response from replaying an acknowledged event. Retries run on worker wake, foreground/connectivity return, and background sync where supported. Browser storage eviction or clearing can still remove unsynchronised changes.

Local transaction -> scoped retry -> matching server acknowledgement
                         |
                 another account: retain, do not consume

Push addresses are bound to a browser login. Revocation removes that binding; expiry and organisation suspension also exclude it from delivery. Vault locking does not revoke the login. A replacement push address is registered before the old one is removed, preserving only that browser's scheduled events. Renewal is retryable across worker restarts and cannot override explicit disable. An already-sent push cannot be recalled from a sleeping device.

Notification requests and browser lookups have deadlines. DNS resolution and transport are bounded; recipient delivery has bounded concurrency, so a stalled recipient does not block all remaining devices. Scheduled sends recheck revocation and consumption before contacting the provider.

Registered Web Push encryption material lets both tests and normal event pushes carry a standards-based declarative fallback: generic title/body and opaque event/account scope, with no terminal text or machine/session names. It contains no vault or SSH decryption material. WebKit can present this fallback if the worker fails; other supported browsers use the worker. Provider acceptance, worker receipt, display request and display result are distinct stages. Receipts retry durably; none proves human attention. Physical Apple verification remains.

The remote watcher alone classifies each session; the browser never re-judges or ages a report. A session is unknown only on first sight (roamd starting). On pane identity, geometry, attachment, window or process changes the previous state is held while delayed redraws settle, so viewers joining, leaving or resizing never change it. Initial working snapshots cannot arm an estimated completion. Where the app's hooks are installed, their events are recorded per app process and take precedence over the screen: a prompt or finished tool means working, a permission or input request means waiting, and stop, interrupt or session end means idle. A hook's waiting holds until the screen has moved on for more than one look; a hook's working stops being believed once neither the screen nor a newer hook has changed for a while. Busy reports are renewed promptly. The browser shows unknown only while the machine is offline or before a report has been confirmed since the page loaded; a failed status fetch keeps the last report. The list retains remembered names with a hollow grey ring; cached reports never establish a new work cycle or generate a notification.

look -> first sight?           -> unknown
     -> layout/viewer changed? -> keep previous state while it settles
     -> hook for this process? -> waiting | working (while fresh) | screen
     -> screen                 -> prompt: waiting; moved lately: working; else idle
report (sealed, signed) -> hub -> browser: online and confirmed? shown : hollow ring

The enabled state comes from browser permission and the local push subscription. Subscription synchronisation failure is a separate state and cannot overwrite an existing enabled setting. Unknown and in-progress reads do not expose an enable action. The inbox refreshes on open and when it returns to the foreground; concurrent refreshes share one request, and superseded results are ignored. Explicit disable still removes the subscription. An enabled browser state is not proof of provider acceptance or an operating-system banner.

Permission + local subscription -> enabled state
Server registration check      -> delivery-check state

The inbox separates a fixed header from a scrollable body. It retains the existing account-wide event, read and delivery data, with explicit state text alongside the visual unread treatment. Settings and hook approval instructions are collapsed disclosures; opening them does not change permissions or consume alerts. Mark all read is disabled when no unread, unhandled events remain. Keyboard Escape, Close or a backdrop click dismisses the dialog.

Open inbox -> event cards -> open session / mark read
                  |
             settings disclosure -> tests / optional setup

Each session preference is a revision-bound encrypted vault item. The same transaction saves an unencrypted delivery rule containing only the owner's device identifier, opaque session token and muted flag. This lets the hub enforce the choice even when browsers are closed. The hub learns that a session is muted, but receives no session names or terminal contents. Revision conflicts cannot partially save the preference or delivery rule. Other unlocked clients refresh through the existing vault change stream. Muting consumes pending notices; events recorded while muted are already read and cannot be replayed on unmute. Existing computer-wide mutes still apply. Notifications already displayed are cleared by the clear push described below.

Bell -> sealed vault item + opaque delivery rule (one save)
                                  |
                      muted: no new push queue

Session identifiers are derived from a private machine-local random secret and the session name. The mapping travels to approved browsers in the existing signed, encrypted status report. The hub holds event kinds and opaque correlation tokens, device ownership, timestamps, presence leases and delivery/read/handled state. This reveals event timing and type, but not terminal or conversation contents.

Machine secret + session -> opaque session token
        |                           |
encrypted status to owner      event to hub
        |                           |
browser resolves session       shared state + encrypted push
        +---------------------------+

The machine observes busy-to-idle and recognised waiting transitions locally. Only advisory event kinds and opaque identifiers leave the machine, not captured screen text. Initial states do not generate historical alerts. Exact hook events take priority when observed for that running agent process.

The machine outbox contains only routing metadata and, for a pending completion, the local transcript path and turn identifier needed to confirm it. Hook input text, tool arguments and answers are not copied into the hub event. Duplicate event IDs do not reset read state. Browser endpoints and event APIs are scoped to the signed-in owner; machine event submission requires that machine's authentication.

Clearing: when an event becomes read or handled (inbox, system notification click or dismissal, presence on its session, a handled or resumed hook event, a completion resolving input, or a mute), the hub sends an encrypted clear push, {"clear":{"id","scope"}}, to every subscription it sent that alert to (accepted, received, display requested or displayed) that has payload keys. The browser that did the reading sends its own push endpoint with the read or presence call and is skipped. Each subscription is cleared once per event; failures are retried a bounded number of times by the delivery retry loop, and only recent events are cleared. The service worker closes the notification tagged event-<id>. Apple browsers and Firefox count a push that shows nothing against the site, so there it first replaces that notification in place (same tag, silent) with Dealt with on another device and then closes it; if closing fails, those words remain. The worker also records the event as read locally, so an alert that arrives after its clear is not displayed. Clear pushes carry no computer or session names.

read / handled -> clears_due(sent, not yet cleared, not the reader)
     -> encrypted {"clear":{id,scope}} -> service worker
     -> Apple/Firefox: show(tag=event-id, silent) -> close(tag)
     -> others: close(tag) if shown

Empty push POST requests explicitly declare a zero content length. The encrypted delivery test instead declares the ciphertext length. Missing length was rejected by Google with a Length Required response, even though its encrypted test succeeded. Push uses VAPID-signed encrypted fallback delivery when registration keys are available. VAPID authorisation is reused separately for each push-service origin and subject, and renewed before expiry. The signing cache is bounded, in memory only, and holds no account or session data. A receiving service worker fetches only events scheduled for its own subscription, displays event-type words with locally remembered display names, and records a display receipt. Display names are learned from signed encrypted owner status while unlocked and notifications are enabled. They are cached only on that device and removed on lock, sign-out or disabling notifications. They are separate from the public offline cache and contain no SSH or vault key. A later subscriber receives no historical push queue. An acceptance receipt and a display receipt are not evidence of human attention. An explicit delivery test first repairs an already-authorised subscription and registers it for the signed-in account. A failed server registration is reported separately from the enabled browser setting and does not remove its existing subscription.

Presence expires when not renewed. Read changes synchronise with active clients; clicking or dismissing an alert marks it read for every subscription. Pending fetches and delivery retries exclude consumed alerts; opening a different device never schedules past alerts again. Clearing notifications already delivered to other operating systems is best-effort. There is no silent clearing push: Apple requires a visible notification for incoming push. An already-sent push can race a read change and produce a neutral activity-update notification on WebKit; other browsers suppress a successfully fetched empty inbox. Requests are bounded and push callbacks are serialised so overlapping pushes do not race their display receipts.

Event history has bounded retention and size. Delivery attempts have bounded retries. Configuration and test coverage are described in the notification manual.

The sent test checks the active worker and uses standard Web Push encryption for the same declarative fallback as normal events. Registration encryption material is retained for future event delivery. The payload contains only generic notification words and opaque routing identifiers; private names stay local.

The direct device test uses the existing permission and active worker's display API. It does not send a push or create a delivery subscription. Display requests can succeed without a visible banner because presentation is controlled by the OS.

Local test -> display request -> OS presentation
Push test  -> push provider -> worker -> display request