DisplaySync

Remote control

The dashboard and mobile app can drive a claimed kiosk over the WebSocket: reload the page, restart the app, reboot the whole device, fetch logs, push an update. This page documents what each command does, how long it takes, and when to reach for it.

Where to find the commands

Every claimed sign has a Remote control card on its detail page:

  • Dashboard: Events → [event] → Signs → [sign] → Remote control
  • Mobile: Sign detail screen → row of action buttons

Both surfaces hit the same backend endpoints and emit the same WebSocket events. Pick whichever is closer to where you are.

The commands

Refresh

Reloads the assigned URL on the kiosk.

PropertyValue
Backend → kiosk eventsign:refresh
Wall impactBrief blank moment (under 1 second), then the page reloads
Expected duration2-3 seconds end to end
Sign state duringStays online
Use caseContent authors pushed an update; you want to be sure every wall sees it

Refresh is the safest, lightest command. It's the first thing to try when something looks wrong with what's on the wall.

Reboot app

Exits the sign app process. The watchdog from Crash recovery restarts it within ~60 seconds.

PropertyValue
Backend → kiosk eventsign:reboot
Wall impactThe wall briefly shows the desktop or splash, then the sign app reappears with the assigned URL
Expected duration30-60 seconds
Sign state duringBriefly Offline, then Online
Use caseThe sign app is misbehaving but Windows itself looks fine — first-line fix

Reboot app is non-destructive. The desktop sign re-reads its config from disk, replays the last assigned URL from the cache, and resumes.

Reboot device

Restarts the entire Windows machine. The kiosk goes through a full boot cycle: BIOS → Windows → auto-login → sign app → URL.

PropertyValue
Backend → kiosk eventsign:reboot_device
Wall impactFull BIOS POST screen, Windows boot logo, brief desktop, then the sign app
Expected duration60-120 seconds (depends on hardware)
Sign state duringOffline for ~90 s, then Online
Use caseSign app reboot didn't fix it. Driver state, network stack, or unidentified Windows-level weirdness.

The kiosk user has shutdown privilege granted in Kiosk configuration, so the reboot doesn't require an interactive logon prompt.

If reboot device returns "permission denied" in the dashboard, the shutdown privilege grant didn't apply — see Troubleshooting → Sign won't claim for re-applying.

Fetch logs

Pulls the kiosk's sign.log and any rotated archives, then surfaces them in the dashboard.

PropertyValue
Backend → kiosk eventsign:fetch_logs
Wall impactNone
Expected duration5-15 seconds depending on log size
Sign state duringStays online
Use caseTriaging a misbehaving sign before rebooting (rebooting destroys evidence)

The fetched bundle appears in the Logs card on the sign detail page. From there you can:

  • View the contents inline (Ctrl/Cmd-F to search)
  • Copy to clipboard
  • Download as a .zip
  • Attach to a support ticket

For what these logs contain, where they live on the kiosk, and how to read the rotation history, see Log locations.

Capture before you reboot

Always Fetch logs before a destructive command if you want to know why the sign was misbehaving. A reboot resets the symptom and clears the in-memory state that made the log informative.

Check for Updates

Triggers electron-updater on the kiosk to check for and (if available) install a new desktop sign version.

PropertyValue
Backend → kiosk eventsign:update
Wall impactBrief overlay showing update progress, then the sign app restarts
Expected duration30 s - 5 min (depends on download size and connection)
Sign state duringOnline → updating → online again
Use casePushing a desktop sign hotfix during a production freeze (rare; see Stability over features)

The kiosk pulls update binaries from updates.displaysync.live (Cloudflare R2). If you've blocked that destination at the venue firewall, the update fails — see Network requirements.

This is a deliberate, controlled rollout — there's no automatic update-during-event behavior. If your image is on v1.2.18 and you need to roll all walls to v1.2.19, you do it explicitly.

For release notes on what's in each version, see Desktop sign release notes.

Safety: token freshness and room scoping

Destructive commands — Reboot app, Reboot device, Check for Updates, and Fetch logs — carry a backend-minted command token (a 32-byte random hex string from crypto.randomBytes) and a timestamp. The kiosk validates:

  1. The token is present and non-empty
  2. The timestamp is within 30 seconds of the kiosk's clock

If either check fails, the command is rejected and the dashboard sees an error. This prevents replay attacks — you can't capture a "reboot" command and re-issue it later, because the timestamp falls outside the freshness window.

Cross-org leakage is prevented separately by Socket.IO room scoping: a command intended for a sign in org X is published to that org's room, and only kiosks subscribed to that room receive it. The kiosk doesn't authenticate the token's origin.

The lighter-weight command (Refresh) skips token validation. It's idempotent and side-effect-free enough that the WebSocket session itself is the trust boundary.

You don't have to do anything to opt into any of this — it's how every command moves between dashboard and kiosk by default.

If a command starts failing with timestamp errors, check the kiosk's clock. NTP misconfiguration on the venue network is the usual cause. See Network requirements → DHCP, DNS, NTP.

Offline command queueing

If a kiosk is offline when you issue a destructive command (reboot, fetch logs, update), the command queues in Redis with a 24-hour TTL and executes when the sign reconnects. The dashboard reflects this — the command card shows Queued instead of Acknowledged, with the issuing user's name attached.

When the sign comes back online, the backend drains the queue and emits each queued command in order. The token's 30-second window is checked against current time at execution, not enqueue time, so commands don't expire while waiting for reconnect.

For idle commands (refresh, settings update), no queueing — they fire only if the sign is online.

Audit trail

Every remote command is logged to the sign's Audit tab on its detail page:

  • Who issued the command (user email + role)
  • Which command
  • Timestamp
  • Result (acknowledged, failed, timed out)

Useful for "who rebooted lobby-east-03 at 3 PM during the keynote?" conversations. The audit log persists with the sign's history regardless of reclaim/relink cycles.

Failure modes

SymptomLikely causeFix
Command "queued" but never executesSign is offlineWait for reconnect, or troubleshoot the offline state
Reboot device "permission denied"Shutdown privilege not granted to kiosk userRe-run shutdown privilege grant
Refresh succeeds but content didn't changeBrowser-side cacheEnsure your content sets sane Cache-Control headers, or use a cache-busting query string
Fetch logs returns emptyLogs directory not writable, or log rotation cleared everythingCheck kiosk user's permissions on the logs dir; restart sign app to recreate
Update timing outVenue blocking updates.displaysync.liveAllowlist the host or pre-deploy the update via image rebuild

Monitoring mode

Monitoring mode lets you put a sign into a "telemetry-only" state — the wall is dark and silent, but the kiosk keeps reporting heartbeats and accepting remote commands. Reach for it when:

  • A sign is powered but not displaying — storage, transit, pre-event venue prep
  • A sign is showing the wrong content and you want telemetry to keep flowing while the actual display goes dark
  • You want a sign physically in place at a venue but visually off until showtime

What it does

AspectMonitoring onMonitoring off (default)
Kiosk windowHiddenVisible (the wall)
AudioMutedAudible
Renderer frame rateThrottledNormal (~60 fps)
WatchdogsPausedActive
HeartbeatContinues with mode: 'monitoring'Continues with mode: 'active'
Sign state in dashboardonline with a "Monitoring" badgeonline
monitoringMode in sign-config.jsontruefalse

The watchdog pause is the subtle bit: with the display hidden and frame rate throttled, the FrameWatchdog and PageWatchdog would otherwise interpret silence as a fault and reload-loop. They're paused to keep monitoring mode quiet.

How to turn it on

Two ways, equivalent in effect:

  • Dashboard: sign detail page → Enter Monitoring button. Dispatches the set_monitoring_mode command over WebSocket.
  • Local hotkey: Ctrl+Shift+M at the kiosk. See Hotkeys → Ctrl+Shift+M.

To turn it off: same routes — Exit Monitoring button or Ctrl+Shift+M again.

The mobile app has no monitoring-mode UI in V1. If you only have a phone, dashboard-via-mobile-browser works.

Persistence across reboot

monitoringMode: true is written to sign-config.json (see File locations). A sign that boots with this flag set comes up silently — no splash window, no audio, just the heartbeat. This is the gotcha: if an operator forgets to exit before next morning, the wall comes back from a power cycle still dark.

The visual indicator that monitoring is active is the Monitoring badge on the sign in the dashboard. If a sign shows online but no content is visible at the venue, the badge is your first check.

Pre-Sysprep gotcha

Don't bake monitoringMode: true into a captured image — every kiosk cloned from that image will cold-boot silent on first deployment. Almost always wrong; see Kiosk configuration → Monitoring mode at deployment time.

What's next

  • Monitoring — health cards on the sign detail page that contextualize when to reach for these commands
  • Troubleshooting — symptom-indexed playbook for when the commands themselves don't fix it
  • Notifications — alerts that fire when commands fail