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:
- Discover — find the DisplaySync install, find the Tailscale installer, parse
config.json. - Configure — install Tailscale (if needed), join the tailnet, optionally wipe DisplaySync config, register the watchdog scheduled task.
- 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
}
| Field | Required | Type | Default | Purpose |
|---|---|---|---|---|
tailscaleAuthKey | ✅ | string | — | Pre-authorized Tailscale auth key. The setup fails fast if this is missing or still set to the placeholder. |
environment | ❌ | string | "production" | Logged in setup.log for diagnostics. Doesn't affect script behavior. |
freshConfig | ❌ | boolean | false | If 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
- Self-elevate. If not admin, relaunch via UAC and exit the non-elevated instance.
- Locate DisplaySync. Searches:
C:\Program Files\DisplaySync Sign\DisplaySync Sign.exeC:\Program Files (x86)\DisplaySync Sign\DisplaySync Sign.exe%LOCALAPPDATA%\Programs\DisplaySync Sign\DisplaySync Sign.exe(per-user install)- The original-user-profile
Programsdirectory (if self-elevation crossed user contexts) - Every
C:\Users\*\AppData\Local\Programs\DisplaySync Sign\candidate
- Locate Tailscale installer. Looks for
tailscale-setup.exein the same directory assetup.ps1(the USB drive root). - 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
- Install Tailscale (if not already present). Runs
tailscale-setup.exesilently with NSIS flags. Idempotent — skips if Tailscale is already installed and up-to-date enough. - Join the tailnet. Runs:
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.tailscale up --authkey <key> --hostname sign-<serial> --unattended - Optionally wipe DisplaySync config. If
freshConfig: true, deletes:C:\ProgramData\DisplaySync\(machine-wide config and.maintenancesentinel — see File locations)%APPDATA%\desktop-sign\for every user (Electron Store config)
- Register the watchdog scheduled task. Creates a task named
DisplaySyncWatchdogthat runs every 60 seconds and:- Checks for the
.maintenancesentinel — skips if present - Checks if
DisplaySync Sign.exeis running — relaunches if not
- Checks for the
Phase 3 — Launch
- Start DisplaySync. Launches
DisplaySync Sign.exeif not already running. - Verify Tailscale connectivity. Runs
tailscale statusandtailscale ip -4to confirm join. Logs the IP. - 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
| Path | Purpose |
|---|---|
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
| Output | Cause | Fix |
|---|---|---|
[FAIL] DisplaySync is not installed | The DisplaySync installer wasn't run before setup.bat | Run the DisplaySync installer first, then re-run setup.bat |
[FAIL] tailscale-setup.exe not found | Missing from the USB | Download from tailscale.com/download and copy to the USB root |
[FAIL] tailscaleAuthKey is still the placeholder | config.json wasn't edited before the drive was used | Edit config.json on the drive and re-run |
[FAIL] tailscale up: auth key expired | The auth key has hit its expiry | Mint a new key and update config.json |
[FAIL] Watchdog task registration failed | Permission issue, or pre-existing task with conflicting name | Reboot, then re-run; the script unregisters and re-registers idempotently |
Script cannot be loaded because running scripts is disabled | PowerShell execution policy | The 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
$AppProductNameand$AppPackageNamenear the top - Different watchdog cadence — edit the
New-ScheduledTaskTriggerinterval - 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
- Building the drive — what to put on the drive before running this script
- Field recovery playbook — how a tech actually uses this in the field
- Tailscale integration — what the auth key flag does