DisplaySync

Sign states

A sign is in exactly one of five states at any moment. This page is the lookup reference — colors, meanings, transition triggers, and the heartbeat thresholds that drive automated transitions.

For narrative explanations of how monitoring works, see Monitoring & health.

State table

StateHexMeaningLifetime
online#22c55eConnected and reporting heartbeatsSteady-state for a healthy claimed sign
offline#6b7280No heartbeat received within the timeout windowTransient — recovers when heartbeats resume
error#ef4444Backend's view that the kiosk's content URL is unreachable, inferred from sign:content_unreachable events emitted by the kiosk's reachability monitorTransient — clears when the kiosk emits sign:content_recovered
maintenance#f59e0bManually placed in maintenance modeOperator-controlled; cleared on next reboot
unlinked#eab308Sign DB record exists but no physical device is linkedUntil the record is linked or deleted

Color semantics:

  • Green (online): everything working
  • Gray (offline): absence of signal — not necessarily broken, just not heard from
  • Red (error): active failure requiring attention
  • Amber (maintenance): intentionally out of service
  • Yellow (unlinked): needs action, but not broken

Transition rules

                       ┌──────────────────────┐
                       │      unlinked        │
                       │ (DB record, no       │
                       │   device claimed)    │
                       └──────┬───────────────┘
                              │ link/claim
                              ↓
       ┌──────────────────────────────────────────┐
       │                  online                  │
       └─┬──────────┬──────────┬─────────┬────────┘
         │          │          │         │
         │          │          │         │ Ctrl+Shift+Q on kiosk,
         │          │          │         │ or .maintenance sentinel
         │          │          │         ↓
         │          │          │      ┌─────────────────┐
         │          │          │      │  maintenance    │
         │          │          │      └────────┬────────┘
         │          │          │               │ reboot, sentinel removed
         │          │          │               ↓
         │          │          │             online
         │          │          │
         │          │ kiosk emits sign:content_unreachable
         │          ↓
         │       ┌──────────┐
         │       │  error   │
         │       └────┬─────┘
         │            │ kiosk emits sign:content_recovered
         │            ↓
         │         online
         │
         │ no heartbeat for 15s+ (3 missed beats)
         ↓
      ┌──────────┐
      │ offline  │
      └────┬─────┘
           │ heartbeat resumes
           ↓
        online

A sign can also transition to unlinked at any time when an admin unregisters or deletes the device from the dashboard.

Online ↔ offline thresholds

PropertyValue
Heartbeat intervalEvery 5 seconds
Heartbeat TTL in Redis30 seconds
Background sweep cadenceEvery 10 seconds
Offline transitionAfter 15 seconds with no heartbeat (3 missed beats)
Online transitionFirst heartbeat received
Worst-case time-to-offline~25 seconds (15 s for TTL expiry + up to 10 s sweep delay)
Notification deferral60 seconds after offline transition

The 60 s notification deferral means a sign that goes offline and comes back within ~75 s total never notifies anyone. The dashboard still reflects the brief offline blip in the audit log.

Cadence is fixed. The 5-second emit interval and the 60-second notification deferral are product defaults; they aren't customer-tunable per-kiosk. Backend ping/pong cadences are deployment-side settings. If you're investigating disconnect timing on a specific kiosk, the lever is venue-side network hygiene (firewall keepalive, AP stability), not these cadences.

How each state is determined

The kiosk's heartbeat carries a fixed status: 'online'. Other states are derived backend-side from auxiliary signals:

Backend has heartbeats?Other signalResulting dashboard state
Yes(default)online
No (last >15 s)offline
Yessign:content_unreachable event receivederror
Yes.maintenance sentinel detected on the kiosk, or operator-setmaintenance
No device is linked to the Sign recordunlinked

The kiosk cannot self-report offline, error, or maintenance in its heartbeat — those are backend-derived conclusions.

Orthogonal mode field

In addition to the five states above, heartbeats carry an orthogonal mode field:

Heartbeat fieldValuesMeaning
mode'active' (default) | 'monitoring'Whether the sign is in monitoring mode — display hidden, audio muted, watchdogs paused, telemetry continues

This isn't a new state — a sign can be online + mode monitoring (running normally but display intentionally dark) or online + mode active (running and displaying). The dashboard surfaces mode: 'monitoring' as a Monitoring badge alongside the state color.

States that trigger notifications

Per the Notifications defaults:

TransitionDefault channels (if event-subscribed)
online → offlineIn-app + push (+ email after 60 s defer)
offline → onlineIn-app + push
online → errorIn-app + push + email
error → onlineIn-app + push
→ unlinkedIn-app + push (event subscribers)

Notification types

TypeTriggerChannelsFilter keyNotes
sign_offlineStatus flips to offline after 60s deferin-app, email, pushsignOffline60s defer prevents flap-spam
sign_errorStatus flips to errorin-app, email, pushsignErrorNo defer — error is rare and should fire fast
sign_onlineStatus recovers from offline or error AFTER notification was deliveredin-app, email, pushsignOnlineOff by default; opt-in per subscription
content_unreachableContent URL fails reachability check, 60s defer, delivered-key trackingin-app, emailsignOfflineBundled under the signOffline filter
content_recoveredContent URL recovers AFTER unreachable was deliveredin-app, emailsignOfflineSame filter as unreachable; only fires if delivered-key was set
network_failoverActive interface switches (ethernet ↔ wifi)in-appsignOfflineSingle-channel; not email/push

The filter key is the dashboard's per-event subscription toggle. Subscribing to signOffline covers sign_offline, content_unreachable, content_recovered, and network_failover — the four "offline-flavored" notifications.

State persistence

Sign state is not persistent across DisplaySync deploys — Redis holds the latest heartbeat with a 30 s TTL. If the backend restarts or Redis flushes, signs all transition to offline briefly until each kiosk emits its next heartbeat (within 5 s).

The sign's historical state transitions live in the audit log indefinitely, scoped per-event.

Source of truth

Sign-state colors and names are part of the product surface — the dashboard, mobile app, and these docs all derive from the same canonical list. If you see a state name or color that disagrees with what your dashboard shows, the dashboard is authoritative.

Pre-claim lifecycle

Before a sign reaches one of the five states above, it goes through a brief pre-claim lifecycle managed in Redis (not the SQL Sign records table).

StageStorageLifetimeTriggered by
PendingRedis ws:pending-sign:<shortCode>24h TTLKiosk emits sign:register on first WebSocket connect
Handshake in-flightRedis linkRequest:<requestId>30s TTLOperator submits claim; backend awaits kiosk link_ack
ClaimedSQL Sign record + Redis heartbeat keyPersistentKiosk emits link_ack success; backend commits
Unclaimed(nothing — record cleared)Operator unregisters; kiosk emits sign:unregistered

The handshake-in-flight stage is part of the acknowledged claim flow when LINK_HANDSHAKE_ENABLED is enabled. See Claiming signs → How a claim is committed.

See also