# install.ps1 — one-shot Windows installer for the tunnel client. # # Usage: # iwr -useb https://tnl.voxent.io/install.ps1 | iex # # Pre-set env vars (optional) to skip the prompt: # $env:REMOTETUNNEL_INVITE = "your-invite-code" # $env:REMOTETUNNEL_CLIENT_NAME = "my-laptop" # default: $env:COMPUTERNAME $ErrorActionPreference = "Stop" $Server = "tnl.voxent.io:7443" $Apex = "tnl.voxent.io" $BaseURL = "https://$Apex" $DownloadURL = "$BaseURL/dl/tunnel-windows-amd64.exe" $SignupURL = "$BaseURL/signup" $InstallDir = Join-Path $env:LOCALAPPDATA "remotetunnel" $ExePath = Join-Path $InstallDir "tunnel.exe" function Write-Step($msg) { Write-Host ""; Write-Host "=> $msg" -ForegroundColor Cyan } function Write-Ok($msg) { Write-Host " $msg" -ForegroundColor Green } function Write-Warn($msg) { Write-Host " $msg" -ForegroundColor Yellow } function Write-Bad($msg) { Write-Host " $msg" -ForegroundColor Red } Write-Host "" Write-Host "remotetunnel — Windows installer" -ForegroundColor White Write-Host "----------------------------------" # ---- 1. install dir ------------------------------------------------------ Write-Step "Install directory: $InstallDir" if (-not (Test-Path $InstallDir)) { New-Item -ItemType Directory -Path $InstallDir -Force | Out-Null Write-Ok "created" } else { Write-Ok "exists" } # ---- 2. binary ----------------------------------------------------------- Write-Step "Installing tunnel.exe" $srcLocal = $null if ($PSScriptRoot) { foreach ($c in @((Join-Path $PSScriptRoot "bin\tunnel.exe"), (Join-Path $PSScriptRoot "tunnel.exe"))) { if (Test-Path $c) { $srcLocal = $c; break } } } if ($srcLocal) { Copy-Item -Path $srcLocal -Destination $ExePath -Force Write-Ok "copied from $srcLocal" } else { Write-Ok "downloading $DownloadURL" try { [Net.ServicePointManager]::SecurityProtocol = ` [Net.SecurityProtocolType]::Tls12 -bor ` [Net.SecurityProtocolType]::Tls11 -bor ` [Net.SecurityProtocolType]::Tls Invoke-WebRequest -UseBasicParsing -Uri $DownloadURL -OutFile $ExePath } catch { Write-Bad "download failed: $_" exit 1 } } # ---- 3. PATH ------------------------------------------------------------- Write-Step "Adding $InstallDir to your user PATH" $userPath = [Environment]::GetEnvironmentVariable("PATH", "User") if ($userPath -split ';' -notcontains $InstallDir) { $newPath = if ($userPath) { "$userPath;$InstallDir" } else { $InstallDir } [Environment]::SetEnvironmentVariable("PATH", $newPath, "User") Write-Ok "added (restart your terminal to see it in new windows)" $env:PATH = "$env:PATH;$InstallDir" } else { Write-Ok "already in PATH" } # ---- 4. signup ----------------------------------------------------------- $Name = if ($env:REMOTETUNNEL_CLIENT_NAME) { $env:REMOTETUNNEL_CLIENT_NAME } else { $env:COMPUTERNAME } $Invite = $env:REMOTETUNNEL_INVITE if (-not $Invite) { Write-Step "Signup: enter your invite code" $Invite = Read-Host " Invite code" } $Invite = $Invite.Trim() if (-not $Invite) { Write-Bad "no invite code entered" exit 1 } function Invoke-Signup($n) { $body = @{ name = $n; invite_code = $Invite } | ConvertTo-Json -Compress try { return @{ Status = 200 Body = Invoke-RestMethod -Method POST -Uri $SignupURL ` -ContentType "application/json" -Body $body } } catch { $status = 0 if ($_.Exception.Response) { $status = [int]$_.Exception.Response.StatusCode } return @{ Status = $status; Error = $_.Exception.Message } } } Write-Step "Creating client `"$Name`" via $SignupURL" $resp = Invoke-Signup $Name # 409 = name already taken — retry once with a short suffix. if ($resp.Status -eq 409) { $suffix = ('{0:x4}' -f (Get-Random -Maximum 65535)) $newName = "$Name-$suffix" Write-Warn "name `"$Name`" is taken — retrying as `"$newName`"" $Name = $newName $resp = Invoke-Signup $Name } if ($resp.Status -ne 200 -or -not $resp.Body.token) { switch ($resp.Status) { 401 { Write-Bad "signup failed: invalid invite code" } 409 { Write-Bad "signup failed: this hostname is already registered (set `$env:REMOTETUNNEL_CLIENT_NAME to a unique name)" } 404 { Write-Bad "signup failed: signup is disabled on this server" } 0 { Write-Bad "signup failed: could not reach $SignupURL" } default { Write-Bad "signup failed (HTTP $($resp.Status))" } } exit 1 } Write-Ok "client created as `"$Name`"" $resp = $resp.Body # ---- 5. login ------------------------------------------------------------ Write-Step "Saving credentials" & $ExePath login ` --token $resp.token ` --server $Server ` --apex $Apex ` --client-id $Name if ($LASTEXITCODE -ne 0) { Write-Bad "login failed (exit $LASTEXITCODE)" exit 1 } # ---- 6. done ------------------------------------------------------------- Write-Host "" Write-Host "All set. Try this in a new PowerShell window:" -ForegroundColor Green Write-Host "" Write-Host " python -m http.server 3000" -ForegroundColor White Write-Host " tunnel http 3000" -ForegroundColor White Write-Host "" Write-Host " # … or expose your SSH server (if running)" -ForegroundColor Gray Write-Host " tunnel ssh --port 2222" -ForegroundColor White Write-Host "" Write-Host "The public URL prints in the tunnel client logs." -ForegroundColor Gray Write-Host ""