DisplaySync

Setup script reference

setup.ps1 is the heart of the USB recovery drive. It runs after setup.bat invokes it, takes the kiosk from "Windows is up and DisplaySync is installed" to "DisplaySync running, on the tailnet, ready to be claimed."

This page documents what the script does step by step, what it reads from config.json, what it writes (and where), and how to debug failures. Useful for AV companies who want to understand what they're handing to a contractor before they hand it over.

At a glance

setup.ps1 is idempotent — re-running it on an already-configured machine is safe. Each step checks for existing state and either skips or refreshes.

It runs in three broad phases:

  1. Discover — find the DisplaySync install, find the Tailscale installer, parse config.json.
  2. Configure — install Tailscale (if needed), join the tailnet, optionally wipe DisplaySync config, register the watchdog scheduled task.
  3. Launch — start DisplaySync, log a result summary.

The script self-elevates: if it's not running as admin, it relaunches itself via UAC and continues. The user clicks "Yes" once when UAC prompts.

config.json schema

{
  "tailscaleAuthKey": "tskey-auth-XXXXXXXXXXXXXXXX",
  "environment": "production",
  "freshConfig": false
}
FieldRequiredTypeDefaultPurpose
tailscaleAuthKeystringPre-authorized Tailscale auth key. The setup fails fast if this is missing or still set to the placeholder.
environmentstring"production"Logged in setup.log for diagnostics. Doesn't affect script behavior.
freshConfigbooleanfalseIf true, wipes DisplaySync's local config (sign identity, cached data) before launch. Use for true recovery flows.

The environment field is informational. The script doesn't switch between staging and production based on it — that's controlled by the .env already baked into the DisplaySync installer.

Step-by-step

Phase 1 — Discover

  1. Self-elevate. If not admin, relaunch via UAC and exit the non-elevated instance.
  2. Locate DisplaySync. Searches:
    • C:\Program Files\DisplaySync Sign\DisplaySync Sign.exe
    • C:\Program Files (x86)\DisplaySync Sign\DisplaySync Sign.exe
    • %LOCALAPPDATA%\Programs\DisplaySync Sign\DisplaySync Sign.exe (per-user install)
    • The original-user-profile Programs directory (if self-elevation crossed user contexts)
    • Every C:\Users\*\AppData\Local\Programs\DisplaySync Sign\ candidate
  3. Locate Tailscale installer. Looks for tailscale-setup.exe in the same directory as setup.ps1 (the USB drive root).
  4. Parse config.json. Reads from the same directory. Fails fast if the auth key is missing or unchanged from the placeholder.

If any of these fail, the script logs a clear error and exits — no destructive changes have happened yet.

Phase 2 — Configure

  1. Install Tailscale (if not already present). Runs tailscale-setup.exe silently with NSIS flags. Idempotent — skips if Tailscale is already installed and up-to-date enough.
  2. Join the tailnet. Runs:
    tailscale up --authkey <key> --hostname sign-<serial> --unattended
    
    The hostname is derived from the device's BIOS serial, so the kiosk shows up in the Tailscale admin console with a stable, identifiable name.
  3. Optionally wipe DisplaySync config. If freshConfig: true, deletes:
    • C:\ProgramData\DisplaySync\ (machine-wide config and .maintenance sentinel — see File locations)
    • %APPDATA%\desktop-sign\ for every user (Electron Store config)
  4. Register the watchdog scheduled task. Creates a task named DisplaySyncWatchdog that runs every 60 seconds and:
    • Checks for the .maintenance sentinel — skips if present
    • Checks if DisplaySync Sign.exe is running — relaunches if not

Phase 3 — Launch

  1. Start DisplaySync. Launches DisplaySync Sign.exe if not already running.
  2. Verify Tailscale connectivity. Runs tailscale status and tailscale ip -4 to confirm join. Logs the IP.
  3. Print summary. Each step gets a PASS or FAIL line; the final line is a clear overall result.

Output

While running, the script writes both to the console and to setup.log next to setup.ps1 on the USB drive. The log persists even after the script exits — your primary diagnostic artifact.

A successful run looks like:

=== DisplaySync Setup ===
[PASS] Found DisplaySync at C:\Users\Tech\AppData\Local\Programs\DisplaySync Sign\DisplaySync Sign.exe
[PASS] Found Tailscale installer
[PASS] Read config.json (environment: production)
[PASS] Tailscale already installed (1.62.0)
[PASS] Tailscale joined as sign-ABCD1234EFGH
[PASS] Tailscale IP: 100.81.45.213
[INFO] freshConfig=false, preserving existing DisplaySync config
[PASS] Watchdog scheduled task registered
[PASS] DisplaySync launched
=== Setup complete ===

A failure looks like:

=== DisplaySync Setup ===
[PASS] Found DisplaySync at C:\Program Files\DisplaySync Sign\DisplaySync Sign.exe
[PASS] Found Tailscale installer
[FAIL] config.json: tailscaleAuthKey is still the placeholder

Setup failed at step 3 of 11. See setup.log for details.

The output is intentionally tech-readable — large [PASS] / [FAIL] markers so a tech standing at a kiosk can read the screen and know exactly what step failed.

What the script writes

PathPurpose
C:\Program Files\Tailscale\Tailscale installation (if not already present)
C:\ProgramData\Tailscale\Tailscale state (node identity, etc.)
setup.log (on the USB drive)Full output log of this run
\Microsoft\Windows\TaskScheduler\ (registry)The watchdog scheduled task
C:\ProgramData\DisplaySync\ (cleared if freshConfig: true)DisplaySync machine-wide config
%APPDATA%\desktop-sign\ (cleared if freshConfig: true)DisplaySync per-user config

Common failure modes

OutputCauseFix
[FAIL] DisplaySync is not installedThe DisplaySync installer wasn't run before setup.batRun the DisplaySync installer first, then re-run setup.bat
[FAIL] tailscale-setup.exe not foundMissing from the USBDownload from tailscale.com/download and copy to the USB root
[FAIL] tailscaleAuthKey is still the placeholderconfig.json wasn't edited before the drive was usedEdit config.json on the drive and re-run
[FAIL] tailscale up: auth key expiredThe auth key has hit its expiryMint a new key and update config.json
[FAIL] Watchdog task registration failedPermission issue, or pre-existing task with conflicting nameReboot, then re-run; the script unregisters and re-registers idempotently
Script cannot be loaded because running scripts is disabledPowerShell execution policyThe script normally bypasses this via setup.bat. If running directly: Set-ExecutionPolicy -Scope Process Bypass

Re-running on a working machine

Re-running setup.bat on a kiosk that's already configured:

  • Tailscale install: skipped (already present)
  • tailscale up: runs again with the auth key — refreshes the join. Harmless.
  • Watchdog task: unregistered and re-registered. Harmless; brief gap during the swap.
  • DisplaySync config wipe: runs only if freshConfig: true. This is destructive if a sign was claimed — it loses its local org/event link and reverts to the QR screen.
  • DisplaySync launch: ensures it's running.

The destructive case is the freshConfig: true flag. If a tech in the field re-runs setup.bat for some other reason and freshConfig is true from when the drive was originally configured, they'll wipe a working sign. For drives that walk into the field, set freshConfig: false unless you specifically need a fresh recovery.

Customizing the script

The script is designed to be readable and modifiable. Common customizations:

  • Different sign-app product name — edit $AppProductName and $AppPackageName near the top
  • Different watchdog cadence — edit the New-ScheduledTaskTrigger interval
  • Additional install steps (anti-virus exclusions, custom registry keys) — append a new step to phase 2

If you fork the script, keep the [PASS] / [FAIL] convention — the tech-facing UX depends on it.

See also