<# .SYNOPSIS CyberTracker installer — downloads the tracker binary, registers the PC, and installs CyberTracker as a Windows service. .DESCRIPTION Run as Administrator in an elevated PowerShell window. The script: 1. Generates a UUIDv4 API key for this installation attempt. 2. Displays a QR code in the console pointing the superuser to the PC registration page so the admin can assign a zone and PC number. 3. Polls the backend until the PC is registered. 4. Downloads the CyberTracker binary and verifies its SHA256 checksum. 5. Runs cybertracker.exe -setup to install the Windows service. 6. Reports any errors back to the backend. One-liner bootstrap (fetch from backend and execute): irm https://cyberpass.tech/api/v1/tracker/dist/Install-App.ps1 | iex #> $ErrorActionPreference = "Stop" # ── Elevation guard ───────────────────────────────────────────────── # Installing the service and writing to HKLM requires an elevated shell. # Fail fast here instead of running the whole registration flow and then # dying mid-install with "Access is denied". if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Host "CyberTracker Installer must be run as Administrator." -ForegroundColor Red Write-Host "Relaunch PowerShell as Administrator and re-run Install-App.ps1." -ForegroundColor Yellow exit 1 } # ── Configuration ─────────────────────────────────────────────────── # Each host provides only the scheme+domain (e.g. https://mirvr.cyberpass.tech); # the tracker API path and the frontend registration URL are derived from them. $ApiHost = if ($env:CYBERPASS_API_HOST) { $env:CYBERPASS_API_HOST } else { "https://mirvr.cyberpass.tech" } $FrontendHost = if ($env:CYBERPASS_FRONTEND_HOST) { $env:CYBERPASS_FRONTEND_HOST } else { "https://mirvr.cyberpass.tech" } $ApiBase = "$ApiHost/api/v1/tracker" # The tracker connects back over WebSocket. Derive the ws(s) endpoint from the # same API host so CYBERPASS_API_HOST overrides every backend URL, not just the # HTTP poll endpoints. Exported as TRANSPORT_URL: -setup reads it and persists # it to the registry (overwriting any previous install) on every run. $TransportUrl = ($ApiHost -replace "^http://", "ws://" -replace "^https://", "wss://") + "/api/v1/tracker/ws" $env:TRANSPORT_URL = $TransportUrl # Durable location to persist the API key once the PC is created, so the key # (and the PC's identity) is recoverable even if a later step (binary download # or service install) fails and the installer exits early. $InstallDataDir = Join-Path $env:ProgramData "CyberPassTracker" $InstallDataFile = Join-Path $InstallDataDir "install.json" $PollMax = [TimeSpan]::FromMinutes(15) $PollWait = [TimeSpan]::FromSeconds(5) $MaxRetries = 3 # ── QR code generation (local, no network needed) ────────────────── $script:QrDllLoaded = $false $script:QrProcess = $null function Show-QR { param([string]$Url) Write-Host "" Write-Host "Scan this QR code with your phone to register this PC:" -ForegroundColor Cyan Write-Host "" if (-not $script:QrDllLoaded) { $dllDir = Join-Path ([System.IO.Path]::GetTempPath()) "cyberqr" if (-not (Test-Path (Join-Path $dllDir "qrcodegen.dll"))) { New-Item -ItemType Directory -Force -Path $dllDir | Out-Null [System.IO.File]::WriteAllBytes( (Join-Path $dllDir "qrcodegen.dll"), [Convert]::FromBase64String($script:QrDllB64) ) } Add-Type -TypeDefinition $script:QrCSharp $script:QrDllLoaded = $true } try { $tmpFile = Join-Path ([System.IO.Path]::GetTempPath()) "cyberpass_qr.bmp" $bytes = [CyberQr.QrHelper]::GenerateBmp($Url, 8, 4) [System.IO.File]::WriteAllBytes($tmpFile, $bytes) $script:QrProcess = Start-Process $tmpFile -PassThru Write-Host " QR image opened in default viewer." -ForegroundColor DarkGray } catch { Write-Host " QR: $Url" -ForegroundColor Yellow } Write-Host "" Write-Host " Or open: $Url" -ForegroundColor DarkGray } # Best-effort teardown of the QR viewer. Never allowed to throw: probing # .HasExited on a process launched through a shell file association can raise # InvalidOperationException ("No process is associated with this object"), # and an unhandled error here would abort the registration flow itself. function Close-QRViewer { if (-not $script:QrProcess) { return } try { if (-not $script:QrProcess.HasExited) { $script:QrProcess.CloseMainWindow() | Out-Null if (-not $script:QrProcess.HasExited) { $script:QrProcess.Kill() } } } catch { # Viewer already gone, or the handle is unusable — nothing to do. } finally { $script:QrProcess = $null } } # Embedded QR code library (Nayuki's qrcodegen, MIT license, compiled) $script:QrDllB64 = "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ZW4uYwBmYWxzZQBxcmNvZGVnZW5fVkVSU0lPTl9NSU4gPD0gdmVyc2lvbiAmJiB2ZXJzaW9uIDw9IHFyY29kZWdlbl9WRVJTSU9OX01BWADws///+7P//x+0//8GtP//H7T//x+0//84tP//EbT//zAgPD0gbnVtQml0cyAmJiBudW1CaXRzIDw9IDE2ICYmICh1bnNpZ25lZCBsb25nKXZhbCA+PiBudW1CaXRzID09IDAAcXJjb2RlZ2VuX1ZFUlNJT05fTUlOIDw9IHZlciAmJiB2ZXIgPD0gcXJjb2RlZ2VuX1ZFUlNJT05fTUFYADIwOCA8PSByZXN1bHQgJiYgcmVzdWx0IDw9IDI5NjQ4ADIxIDw9IHFyc2l6ZSAmJiBxcnNpemUgPD0gMTc3ICYmIDAgPD0geCAmJiB4IDwgcXJzaXplICYmIDAgPD0geSAmJiB5IDwgcXJzaXplAG4gPD0gcXJzaXplICogMwAwIDw9IGUgJiYgZSA8IDQAcXJjb2RlICE9IE5VTEwAKHFyY29kZWdlbl9WRVJTSU9OX01JTiAqIDQgKyAxNykgPD0gcmVzdWx0ICYmIHJlc3VsdCA8PSAocXJjb2RlZ2VuX1ZFUlNJT05fTUFYICogNCArIDE3KQAiuP//LLj//zW4//85uP//Rbj//1K4//9luP//cbj//2JpdHMgPj4gMTUgPT0gMABzZWdzICE9IE5VTEwgfHwgbGVuID09IDAAcXJjb2RlZ2VuX1ZFUlNJT05fTUlOIDw9IG1pblZlcnNpb24gJiYgbWluVmVyc2lvbiA8PSBtYXhWZXJzaW9uICYmIG1heFZlcnNpb24gPD0gcXJjb2RlZ2VuX1ZFUlNJT05fTUFYADAgPD0gKGludCllY2wgJiYgKGludCllY2wgPD0gMyAmJiAtMSA8PSAoaW50KW1hc2sgJiYgKGludCltYXNrIDw9IDcAMCA8PSBudW1DaGFycyAmJiBudW1DaGFycyA8PSBJTlQxNl9NQVgAMCA8PSBiaXRMZW5ndGggJiYgYml0TGVuZ3RoIDw9IElOVDE2X01BWAAwIDw9IHJlc3VsdCAmJiByZXN1bHQgPD0gSU5UMTZfTUFYAGJpdExlbiA9PSBkYXRhVXNlZEJpdHMAYml0TGVuIDw9IGRhdGFDYXBhY2l0eUJpdHMAYml0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AAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAEdDQzogKFJldjUsIEJ1aWx0IGJ5IE1TWVMyIHByb2plY3QpIDE2LjEuMAAAAAAAAP//////////8EG6EAMAAAAAAAAAAAAAAP//////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADO6EAMAAADgMroQAwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAgSAAAAgAAAEBIAAPsSAAAQgAAAABMAAA8TAAAggAAAEBMAABwTAAAkgAAAIBMAACETAAAogAAAMBMAAF8TAAAsgAAAXxMAALQTAAAwgAAAtBMAADoUAAA0gAAAOhQAAM8UAAA8gAAAzxQAADgVAABEgAAAOBUAAMsVAABMgAAAyxUAACAWAABUgAAAIBYAAIoWAABcgAAAihYAAOUWAABkgAAA5RYAAGcXAAB4gAAAZxcAAOkYAACAgAAA6RgAAAcZAACYgAAABxkAAGUZAACcgAAAZRkAAIUZAACogAAAhRkAANAZAACsgAAA0BkAACUbAAC0gAAAJRsAALEcAADMgAAAsRwAAEQqAADcgAAARCoAANEqAAD0gAAA0SoAAA8rAAAEgQAADysAAFErAAAMgQAAUSsAAI4rAAAUgQAAjisAANsrAAAcgQAA2ysAAP8rAAAkgQAA/ysAAIwsAAAsgQAAjCwAANwtAAA8gQAA3C0AAFMvAABQgQAAUy8AAMswAABogQAAyzAAAO8xAACAgQAA8DEAADIyAACMgQAAQDIAALIyAACUgQAAwDIAAN8yAACggQAA4DIAAPUyAACkgQAAADMAAHszAACogQAAgDMAAIMzAAC0gQAAkDMAAO4zAAC4gQAA8DMAAFI1AADEgQAAYDUAAAA5AADQgQAAADkAAGc5AADogQAAcDkAAAY6AAD4gQAAEDoAAKE6AAAAggAAsDoAAKI7AAAIggAAwDsAAMM7AAAQggAA0DsAAPw7AAAUggAAADwAAFA8AAAYggAAUDwAAOY8AAAcggAA8DwAAGE9AAAsggAAcD0AAJ09AAAwggAAoD0AAAw+AAA0ggAAED4AAEA+AAA4ggAAQD4AAME+AAA8ggAA0D4AAIg/AABAggAA0D8AANY/AABEggAA4D8AAC1AAABIggAAMEAAAGNAAABYggAAcEAAALRAAABkggAAwEAAAMhAAABwggAA0EAAAP5AAAB0ggAA8EEAAPVBAAB8ggAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEIBQAIYgQwA2ACcAFQAAABCAUACGIEMANgAnABUAAAAQAAAAEAAAABAAAAAQAAAAEAAAABBAEABEIAAAEEAQAEQgAAAQUCAAUyATABBAEABEIAAAEEAQAEQgAAAQQBAARCAAABDggADjIKMAlgCHAHUAbABNAC4AEEAQAEQgAAARAJABCiDDALYApwCVAIwAbQBOAC8AAAAQAAAAEGAwAGQgIwAWAAAAEAAAABBAEABEIAAAEQCQAQYgwwC2AKcAlQCMAG0ATgAvAAAAEKBgAKMgYwBWAEcANQAsABEwoAEwEVAAwwC2AKcAlQCMAG0ATgAvABCAUACOIEMANgAnABUAAAAQQBAASiAAABBAEABEIAAAEEAQAEQgAAAQUCAAUyATABBAEABEIAAAEKBgAKMgYwBWAEcANQAsABDAcADIIIMAdgBnAFUATAAtAAAAEQCQAQogwwC2AKcAlQCMAG0ATgAvAAAAEQCQAQ4gwwC2AKcAlQCMAG0ATgAvAAAAEHBAAHcgMwAmABcAEEAQAEQgAAAQYDAAZCAjABYAAAAQAAAAEAAAABBgMABkICMAFgAAABAAAAAQYDAAZiAjABYAAAAQcEAAeSAzACYAFwARMJRRMDDnIKMAlgCHAHwAXQA+ABUAAAAQgFAAhCBDADYAJwAVAAAAEEAQAEYgAAAQQBAARCAAABBAEABGIAAAEAAAABAAAAAQAAAAEIBQAIQgQwA2ACcAFQAAABAAAAAQAAAAEAAAABAAAAAQAAAAEAAAABAAAAAQgFAAhCBDADYAJwAVAAAAEHBAAHUgMwAmABcAEHBAAHcgMwAmABcAEAAAABBQIABTIBMAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgISVagAAAACqoAAAAQAAAA0AAAANAAAAKKAAAFygAACQoAAA2ysAAEQqAADRKgAAsRwAAFMvAAAPKwAAhRkAAI4rAABRKwAA3C0AAP8rAADLMAAAjCwAALigAADYoAAA76AAAAihAAApoQAAPqEAAFKhAABkoQAAfaEAAJGhAACsoQAAwKEAANKhAAAAAAEAAgADAAQABQAGAAcACAAJAAoACwAMAHFyY29kZWdlbi5kbGwAcXJjb2RlZ2VuX2NhbGNTZWdtZW50QnVmZmVyU2l6ZQBxcmNvZGVnZW5fZW5jb2RlQmluYXJ5AHFyY29kZWdlbl9lbmNvZGVTZWdtZW50cwBxcmNvZGVnZW5fZW5jb2RlU2VnbWVudHNBZHZhbmNlZABxcmNvZGVnZW5fZW5jb2RlVGV4dABxcmNvZGVnZW5fZ2V0TW9kdWxlAHFyY29kZWdlbl9nZXRTaXplAHFyY29kZWdlbl9pc0FscGhhbnVtZXJpYwBxcmNvZGVnZW5faXNOdW1lcmljAHFyY29kZWdlbl9tYWtlQWxwaGFudW1lcmljAHFyY29kZWdlbl9tYWtlQnl0ZXMAcXJjb2RlZ2VuX21ha2VFY2kAcXJjb2RlZ2VuX21ha2VOdW1lcmljAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJCwAAAAAAAAAAAAAIy0AACgsQAA4LAAAAAAAAAAAAAApLQAAPCxAAD4sAAAAAAAAAAAAADMtAAACLIAABCxAAAAAAAAAAAAABC1AAAgsgAAWLEAAAAAAAAAAAAASLUAAGiyAACIsQAAAAAAAAAAAABwtQAAmLIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALCyAAAAAAAAyLIAAAAAAADgsgAAAAAAAPCyAAAAAAAADLMAAAAAAAAkswAAAAAAACyzAAAAAAAAOrMAAAAAAABMswAAAAAAAAAAAAAAAAAAXLMAAAAAAABmswAAAAAAAAAAAAAAAAAAbrMAAAAAAAB4swAAAAAAAAAAAAAAAAAAgrMAAAAAAACMswAAAAAAAKSzAAAAAAAArLMAAAAAAADIswAAAAAAANSzAAAAAAAA4rMAAAAAAAD+swAAAAAAAAAAAAAAAAAABrQAAAAAAAAYtAAAAAAAADK0AAAAAAAAPLQAAAAAAABItAAAAAAAAAAAAAAAAAAAUrQAAAAAAABctAAAAAAAAAAAAAAAAAAAsLIAAAAAAADIsgAAAAAAAOCyAAAAAAAA8LIAAAAAAAAMswAAAAAAACSzAAAAAAAALLMAAAAAAAA6swAAAAAAAEyzAAAAAAAAAAAAAAAAAABcswAAAAAAAGazAAAAAAAAAAAAAAAAAABuswAAAAAAAHizAAAAAAAAAAAAAAAAAACCswAAAAAAAIyzAAAAAAAApLMAAAAAAACsswAAAAAAAMizAAAAAAAA1LMAAAAAAADiswAAAAAAAP6zAAAAAAAAAAAAAAAAAAAGtAAAAAAAABi0AAAAAAAAMrQAAAAAAAA8tAAAAAAAAEi0AAAAAAAAAAAAAAAAAABStAAAAAAAAFy0AAAAAAAAAAAAAAAAAAAqAURlbGV0ZUNyaXRpY2FsU2VjdGlvbgBSAUVudGVyQ3JpdGljYWxTZWN0aW9uAACNAkdldExhc3RFcnJvcgAAmwNJbml0aWFsaXplQ3JpdGljYWxTZWN0aW9uAPkDTGVhdmVDcml0aWNhbFNlY3Rpb24AALAFU2xlZXAA1AVUbHNHZXRWYWx1ZQAEBlZpcnR1YWxQcm90ZWN0AAAGBlZpcnR1YWxRdWVyeQAAGABjYWxsb2MAABkAZnJlZQAAQARtZW1jcHkAAEQEc3RyY2hyAAASAF9hc3NlcnQAIgBfZXhlY3V0ZV9vbmV4aXRfdGFibGUAIwBfZXhpdAA1AF9pbml0aWFsaXplX29uZXhpdF90YWJsZQAAOQBfaW5pdHRlcm0AOgBfaW5pdHRlcm1fZQA+AF9yZWdpc3Rlcl9vbmV4aXRfZnVuY3Rpb24AVwBhYm9ydAAAAF9fYWNydF9pb2JfZnVuYwADAF9fc3RkaW9fY29tbW9uX3ZmcHJpbnRmADMAX2ZpbGVubwB2AF9zZXRtb2RlAACcAGZmbHVzaAAAqABzdHJsZW4AAKsAc3RybmNtcAAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAACwAAAAsAAAALAAAEtFUk5FTDMyLmRsbAAAAAAUsAAAFLAAAGFwaS1tcy13aW4tY3J0LWhlYXAtbDEtMS0wLmRsbAAAKLAAACiwAABhcGktbXMtd2luLWNydC1wcml2YXRlLWwxLTEtMC5kbGwAAAA8sAAAPLAAADywAAA8sAAAPLAAADywAAA8sAAAPLAAAGFwaS1tcy13aW4tY3J0LXJ1bnRpbWUtbDEtMS0wLmRsbAAAAFCwAABQsAAAULAAAFCwAABQsAAAYXBpLW1zLXdpbi1jcnQtc3RkaW8tbDEtMS0wLmRsbABksAAAZLAAAGFwaS1tcy13aW4tY3J0LXN0cmluZy1sMS0xLTAuZGxsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFAAABAAAAAQoECgYKAAAABgAAA4AAAAoKfAp8in0KfYp4CpkKmgqbCpwKnQqeCp8KkAqhCqIKowqkCqUKpgqpiu4K7orgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" # C# P/Invoke wrapper for qrcodegen.dll (compiled at runtime via Add-Type) $script:QrCSharp = @" using System; using System.IO; using System.Runtime.InteropServices; using System.Text; namespace CyberQr { public static class QrHelper { [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] private static extern bool SetDllDirectory(string lpPathName); public static byte[] GenerateBmp(string text, int pixelsPerModule, int margin) { string dllDir = Path.Combine(Path.GetTempPath(), "cyberqr"); SetDllDirectory(dllDir); int bufLen = (40 * 4 + 17); bufLen = (bufLen * bufLen + 7) / 8 + 1; byte[] tempBuf = new byte[bufLen]; byte[] qrBuf = new byte[bufLen]; byte[] textBytes = Encoding.UTF8.GetBytes(text); IntPtr textPtr = Marshal.AllocHGlobal(textBytes.Length + 1); Marshal.Copy(textBytes, 0, textPtr, textBytes.Length); Marshal.WriteByte(textPtr, textBytes.Length, 0); bool ok = Native.qrcodegen_encodeText( textPtr, tempBuf, qrBuf, Native.Ecc.MEDIUM, 1, 40, (int)Native.Mask.AUTO, true); Marshal.FreeHGlobal(textPtr); if (!ok) throw new Exception("QR code encoding failed"); int size = Native.qrcodegen_getSize(qrBuf); int imgSize = (size + margin * 2) * pixelsPerModule; int rowBytes = ((imgSize + 31) / 32) * 4; int pixelDataSize = rowBytes * imgSize; int fileSize = 14 + 40 + 8 + pixelDataSize; byte[] bmp = new byte[fileSize]; bmp[0] = (byte)'B'; bmp[1] = (byte)'M'; BitConverter.GetBytes(fileSize).CopyTo(bmp, 2); BitConverter.GetBytes(62).CopyTo(bmp, 10); BitConverter.GetBytes(40).CopyTo(bmp, 14); BitConverter.GetBytes(imgSize).CopyTo(bmp, 18); BitConverter.GetBytes(imgSize).CopyTo(bmp, 22); BitConverter.GetBytes((short)1).CopyTo(bmp, 26); BitConverter.GetBytes((short)1).CopyTo(bmp, 28); bmp[58] = 255; bmp[59] = 255; bmp[60] = 255; for (int y = 0; y < imgSize; y++) { int fileY = imgSize - 1 - y; int rowOff = 62 + fileY * rowBytes; for (int x = 0; x < imgSize; x++) { int qrX = (x / pixelsPerModule) - margin; int qrY = (y / pixelsPerModule) - margin; bool isBlack = qrX >= 0 && qrX < size && qrY >= 0 && qrY < size && Native.qrcodegen_getModule(qrBuf, qrX, qrY); if (!isBlack) { int bi = rowOff + x / 8; bmp[bi] |= (byte)(1 << (7 - (x % 8))); } } } return bmp; } private static class Native { private const string Dll = "qrcodegen.dll"; public enum Ecc { LOW = 0, MEDIUM = 1, QUARTILE = 2, HIGH = 3 } public enum Mask { AUTO = -1 } [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool qrcodegen_encodeText( IntPtr text, byte[] tempBuffer, byte[] qrcode, Ecc ecl, int minVersion, int maxVersion, int mask, bool boostEcl); [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern int qrcodegen_getSize(byte[] qrcode); [DllImport(Dll, CallingConvention = CallingConvention.Cdecl)] public static extern bool qrcodegen_getModule(byte[] qrcode, int x, int y); } } } "@ function Invoke-WithBackoff { param( [scriptblock]$Action, [int]$MaxAttempts = 10, [int]$InitialDelay = 3 ) $delay = $InitialDelay for ($i = 0; $i -lt $MaxAttempts; $i++) { try { return & $Action } catch { $code = 0 if ($_.Exception.Response) { $code = [int]$_.Exception.Response.StatusCode } if ($code -ge 500 -or $code -eq 0) { $tries = $i + 1 Write-Host "`r Retrying ($tries/$MaxAttempts)..." -NoNewline -ForegroundColor DarkGray Start-Sleep -Seconds $delay $delay = [Math]::Min($delay * 2, 24) } else { throw } } } Write-Host "" throw "Max retries exceeded" } function Report-Error { param( [string]$ApiKey, [string]$Message ) try { $headers = @{ "Authorization" = "Bearer $ApiKey"; "Content-Type" = "application/json" } $body = @{ error = $Message } | ConvertTo-Json Invoke-RestMethod -Uri "$ApiBase/install/error" -Method Post -Headers $headers -Body $body -UseBasicParsing -ErrorAction SilentlyContinue } catch { # Best-effort; do not fail the installer on error reporting failure. } } # Store the API key (and the backend URLs it needs) on disk once the PC has # been created. If anything later in the install fails, the operator can find # the key here and re-run -setup manually instead of orphaning the machine. function Store-InstallKey { param( [string]$ApiKey, [string]$ApiHost, [string]$FrontendHost ) try { New-Item -ItemType Directory -Force -Path $InstallDataDir | Out-Null $payload = @{ apiKey = $ApiKey apiHost = $ApiHost frontendHost = $FrontendHost registeredAt = (Get-Date).ToUniversalTime().ToString("o") } | ConvertTo-Json Set-Content -Path $InstallDataFile -Value $payload -Encoding utf8 Write-Host " API key saved to $InstallDataFile" -ForegroundColor DarkGray } catch { # Non-fatal: persistence is best-effort, never break the install for it. Write-Host " Warning: could not save API key to $InstallDataFile" -ForegroundColor DarkYellow } } # ── Step 1: Generate API key ─────────────────────────────────────── $apiKey = [guid]::NewGuid().ToString() # Unix timestamp (seconds) marking the end of the 15-minute registration # window. Embedded in the QR so the backend can refuse to create the PC once # the installation invitation has expired. # Use the .NET epoch offset (not `Get-Date -UFormat %s`), which behaves # inconsistently across PowerShell editions — on Windows PowerShell 5.1 it can # return an inflated value that overflows an [int] cast. $expiresAt = [DateTimeOffset]::UtcNow.ToUnixTimeSeconds() + $PollMax.TotalSeconds Write-Host "CyberTracker Installer" -ForegroundColor Green Write-Host "=====================" -ForegroundColor Green Write-Host "" Write-Host "API Key: $apiKey" -ForegroundColor Cyan # ── Step 2: Show QR for registration ────────────────────────────── $registerUrl = "$FrontendHost/dashboard/super/computers#apiKey=$apiKey&expiresAt=$expiresAt" Show-QR -Url $registerUrl Write-Host "" Write-Host "Waiting for admin to register this PC..." -ForegroundColor Yellow Write-Host " (Timeout: $($PollMax.TotalMinutes) minutes)" -ForegroundColor DarkGray # ── Step 3: Poll until registered ────────────────────────────────── $deadline = (Get-Date).Add($PollMax) $registered = $false while ((Get-Date) -lt $deadline) { try { $headers = @{ "Authorization" = "Bearer $apiKey" } $resp = Invoke-WebRequest -Uri "$ApiBase/install/checksum" -Headers $headers -UseBasicParsing -ErrorAction Stop if ($resp.StatusCode -eq 200) { $checksum = ($resp.Content | ConvertFrom-Json).checksum Write-Host "`r Registration confirmed! Checksum: $checksum" -ForegroundColor Green # The PC now exists in the backend — persist the API key so a # failure in the remaining steps (or a human re-run) can recover it. Store-InstallKey -ApiKey $apiKey -ApiHost $ApiHost -FrontendHost $FrontendHost $registered = $true break } } catch { $code = 0 if ($_.Exception.Response) { $code = [int]$_.Exception.Response.StatusCode } } for ($s = 0; $s -lt [int]$PollWait.TotalSeconds; $s++) { $rem = $deadline - (Get-Date) if ($rem.TotalSeconds -le 0) { break } $ts = "{0:mm\:ss}" -f $rem Write-Host "`r Waiting for registration... ($ts left)" -NoNewline -ForegroundColor DarkGray Start-Sleep -Seconds 1 } } if (-not $registered) { $msg = "Admin did not complete registration within $($PollMax.TotalMinutes) minutes — restart setup" Write-Host "" Write-Host $msg -ForegroundColor Red Report-Error -ApiKey $apiKey -Message $msg exit 1 } # Registration is done, so the QR is no longer useful. Kept outside the poll # loop: a viewer-teardown failure must never be mistaken for a failed # registration. Close-QRViewer Write-Host "" # ── Step 4: Download binary and verify checksum ──────────────────── # When piped through `irm | iex` there is no script file, so # $PSScriptRoot is empty and Join-Path fails with an empty 'Path'. Run # from a directory we can write to, preferring the script root when it # exists (e.g. when the script is run from a local file). $installDir = if ([string]::IsNullOrWhiteSpace($PSScriptRoot)) { (Get-Location).Path } else { $PSScriptRoot } $binaryPath = Join-Path $installDir "cybertracker.exe" $downloadOk = $false $tries = 0 for ($attempt = 1; $attempt -le $MaxRetries; $attempt++) { Write-Host "`r Downloading CyberTracker ($attempt/$MaxRetries)..." -NoNewline -ForegroundColor Cyan try { $headers = @{ "Authorization" = "Bearer $apiKey" } Invoke-WebRequest -Uri "$ApiBase/dist/cybertracker" -Headers $headers -OutFile $binaryPath -UseBasicParsing -ErrorAction Stop # Verify checksum $hash = (Get-FileHash -Path $binaryPath -Algorithm SHA256).Hash.ToLower() if ($hash -eq $checksum) { Write-Host "`r Checksum verified: $hash" -ForegroundColor Green $downloadOk = $true break } else { if ($attempt -eq $MaxRetries) { Write-Host "`r Checksum mismatch after $MaxRetries attempts" -ForegroundColor Red Report-Error -ApiKey $apiKey -Message "SHA256 mismatch after $MaxRetries attempts" Remove-Item -Path $binaryPath -Force -ErrorAction SilentlyContinue exit 1 } } } catch { if ($attempt -eq $MaxRetries) { Write-Host "`r Download failed after $MaxRetries attempts" -ForegroundColor Red Report-Error -ApiKey $apiKey -Message "Cannot download binary after $MaxRetries attempts" exit 1 } } Start-Sleep -Seconds 2 } # ── Step 5: Run -setup ───────────────────────────────────────────── Write-Host "Installing CyberTracker service..." -ForegroundColor Cyan $setupProc = Start-Process -FilePath $binaryPath -ArgumentList "-setup", $apiKey -Wait -PassThru -NoNewWindow $exitCode = $setupProc.ExitCode switch ($exitCode) { 0 { Write-Host "Installation complete — CyberTracker running!" -ForegroundColor Green } 2 { $msg = "Failed to save API key — check Registry permissions" Write-Host $msg -ForegroundColor Red Report-Error -ApiKey $apiKey -Message $msg exit $exitCode } 3 { $msg = "Failed to install service — check admin rights" Write-Host $msg -ForegroundColor Red Report-Error -ApiKey $apiKey -Message $msg exit $exitCode } 4 { $msg = "Service started but verify failed — try again" Write-Host $msg -ForegroundColor Red Report-Error -ApiKey $apiKey -Message $msg exit $exitCode } default { $msg = "Setup failed with exit code $exitCode" Write-Host $msg -ForegroundColor Red Report-Error -ApiKey $apiKey -Message $msg exit $exitCode } }