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.
| Property | Value |
|---|---|
| Backend → kiosk event | sign:refresh |
| Wall impact | Brief blank moment (under 1 second), then the page reloads |
| Expected duration | 2-3 seconds end to end |
| Sign state during | Stays online |
| Use case | Content 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.
| Property | Value |
|---|---|
| Backend → kiosk event | sign:reboot |
| Wall impact | The wall briefly shows the desktop or splash, then the sign app reappears with the assigned URL |
| Expected duration | 30-60 seconds |
| Sign state during | Briefly Offline, then Online |
| Use case | The 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.
| Property | Value |
|---|---|
| Backend → kiosk event | sign:reboot_device |
| Wall impact | Full BIOS POST screen, Windows boot logo, brief desktop, then the sign app |
| Expected duration | 60-120 seconds (depends on hardware) |
| Sign state during | Offline for ~90 s, then Online |
| Use case | Sign 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.
| Property | Value |
|---|---|
| Backend → kiosk event | sign:fetch_logs |
| Wall impact | None |
| Expected duration | 5-15 seconds depending on log size |
| Sign state during | Stays online |
| Use case | Triaging 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.
| Property | Value |
|---|---|
| Backend → kiosk event | sign:update |
| Wall impact | Brief overlay showing update progress, then the sign app restarts |
| Expected duration | 30 s - 5 min (depends on download size and connection) |
| Sign state during | Online → updating → online again |
| Use case | Pushing 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:
- The token is present and non-empty
- 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
| Symptom | Likely cause | Fix |
|---|---|---|
| Command "queued" but never executes | Sign is offline | Wait for reconnect, or troubleshoot the offline state |
| Reboot device "permission denied" | Shutdown privilege not granted to kiosk user | Re-run shutdown privilege grant |
| Refresh succeeds but content didn't change | Browser-side cache | Ensure your content sets sane Cache-Control headers, or use a cache-busting query string |
| Fetch logs returns empty | Logs directory not writable, or log rotation cleared everything | Check kiosk user's permissions on the logs dir; restart sign app to recreate |
| Update timing out | Venue blocking updates.displaysync.live | Allowlist 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
| Aspect | Monitoring on | Monitoring off (default) |
|---|---|---|
| Kiosk window | Hidden | Visible (the wall) |
| Audio | Muted | Audible |
| Renderer frame rate | Throttled | Normal (~60 fps) |
| Watchdogs | Paused | Active |
| Heartbeat | Continues with mode: 'monitoring' | Continues with mode: 'active' |
| Sign state in dashboard | online with a "Monitoring" badge | online |
monitoringMode in sign-config.json | true | false |
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_modecommand over WebSocket. - Local hotkey:
Ctrl+Shift+Mat 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