<# The Lyriks bootstrap for Windows, served at https://get.lyriks.io/windows (aliases /ps and /install.ps1). Run it from PowerShell: irm https://get.lyriks.io/windows | iex Parameters survive the pipe as environment variables: $env:LYRIKS_ADMIN_EMAIL = 'you@example.com'; $env:LYRIKS_LICENSE_KEY = 'lyk_...'; irm https://get.lyriks.io/windows | iex or use the scriptblock form to pass them explicitly: & ([scriptblock]::Create((irm https://get.lyriks.io/windows))) -LicenseKey lyk_... Why this script exists: Lyriks runs as linux/amd64 containers, and Windows runs those inside WSL2 (Docker Desktop's own engine lives there too). There is no WSL-free path; what CAN go away is the user ever touching WSL. This script gets WSL2 and Docker Desktop into place from PowerShell, then hands over to the Linux bootstrap inside the default WSL distribution. The appliance ends up in the distro at ~/lyriks; the app is used from the Windows browser at http://localhost:. Contract, same as the sh side: everything is CHECKED silently, nothing is INSTALLED silently. Every missing piece is proposed and needs a yes (-Yes consents to all). Re-running is safe: every step reconciles. #> param( [ValidateSet('community', 'enterprise')] [string]$Edition = $(if ($env:LYRIKS_EDITION) { $env:LYRIKS_EDITION } else { 'community' }), [string]$AdminEmail = $env:LYRIKS_ADMIN_EMAIL, [string]$LicenseKey = $env:LYRIKS_LICENSE_KEY, [int]$Port = $(if ($env:LYRIKS_PORT) { [int]$env:LYRIKS_PORT } else { 3000 }), [switch]$Yes ) $ErrorActionPreference = 'Stop' function Say([string]$m) { Write-Host "> $m" -ForegroundColor Cyan } function Ok([string]$m) { Write-Host "OK $m" -ForegroundColor Green } function Fail([string]$m) { Write-Host "ERROR: $m" -ForegroundColor Red; exit 1 } function Confirm-Step([string]$question, [string]$refused) { if ($Yes) { Say "$question (consented via -Yes)"; return } $answer = Read-Host "$question [Y/n]" if ($answer -match '^[nN]') { Fail $refused } } # ── 1 · machine ───────────────────────────────────────────────────────────── if ($env:PROCESSOR_ARCHITECTURE -ne 'AMD64') { Fail "Lyriks ships linux/amd64 images and this machine is $($env:PROCESSOR_ARCHITECTURE); Windows on ARM is not supported" } Ok 'Machine: x86-64 on Windows' if ($LicenseKey -and $LicenseKey -notlike 'lyk_*') { Fail 'The licence key should start with lyk_ (it is in your install pack)' } # ── 2 · WSL2 ──────────────────────────────────────────────────────────────── if (-not (Get-Command wsl.exe -ErrorAction SilentlyContinue)) { Confirm-Step 'WSL is not installed. Install it now (wsl --install, needs administrator + one reboot)?' 'WSL is required' $principal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) if (-not $principal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { Fail 'Installing WSL needs an administrator PowerShell: reopen PowerShell as administrator, run "wsl --install", reboot, then re-run this command' } wsl.exe --install Say 'WSL is installing. Reboot when asked, let Ubuntu finish its first-run user setup, then re-run this command.' exit 0 } # A present wsl.exe does not mean a usable distribution: `wsl -e true` proves # there is a default distro that actually starts. wsl.exe -e true 2>$null if ($LASTEXITCODE -ne 0) { Confirm-Step 'WSL has no working distribution. Install Ubuntu now (wsl --install -d Ubuntu)?' 'A WSL distribution is required' wsl.exe --install -d Ubuntu Say 'Let Ubuntu finish its first-run user setup (it asks for a username and password), then re-run this command.' exit 0 } # Idempotent and silent when already set: new distros must land on WSL2, # because WSL1 has no real kernel and can never run Docker. wsl.exe --set-default-version 2 *> $null Ok 'WSL2 distribution available' # ── 3 · Docker Desktop ────────────────────────────────────────────────────── if (-not (Get-Command docker -ErrorAction SilentlyContinue)) { if (Get-Command winget -ErrorAction SilentlyContinue) { Confirm-Step 'Docker Desktop is missing. Install it now (winget install Docker.DockerDesktop)?' 'Docker Desktop is required' winget install -e --id Docker.DockerDesktop --accept-package-agreements --accept-source-agreements Say 'Docker Desktop is installed. Start it from the Start menu, accept its first-run dialog, wait for the whale icon to settle, then re-run this command.' } else { Say 'Docker Desktop is missing and winget is not available.' Say 'Install it from https://docs.docker.com/desktop/ then re-run this command.' } exit 0 } docker info *> $null if ($LASTEXITCODE -ne 0) { $desktop = Join-Path $env:ProgramFiles 'Docker\Docker\Docker Desktop.exe' if (Test-Path $desktop) { Confirm-Step 'Docker Desktop is not running. Start it now?' 'Docker Desktop must be running' Start-Process $desktop Say 'Waiting for Docker Desktop (a first launch shows a dialog to accept) ...' $waited = 0 while ($true) { docker info *> $null if ($LASTEXITCODE -eq 0) { break } if ($waited -ge 120) { Fail 'Docker Desktop did not come up in 120s: finish its first-run dialog, then re-run this command' } Start-Sleep -Seconds 3; $waited += 3 } } else { Fail 'Docker is on PATH but its daemon is not reachable: start Docker Desktop, then re-run this command' } } Ok 'Docker daemon reachable' # ── 4 · Docker visible from inside WSL ────────────────────────────────────── # The kit runs inside the distro, so the docker CLI must exist THERE. With # Docker Desktop that is one checkbox, and there is no supported CLI switch # for it: pointing the user at the exact setting is the entire fix. wsl.exe -e docker version *> $null if ($LASTEXITCODE -ne 0) { Fail 'Docker Desktop is running but not wired into your WSL distribution: Docker Desktop > Settings > Resources > WSL integration > enable your distro > Apply & restart, then re-run this command' } Ok 'Docker reachable from inside WSL' # ── 5 · hand over to the Linux bootstrap inside WSL ───────────────────────── # Every edition creates the operator account at install (the kit refuses an # install nobody can sign in to), so the email is required here too. if (-not $AdminEmail) { if ($Yes) { Fail 'Pass -AdminEmail (or $env:LYRIKS_ADMIN_EMAIL): it creates the operator account this install signs in with' } $AdminEmail = Read-Host 'Admin email (the operator account created at install)' if (-not $AdminEmail) { Fail 'The admin email is required: it creates the operator account this install signs in with' } } if (-not $LicenseKey -and -not $Yes) { $LicenseKey = Read-Host 'Licence key (lyk_..., Enter to skip and activate later at /activate)' if ($LicenseKey -and $LicenseKey -notlike 'lyk_*') { Fail 'That does not look like a licence key (lyk_...)' } } wsl.exe -e bash -lc 'command -v curl' *> $null if ($LASTEXITCODE -ne 0) { Confirm-Step 'curl is missing inside the WSL distribution. Install it (sudo apt-get install curl)?' 'curl is required inside WSL' wsl.exe -e bash -lc 'sudo apt-get update && sudo apt-get install -y curl' if ($LASTEXITCODE -ne 0) { Fail 'Could not install curl inside WSL: install it in the distro, then re-run this command' } } # --from-windows: the AI-client step must happen on the OS the AI client runs # on. This script does it below against the WINDOWS Claude; the flag stops the # Linux side from suggesting the same command inside WSL, where it would # configure a WSL Claude the user does not use. $sh = "curl -fsSL https://get.lyriks.io | sh -s -- $Edition --port $Port --yes --from-windows --admin-email $AdminEmail" if ($LicenseKey) { $sh += " --license-key $LicenseKey" } Say 'Handing over to the Linux bootstrap inside WSL ...' wsl.exe -e bash -lc $sh if ($LASTEXITCODE -ne 0) { Fail 'The install did not finish; the messages above say where it stopped. Fix that and re-run this command.' } # ── 6 · optional: wire the Windows AI client ──────────────────────────────── # Claude is never a dependency: Lyriks is installed and usable at this point # whatever happens below. Detection targets the Windows client on purpose. $mcpUrl = "http://localhost:$Port/mcp" if (Get-Command claude -ErrorAction SilentlyContinue) { $configure = $Yes if (-not $configure) { $answer = Read-Host 'Claude Code detected. Configure Lyriks MCP for Claude Code? [Y/n]' $configure = $answer -notmatch '^[nN]' } if ($configure) { claude mcp add --scope user --transport http lyriks $mcpUrl if ($LASTEXITCODE -eq 0) { Ok 'Lyriks MCP configured for Claude Code' } else { Say "Claude may already have a 'lyriks' server; check with: claude mcp list" } } } else { Say 'Claude Code was not detected. Connect any MCP-compatible client later using:' Say " $mcpUrl" } # ── 7 · what now ──────────────────────────────────────────────────────────── Write-Host '' Write-Host '🚀 Lyriks is ready' -ForegroundColor Green Write-Host "http://localhost:$Port" Write-Host '' Say "Sign in as $AdminEmail with the temporary password printed above (shown once), then choose your own" if (-not $LicenseKey) { Say "Activate: sign in, then paste your licence key at http://localhost:$Port/activate" } Say 'Manage it from PowerShell without opening WSL, for example:' Say ' wsl -e bash -lc "cd ~/lyriks && ./lyriks status"' Say ' wsl -e bash -lc "cd ~/lyriks && ./lyriks update"'