<# .SYNOPSIS Holon Toolbox Remote Launcher .DESCRIPTION Downloads and runs the Holon Toolbox from a private GitHub repo. Usage: irm https://toolbox.nxcore.hu | iex The user enters a short memorable password that decrypts the stored GitHub PAT, which is then used to download the repo as a zip file. No git installation required. Files are stored in %LOCALAPPDATA%\Holon-Toolbox and overwritten on each run. #> # ============================================================ # CONFIG — Paste your encrypted PAT here (from encrypt-pat.ps1) # ============================================================ $EncryptedPAT = "v2:G8F/QYIpmAXQhYqlKPZi76ZFQFc8ECaOjNPb8sO0QeB4dbt2U6BQ0SQ7BaH30KANYoatuuQ7bEQio24MQJbRGwMMsHd/dlwrQW9AaGaUwoahUPWV0ChA5yI1XPJNdptAMrZDc10jrUsJ3RUMgzv5egDnM/X2yyviXW2j1dCFBtQ=" $RepoOwner = "DRYN07" $RepoName = "holon-toolbox" $Branch = "main" # ============================================================ # The repo zipball is the one download every remote launch waits on. Windows # PowerShell redraws the progress bar per chunk, which costs roughly an order of # magnitude on Invoke-WebRequest -OutFile - and this loader frequently runs under # 5.1, because that is what `irm | iex` lands in on a fresh client. $ProgressPreference = "SilentlyContinue" # ============================================================ # Execution policy # ============================================================ # `irm ... | iex` is NEVER blocked by execution policy - the policy only gates # script *files*. But this launcher runs holon-toolbox.ps1 from disk in a # moment, and a fresh Windows client leaves Windows PowerShell at Restricted, # which fails with "cannot be loaded because running scripts is disabled on # this system". That is why the error appears right after "Ready". # # Process scope only: it lives and dies with this window, needs no admin, and # changes nothing about the machine. Execution policy is documented by # Microsoft as a safety feature, not a security boundary, so this is the # ordinary way to run a downloaded launcher. # # It deliberately does NOT touch Group Policy. A policy-enforced setting is an # administrator's decision and outranks every other scope, so instead of trying # to defeat it we detect that case and print what actually needs to happen. try { Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass -Force -ErrorAction Stop } catch { # Non-fatal: the check below reports whether we are actually able to run. } if ((Get-ExecutionPolicy) -in @('Restricted', 'AllSigned')) { $blockingScope = Get-ExecutionPolicy -List | Where-Object { $_.Scope -in @('MachinePolicy', 'UserPolicy') -and $_.ExecutionPolicy -ne 'Undefined' } | Select-Object -First 1 Write-Host "" Write-Host " [!] Script execution is blocked on this machine." -ForegroundColor Red if ($blockingScope) { Write-Host " Enforced by Group Policy ($($blockingScope.Scope) = $($blockingScope.ExecutionPolicy))," -ForegroundColor Yellow Write-Host " which overrides every per-session setting. Ask whoever manages this" -ForegroundColor Yellow Write-Host " machine to relax it, or run the toolbox on a machine without that policy." -ForegroundColor Yellow } else { Write-Host " Could not switch this session to Bypass. Start a new window with:" -ForegroundColor Yellow Write-Host " powershell -ExecutionPolicy Bypass -NoProfile" -ForegroundColor White Write-Host " then run the irm command again." -ForegroundColor Yellow } Write-Host "" return } # --- Box rendering (inner width = 32, computed padding) --- $boxW = 32 $d = [char]0x2500 $titleText = "$d$d Holon Toolbox " $midText = " Remote Launcher" $hrTop = " $([char]0x250C)$titleText$([string]::new($d, $boxW - $titleText.Length))$([char]0x2510)" $hrMid = " $([char]0x2502)$midText$(' ' * ($boxW - $midText.Length))$([char]0x2502)" $hrBottom = " $([char]0x2514)$([string]::new($d, $boxW))$([char]0x2518)" Write-Host "" Write-Host $hrTop -ForegroundColor Cyan Write-Host $hrMid -ForegroundColor Cyan Write-Host $hrBottom -ForegroundColor Cyan Write-Host "" # --- 1. Password prompt --- $securePass = Read-Host " Unlock password" -AsSecureString $password = [Runtime.InteropServices.Marshal]::PtrToStringAuto( [Runtime.InteropServices.Marshal]::SecureStringToBSTR($securePass) ) if ($password.Length -lt 8) { Write-Host " [!] Invalid password." -ForegroundColor Red return } # --- 2. Decrypt PAT --- Write-Host " Authenticating..." -ForegroundColor DarkGray # Two payload formats, told apart by the "v2:" prefix. # # v2 salt[16] | iv[16] | ciphertext, key = PBKDF2-SHA256(password, salt, 300k) # v1 iv[16] | ciphertext, key = SHA256(password) [legacy] # # v1 is still read so an already-deployed loader keeps working, but it is weak # and should be replaced: this file is public by design (it is served for # `irm | iex`), so its ciphertext is public too, and a single unsalted SHA-256 # costs an attacker one hash per guess offline. Re-run remote\encrypt-pat.ps1 # to produce a v2 blob and redeploy. function Read-HolonPat { [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingPlainTextForPassword', 'Password', Justification = 'Not a credential being collected - it is the unlock passphrase already converted from the SecureString above, because a key derivation function needs the bytes. The caller nulls it immediately after this returns.')] param([string]$Payload, [string]$Password, [ValidateSet("v1", "v2")][string]$Format) try { $raw = if ($Payload.StartsWith("v2:")) { $Payload.Substring(3) } else { $Payload } $combined = [Convert]::FromBase64String($raw) if ($Format -eq "v2") { if ($combined.Length -le 32) { return $null } $salt = $combined[0..15] $iv = $combined[16..31] $cipherText = $combined[32..($combined.Length - 1)] $kdf = New-Object System.Security.Cryptography.Rfc2898DeriveBytes( $Password, $salt, 300000, [System.Security.Cryptography.HashAlgorithmName]::SHA256) $keyBytes = $kdf.GetBytes(32) $kdf.Dispose() } else { $iv = $combined[0..15] $cipherText = $combined[16..($combined.Length - 1)] $sha = [System.Security.Cryptography.SHA256]::Create() $keyBytes = $sha.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($Password)) $sha.Dispose() } $aes = [System.Security.Cryptography.Aes]::Create() $aes.Key = $keyBytes $aes.IV = $iv $aes.Mode = [System.Security.Cryptography.CipherMode]::CBC $aes.Padding = [System.Security.Cryptography.PaddingMode]::PKCS7 $decryptor = $aes.CreateDecryptor() $decrypted = $decryptor.TransformFinalBlock($cipherText, 0, $cipherText.Length) $out = [System.Text.Encoding]::UTF8.GetString($decrypted) $decryptor.Dispose() $aes.Dispose() if ($out.Length -lt 10) { return $null } return $out } catch { return $null } } $declaredFormat = if ($EncryptedPAT.StartsWith("v2:")) { "v2" } else { "v1" } $pat = Read-HolonPat -Payload $EncryptedPAT -Password $password -Format $declaredFormat # If the declared format fails, try the other one before blaming the password. # A dropped "v2:" prefix leaves a perfectly good blob that decrypts with the # wrong KDF, and the only symptom is "wrong password" - indistinguishable from a # typo, and a dead end for whoever is standing at this prompt. Observed # 2026-08-06: three characters lost while pasting the value in. if ($null -eq $pat -and $declaredFormat -eq "v1") { $pat = Read-HolonPat -Payload $EncryptedPAT -Password $password -Format "v2" if ($null -ne $pat) { Write-Host " [!] The stored PAT is a v2 blob but its 'v2:' prefix is missing." -ForegroundColor Yellow Write-Host " Continuing, but fix loader.ps1: `$EncryptedPAT = `"v2:`"" -ForegroundColor Yellow } } $password = $null if ($null -eq $pat) { Write-Host " [!] Wrong password or corrupted data." -ForegroundColor Red return } Write-Host " $([char]0x2714) Authenticated" -ForegroundColor Green # --- 2b. Launch mode + temp base --- # ONE tree, %ProgramData%\Holon-Toolbox (2026-08-13). '.temp' is the part that is # deleted when the session ends; 'Downloads' next to it is not. These two names # must match $global:HOLON_ROOT_FOLDER_NAME / $global:HOLON_TEMP_FOLDER_NAME in # modules\mod-helpers.ps1 - both sides use them to decide what may be deleted. $HolonRootFolderName = "Holon-Toolbox" $HolonTempFolderName = ".temp" function Resolve-HolonTempBase { # Primary C:\ProgramData\Holon-Toolbox\.temp, fallback # %USERPROFILE%\Downloads\Holon-Toolbox\.temp, last resort %TEMP%. $candidates = @( (Join-Path (Join-Path $env:ProgramData $script:HolonRootFolderName) $script:HolonTempFolderName), (Join-Path (Join-Path $env:USERPROFILE "Downloads\$script:HolonRootFolderName") $script:HolonTempFolderName) ) foreach ($base in $candidates) { try { if (-not (Test-Path $base)) { New-Item -Path $base -ItemType Directory -Force -ErrorAction Stop | Out-Null } $probe = Join-Path $base ".write-probe" [System.IO.File]::WriteAllText($probe, "x") Remove-Item $probe -Force -ErrorAction SilentlyContinue return $base } catch { continue } } return $env:TEMP } Write-Host "" Write-Host " [R] Run " -NoNewline -ForegroundColor Cyan; Write-Host "download to temp, run, auto-clean on exit" -ForegroundColor Gray Write-Host " [D] Download " -NoNewline -ForegroundColor Cyan; Write-Host "pick a folder, run from there, auto-clean on exit" -ForegroundColor Gray Write-Host "" $modeChoice = Read-Host " Select mode (R/D) [default R]" if ($modeChoice -match '^[Dd]') { $target = (Read-Host " Target folder (full path)").Trim().Trim('"') if ([string]::IsNullOrWhiteSpace($target)) { Write-Host " [!] No folder given - using Run mode." -ForegroundColor Yellow $baseTemp = Resolve-HolonTempBase } else { try { if (-not (Test-Path $target)) { New-Item -Path $target -ItemType Directory -Force -ErrorAction Stop | Out-Null } $baseTemp = $target } catch { Write-Host " [!] Cannot use that folder ($_). Using Run mode." -ForegroundColor Yellow $baseTemp = Resolve-HolonTempBase } } } else { $baseTemp = Resolve-HolonTempBase } Write-Host " Temp base: $baseTemp" -ForegroundColor DarkGray # --- 3. Download repo as zip --- # All ephemeral files live under $baseTemp; the install goes in an 'Holon-Toolbox' # subfolder that is the ONLY thing we auto-delete (never the user's chosen parent). # 'app', not 'Holon-Toolbox': the base is already ...\Holon-Toolbox\.temp, and a # second folder of the same name inside it is what made the layout unreadable # (C:\ProgramData\.holon-toolbox-temp\Holon-Toolbox\ held the extracted toolbox # while C:\ProgramData\Holon-Toolbox\ was something else entirely). $installDir = Join-Path $baseTemp "app" $zipPath = Join-Path $baseTemp "holon-toolbox-dl.zip" # Export for the toolbox process (in-process launch inherits these). $env:HOLON_TEMP = $baseTemp $env:HOLON_EPHEMERAL_DIR = $installDir # The bundled SZSZC installers (config\install-apps) are NOT in the repo zipball - # they are ~800 MB of third-party binaries, several above GitHub's 100 MB repo file # limit, so they ship as release assets instead. The toolbox fetches the one it # needs on demand, which requires the same token we just decrypted. Session-scoped # env var only: it lives in this process tree and is cleared when the run ends. $env:HOLON_PAT = $pat Write-Host " Downloading toolbox..." -ForegroundColor DarkGray try { $headers = @{ Authorization = "Bearer $pat" Accept = "application/vnd.github+json" "X-GitHub-Api-Version" = "2022-11-28" } $zipUrl = "https://api.github.com/repos/$RepoOwner/$RepoName/zipball/$Branch`?t=$([DateTimeOffset]::UtcNow.ToUnixTimeSeconds())" $pat = $null Invoke-WebRequest -Uri $zipUrl -Headers $headers -OutFile $zipPath -UseBasicParsing } catch { Write-Host " [!] Download failed: $_" -ForegroundColor Red if (Test-Path $zipPath) { Remove-Item $zipPath -Force -ErrorAction SilentlyContinue } return } Write-Host " $([char]0x2714) Downloaded" -ForegroundColor Green # --- 4. Extract to stable short path --- Write-Host " Extracting..." -ForegroundColor DarkGray try { # Extract to temp first $tmpExtract = Join-Path $baseTemp "holon-extract-tmp" if (Test-Path $tmpExtract) { Remove-Item $tmpExtract -Recurse -Force } Expand-Archive -Path $zipPath -DestinationPath $tmpExtract -Force Remove-Item $zipPath -Force -ErrorAction SilentlyContinue # GitHub zip creates a subfolder like "owner-repo-hash/" — find it $innerDir = Get-ChildItem -Path $tmpExtract -Directory | Select-Object -First 1 if (-not $innerDir) { throw "No directory found in archive" } # Copy contents to stable short path (overwrite previous version) if (Test-Path $installDir) { Remove-Item $installDir -Recurse -Force } Copy-Item -Path $innerDir.FullName -Destination $installDir -Recurse -Force # Clean up temp extract Remove-Item $tmpExtract -Recurse -Force -ErrorAction SilentlyContinue # Strip Mark-of-the-Web from everything we just unpacked. # This is a SECOND, independent reason a script can refuse to run: files # that came out of a downloaded archive carry a Zone.Identifier stream # marking them as internet-sourced, and RemoteSigned - the default for # PowerShell 7 - blocks exactly those unless they are signed. So a machine # whose policy looks perfectly fine can still fail without this. Get-ChildItem -LiteralPath $installDir -Recurse -File -ErrorAction SilentlyContinue | Where-Object { $_.Extension -in @('.ps1', '.psm1', '.psd1', '.cmd', '.bat') } | Unblock-File -ErrorAction SilentlyContinue $scriptPath = Join-Path $installDir "holon-toolbox.ps1" if (-not (Test-Path $scriptPath)) { throw "holon-toolbox.ps1 not found at: $installDir" } # Drop the ephemeral marker (holds the temp base) so the toolbox knows it is # loader-managed and self-cleans — this survives elevation relaunches. [System.IO.File]::WriteAllText((Join-Path $installDir ".holon-ephemeral"), $baseTemp, [System.Text.Encoding]::UTF8) } catch { Write-Host " [!] Extraction failed: $_" -ForegroundColor Red return } Write-Host " $([char]0x2714) Ready" -ForegroundColor Green Write-Host "" # --- 5. Run the toolbox --- try { & $scriptPath } catch { Write-Host " [!] Toolbox error: $_" -ForegroundColor Red } # --- 6. Cleanup & Done --- # Drop the session token first - nothing after this point needs it. $env:HOLON_PAT = $null # The toolbox has exited (either directly or via admin relaunch). Backstop cleanup — # the toolbox self-cleans on a clean exit; this covers abrupt closes. $relaunchFile = Join-Path $baseTemp "holon-relaunch.ps1" $wtLaunchFile = Join-Path $baseTemp "holon-wt-admin.ps1" # Only remove the WHOLE base if it is OUR managed '.holon-toolbox-temp' folder — # never a user-chosen Download folder (Download mode: base = the user's folder, so # we only ever delete the 'Holon-Toolbox' subfolder inside it). # Both halves: '.temp' UNDER 'Holon-Toolbox'. In Download mode the base is a # folder the USER chose, and removing that would be catastrophic. $baseIsManaged = ((Split-Path $baseTemp -Leaf) -eq $HolonTempFolderName -and (Split-Path (Split-Path $baseTemp -Parent) -Leaf) -eq $HolonRootFolderName) $escapedInstall = $installDir -replace "'", "''" $escapedBase = $baseTemp -replace "'", "''" $baseRemovalLine = if ($baseIsManaged) { "Remove-Item -Path '$escapedBase' -Recurse -Force -ErrorAction SilentlyContinue" } else { "" } if (Test-Path $relaunchFile) { # Admin relaunch happened — the elevated session is still running from $installDir. # DON'T delete now. Wait until the relaunch file is gone (elevated session cleans it) # or a 2-hour timeout, THEN remove the install dir + launchers + managed base. try { $shell = (Get-Process -Id $PID).Path $escapedRelaunch = $relaunchFile -replace "'", "''" $escapedWt = $wtLaunchFile -replace "'", "''" $cleanupScript = @" `$timeout = (Get-Date).AddHours(2) while ((Test-Path '$escapedRelaunch') -and (Get-Date) -lt `$timeout) { Start-Sleep -Seconds 5 } Start-Sleep -Seconds 3 Remove-Item -Path '$escapedInstall' -Recurse -Force -ErrorAction SilentlyContinue Remove-Item -Path '$escapedRelaunch' -Force -ErrorAction SilentlyContinue Remove-Item -Path '$escapedWt' -Force -ErrorAction SilentlyContinue $baseRemovalLine Remove-Item -LiteralPath `$PSCommandPath -Force -ErrorAction SilentlyContinue "@ # Live OUTSIDE the base so recursive base removal can complete. $cleanupScriptPath = Join-Path $env:TEMP "holon-deferred-cleanup.ps1" [System.IO.File]::WriteAllText($cleanupScriptPath, $cleanupScript, [System.Text.Encoding]::UTF8) Start-Process $shell -ArgumentList "-NoProfile -WindowStyle Hidden -File `"$cleanupScriptPath`"" -WorkingDirectory $env:SystemRoot -ErrorAction SilentlyContinue } catch { } } else { # Normal exit (no relaunch) — schedule immediate removal of install dir + managed base. try { $shell = (Get-Process -Id $PID).Path $cleanupCmd = "Start-Sleep -Seconds 2; Remove-Item -Path '$escapedInstall' -Recurse -Force -ErrorAction SilentlyContinue" if ($baseIsManaged) { $cleanupCmd += "; $baseRemovalLine" } Start-Process $shell -ArgumentList "-NoProfile -WindowStyle Hidden -Command `"$cleanupCmd`"" -WorkingDirectory $env:SystemRoot -ErrorAction SilentlyContinue } catch { } } # Clean download zip if still around (best-effort; managed base removal covers the rest) $dlZip = Join-Path $baseTemp "holon-toolbox-dl.zip" if (Test-Path $dlZip) { Remove-Item $dlZip -Force -ErrorAction SilentlyContinue } Write-Host "" Write-Host " $([char]0x2714) Session ended." -ForegroundColor Green