DisplaySync/ docs

Tailscale integration

Tailscale gives you a private overlay network that reaches every kiosk regardless of what venue network it landed on. With it, you can RDP/VNC into a misbehaving sign from anywhere; without it, you're calling someone at the venue.

This step is optional but strongly recommended for any fleet you don't physically supervise day-of. It adds about five minutes to the image build.

Why a tagged, pre-authorized auth key

Each kiosk needs to log itself into your tailnet on first boot, with no human interaction. The way to do that:

  1. Create an ACL tag in Tailscale, e.g., tag:displaysync-sign
  2. Mint a reusable, pre-authorized auth key assigned to that tag
  3. Bake the key into the image (in the Tailscale install command line)
  4. Use ACLs to restrict what tag:displaysync-sign devices can reach and who can reach them

A tagged device has no human owner — it's a machine identity. That matters for two reasons:

  • No expiry games: untagged devices on the free plan re-authenticate every 6 months. Tagged ones don't.
  • ACL clarity: ACLs that target the tag are simple and stable, regardless of which person originally generated the key.

A pre-authorized key is added to the tailnet without admin approval. A reusable key can onboard many devices from one secret. Treat that secret like a production credential — store it in your secrets manager, rotate it periodically, and never commit it to a public repo.

1. Create the tag and ACL

In the Tailscale admin console, edit your tailnet's ACL policy. A starting-point grant set:

{
  "tagOwners": {
    "tag:displaysync-sign": ["autogroup:admin"]
  },
  "grants": [
    // Admins can reach everything (signs, peer devices, etc.)
    { "src": ["autogroup:admin"], "dst": ["*"], "ip": ["*"] },

    // Members (your support team) can reach signs + their own devices
    { "src": ["autogroup:member"], "dst": ["tag:displaysync-sign"], "ip": ["*"] },
    { "src": ["autogroup:member"], "dst": ["autogroup:self"],       "ip": ["*"] },

    // Signs can reach NOTHING on the tailnet — outbound-only by default.
    // (No grant for tag:displaysync-sign as src.)
  ]
}

This gives you:

  • Admins reach anything.
  • Members (techs) reach signs and their own devices.
  • Signs reach nothing — they're outbound-only beachheads.

If you want signs to talk to a specific service on the tailnet (e.g., a logging endpoint), add a narrow grant. Default-deny is the right starting point.

Free plan ACL note

Tailscale's free plan limits the grants DSL features available. If your tailnet is on the free plan and the JSON above won't compile, fall back to the older acls syntax — same intent, different syntax. The Tailscale docs cover the migration.

2. Mint the auth key

Admin console → Settings → Keys → Generate auth key:

  • Reusable:
  • Pre-approved:
  • Ephemeral: ✗ (we want devices to persist)
  • Tags: tag:displaysync-sign
  • Expiration: 1–90 days (the lower the better; rotate when needed)

Copy the key. You'll bake it into the install command in step 4. The key won't be shown again — store it.

3. Install Tailscale on the build machine

Download the latest Windows installer from https://pkgs.tailscale.com/stable/. The installer accepts MSI properties for unattended setup.

Silent install:

Start-Process msiexec.exe -ArgumentList `
  "/i tailscale-setup.msi /quiet /norestart" -Wait

After install, configure it as a system service that starts at boot:

Set-Service -Name "Tailscale" -StartupType Automatic
Start-Service -Name "Tailscale"

4. Bring the kiosk up on the tailnet

Run tailscale up with the auth key. The flags below are what we want for kiosk fleet behavior:

& "C:\Program Files\Tailscale\tailscale.exe" up `
  --authkey "tskey-auth-XXXXXXXXXXXXXX" `
  --hostname $env:COMPUTERNAME `
  --unattended
FlagPurpose
--authkeyThe pre-authorized key from step 2
--hostnameShow the device under its real Windows hostname in the admin console
--unattendedRun as a system service, even when no user is logged in (critical for kiosks)

After this completes, run tailscale status and confirm the device shows up. From another tailnet device, ping its tail-IP (e.g., 100.x.y.z) and confirm it responds.

5. Wire up the IP into DisplaySync's heartbeat

The desktop sign reads tailscale ip -4 on every heartbeat and reports it as deviceInfo.tailscaleIp. The dashboard surfaces this as a clickable link on the sign's detail page. No additional configuration needed — if Tailscale is up and reachable on the kiosk, the dashboard will show it.

If the field is blank in the dashboard but tailscale status works locally, check that the kiosk user has read access to the Tailscale CLI binary and named pipe. Restart the sign app after fixing.

6. Connect VNC, RDP, or SSH (optional)

For hands-on access from a tagged support device, install VNC server during the image build. Common picks:

  • TightVNC (free, simple, sufficient for occasional support)
  • MeshCentral (heavier, includes audit logging — overkill for most)

Whatever you pick, configure it to:

  • Bind only to the Tailscale interface (don't listen on the LAN)
  • Require a password
  • Auto-start as a service

The Tailscale ACLs from step 1 are what actually gate access — VNC's password is a defense-in-depth layer.

RDP (built-in) is also a solid choice: enable Remote Desktop, ensure the kiosk user has the Remote Desktop Users role, and rely on Tailscale ACLs for the network gate.

7. Verify on the build machine

Before moving on:

# Service is running and set to auto-start
Get-Service Tailscale | Select-Object Status, StartType

# Device is logged in and tagged
& "C:\Program Files\Tailscale\tailscale.exe" status

# Tail-IP is assigned
& "C:\Program Files\Tailscale\tailscale.exe" ip -4

Then from any tagged admin device, ping the tail-IP. If it responds, you're good.

What about the auth key after capture?

After tailscale up --authkey ... runs successfully, the device persists its node key in the Tailscale state directory (C:\ProgramData\Tailscale\). The auth key is not stored — only the resulting machine identity. That means:

  • Captured images all join the tailnet on first boot (the unattended service handles it).
  • The tailnet log will show one device per clone — you don't need to worry about all clones sharing one identity.
  • If a clone fails to join, it's almost always because Sysprep generalize cleared the Tailscale state. The fix is to re-run tailscale up --authkey ... on first boot. The USB recovery kit handles this case automatically; for routine clones it should "just work."

If you choose not to use Tailscale, the dashboard's Remote Control commands still work over the regular WebSocket — you only lose the ability to open a desktop session into the kiosk. Worth the trade-off only for very small fleets you can physically reach.

What's next

Continue to Crash recovery — the Task Scheduler watchdog that restarts the sign app if it crashes.