# HulyNet for testers on Windows, from https://yl5ako37.nothing.md/ (published there as get.ps1 by # scripts/testers/server-setup.sh: the stable channel at the server's root, the unstable # one under /unstable): downloads the release, checks it, installs it as the service # for the whole computer with this server's configuration, which the archive carries, and, # given the wave's invite code, registers the computer and attaches it to our routers (ADR # 0034, stage 5; ADR 0026): a new computer, one still in a network of its own from before # (it moves), and one attached already (a fresh ticket) alike. Every computer attaches: a # network of one's own per tester ended on 2026-11-10, and the registrar answers 410 to an # older line's registration, which asked for one. In a terminal opened as administrator: # # & ([scriptblock]::Create((irm https://yl5ako37.nothing.md/get.ps1))) [-Name ] # & ([scriptblock]::Create((irm https://yl5ako37.nothing.md/get.ps1))) -Uninstall [-Purge] # # Without a code it only installs. Running it again is safe: an installed computer is # upgraded, and a registered one keeps its name. A computer that is in its network or # attached already keeps its configuration (`attach` changes it, or nothing does), and # whatever goes wrong after the programs are replaced (the registrar's answer is not 200, # `attach` fails) leaves it as it was, saying so. It runs in the tester's own # window, so it never calls exit (that would close the window): a failure is thrown and # said. Written for Windows PowerShell 5.1, ASCII only. param([string]$Invite, [string]$Name, [switch]$Uninstall, [switch]$Purge) function Main { # Cmdlets that must stop it say so (-ErrorAction Stop): with 'Stop' for all, Windows # PowerShell 5.1 would also stop at the first line a native program writes to its # error output. Windows PowerShell 5.1 may offer only old TLS by default; downloads # are slow with its progress bar. [Net.ServicePointManager]::SecurityProtocol = [Net.ServicePointManager]::SecurityProtocol -bor [Net.SecurityProtocolType]::Tls12 $ProgressPreference = 'SilentlyContinue' $programs = Join-Path $env:ProgramFiles 'HulyNet' $cli = Join-Path $programs 'hulyscale.exe' $config = Join-Path (Join-Path $env:ProgramData 'HulyNet') 'hulyscale.toml' $principal = [Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent() if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { throw 'it needs administrator rights: right-click Start, choose Terminal (Admin), and paste the line there' } if ($Purge -and -not $Uninstall) { throw '-Purge goes with -Uninstall' } if ([Environment]::OSVersion.Version.Major -lt 10) { throw 'HulyNet needs Windows 10 or 11' } if ($env:PROCESSOR_ARCHITECTURE -ne 'AMD64') { throw "HulyNet runs on 64-bit PCs (x64) so far, not on $env:PROCESSOR_ARCHITECTURE" } if ($Uninstall) { $installer = Join-Path $programs 'install.ps1' if (-not (Test-Path -LiteralPath $installer)) { throw 'HulyNet is not installed here' } $arguments = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $installer, '-Uninstall') if ($Purge) { $arguments += '-Purge' } & powershell.exe @arguments if ($LASTEXITCODE -ne 0) { throw 'uninstalling failed (see above)' } return } # What the computer is before the programs are replaced: in a network of its own (its # service says so), attached ([access] in its configuration), or neither; and the # router it prefers, which its ticket then names first (`hulyscale exit`: the member or # router it names; "entry" once a tester chose the entry). $was = '' $first = '' if ($Invite -and (Test-Path -LiteralPath $cli)) { if ((Test-Path -LiteralPath $config) -and (Select-String -LiteralPath $config -Pattern '^\[access\]' -Quiet)) { $was = 'attached' } else { $before = & $cli status 2>$null $network = ($before | Select-String -Pattern '^network\s+(\S+)' | Select-Object -First 1).Matches.Groups[1].Value if ($network -and $network -ne 'none') { $was = 'member' } } $preferred = (& $cli exit 2>$null | Select-Object -First 1) if ("$preferred" -match '^\s*(entry|exit)([^a-z0-9-]|$)') { $first = $Matches[1] } } $base = 'https://yl5ako37.nothing.md/downloads' $file = 'hulyscale-windows-x86_64.zip' $tmp = Join-Path $env:TEMP ('hulynet-' + [Guid]::NewGuid().ToString('N')) New-Item -ItemType Directory -Force -Path $tmp -ErrorAction Stop | Out-Null try { Write-Host "Downloading HulyNet ($file)..." try { Invoke-WebRequest -UseBasicParsing -Uri "$base/$file" -OutFile (Join-Path $tmp $file) -ErrorAction Stop $sums = (Invoke-WebRequest -UseBasicParsing -Uri "$base/SHA256SUMS" -ErrorAction Stop).Content } catch { throw 'the download failed: check the connection and try again' } if ($sums -is [byte[]]) { $sums = [Text.Encoding]::ASCII.GetString($sums) } $line = ($sums -split "`n") | Where-Object { $_ -match ('^([0-9a-f]{64}) ' + [regex]::Escape($file) + '\s*$') } | Select-Object -First 1 if (-not $line) { throw 'the download is damaged: try again' } $expected = ($line -split '\s+')[0] $actual = (Get-FileHash -Algorithm SHA256 -LiteralPath (Join-Path $tmp $file) -ErrorAction Stop).Hash.ToLowerInvariant() if ($expected -ne $actual) { throw 'the download is damaged: try again' } Expand-Archive -LiteralPath (Join-Path $tmp $file) -DestinationPath $tmp -Force -ErrorAction Stop $folder = Join-Path $tmp 'hulyscale' Get-ChildItem -LiteralPath $folder | Unblock-File # The archive's configuration is this server's: it replaces an installed one, unless # the computer is in its network or attached: `attach` changes that one, and keeps # it in place if it fails. $arguments = @('-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', (Join-Path $folder 'install.ps1')) if (-not $was) { $arguments += @('-Config', (Join-Path $folder 'hulyscale.toml')) } & powershell.exe @arguments if ($LASTEXITCODE -ne 0) { throw 'installing failed (see above)' } } finally { Remove-Item -LiteralPath $tmp -Recurse -Force -ErrorAction SilentlyContinue } if ($Invite) { Attach-Network $cli $Invite $Name $first $was } } # Registers this computer as one that attaches (ADR 0034) with the invite code, and # attaches it to our routers with the ticket it gets: `hulyscale attach` writes [access] # and restarts the service, and moves a computer out of its own network. Anything but # 200 from the registrar is said as it said it (one line), and nothing more is done. function Attach-Network([string]$cli, [string]$invite, [string]$name, [string]$first, [string]$was) { Write-Host '' $endpoint = $null for ($i = 0; $i -lt 20 -and -not $endpoint; $i++) { $status = & $cli status 2>$null $endpoint = ($status | Select-String -Pattern '^endpoint\s+(\S+)' | Select-Object -First 1).Matches.Groups[1].Value if (-not $endpoint) { Start-Sleep -Seconds 1 } } if (-not $endpoint) { throw 'HulyNet did not start; please send us its log: C:\ProgramData\HulyNet\hulyscaled.log' } # A computer that already sends everything through HulyNet reaches nothing, this # server included, while the service just installed starts: it stays closed until it # is carried again (a few seconds). Up to a minute for the server to answer; after # that the registration itself says what is wrong. for ($i = 0; $i -lt 20; $i++) { try { Invoke-WebRequest -UseBasicParsing -TimeoutSec 2 -Uri 'https://yl5ako37.nothing.md/VERSION' -ErrorAction Stop | Out-Null break } catch { Start-Sleep -Seconds 1 } } Write-Host 'Registering this computer...' $form = @{ invite = $invite; endpoint = $endpoint; access = '1' } if ($name) { $form.name = $name } if ($first) { $form.first = $first } try { $reply = (Invoke-WebRequest -UseBasicParsing -Method Post -Uri 'https://yl5ako37.nothing.md/register' -Body $form -ErrorAction Stop).Content } catch { $response = $_.Exception.Response if (-not $response) { throw 'the server cannot be reached: try again' } $said = (New-Object IO.StreamReader($response.GetResponseStream())).ReadToEnd().Trim() $said = ($said -split "`n" | Select-Object -First 1) if (-not $said) { $said = "the server answered $([int]$response.StatusCode): try again in a minute" } throw $said } if ($reply -is [byte[]]) { $reply = [Text.Encoding]::UTF8.GetString($reply) } $ticket = (($reply -split "`n") | Where-Object { $_ -match '^access ' } | Select-Object -First 1) -replace '^access ', '' $named = (($reply -split "`n") | Where-Object { $_ -match '^name ' } | Select-Object -First 1) -replace '^name ', '' if (-not $ticket) { throw 'the server''s answer has no ticket: try again, or write to us' } # The registrar the daemon asks for its ticket again (ADR 0036, stage 2): this server, # given to a CLI that takes it (an earlier release's, which a way back installs, does # not). $registrar = @() if ((& $cli attach --help 2>$null | Out-String) -match '--registrar') { $registrar = @('--registrar', 'https://yl5ako37.nothing.md') } $said = (& $cli attach $ticket.Trim() @registrar 2>&1 | ForEach-Object { "$_" }) if ($LASTEXITCODE -ne 0) { $last = ($said | Select-Object -Last 1) if ($was -eq 'member') { $kept = 'This computer is still in its own network, as before.' } elseif ($was -eq 'attached') { $kept = 'This computer is still attached, as before.' } else { $kept = 'This computer''s own connection is as it was.' } throw "attaching failed: $("$last".Trim()). $kept Please send us this message and what 'hulyscale report --private' prints." } $attached = $null for ($i = 0; $i -lt 30 -and -not $attached; $i++) { $attached = (& $cli status 2>$null | Select-String -Pattern '^attached\s+to ' | Select-Object -First 1) if (-not $attached) { Start-Sleep -Seconds 1 } } if (-not $attached) { throw 'HulyNet took the ticket but does not say it is attached yet: run "hulyscale status" in a minute; if no line begins "attached", please send us what it prints.' } Write-Host '' Write-Host "Done: this computer is `"$($named.Trim())`" on HulyNet, and its traffic goes through it." Write-Host 'To check: curl.exe https://ifconfig.me' } try { Main } catch { Write-Host "HulyNet: $($_.Exception.Message)" -ForegroundColor Red }