Provisioning and saving a Windows WSL2 Test Machine
Follow setup responsibilities and the project-owned manual preparation inventory in the provisioning index.
Follow this ordered procedure from a bare Windows host through a proved saved Linux home. Yoke runs inside WSL2 Ubuntu, not native Windows. The registered SSH account is a Windows user; its default Ubuntu user is dedicated/non-root. Infrastructure, account names, endpoints, passwords and paths are project-owned.
1. Infrastructure and Windows account
Declare infrastructure in the project's IaC, or record the operator's explicit
exception before provision. For a cloud host, use an image/instance supporting
Windows and WSL2 nested virtualization; declare the provider's nested-virtualization option and validate it on the host. Do not infer support from an
instance family name. Apply the project's cost gate and query current costs.
Record the instance's cloud_instance_id (section 3): a lease then starts the
host through the project's aws-admin key and waits for SSH, and the end of
its last lease stops it, so it runs only while QA holds it. Never start it
by hand to satisfy a documentation audit without authorization.
Create/use the dedicated Windows SSH account. WSL distributions are per Windows
user: installing under a service or another administrator account does not
prepare the test account. Separate operator users/homes remain outside capture,
restore, inspection and logout. Restrict SSH and RDP ingress to approved
execution machines or private-network routes.
An automatically assigned public IPv4 changes on stop/start, so Yoke never
stops a cloud host without an Elastic IP (test_machine_power_stop_skipped).
An operator-approved Elastic IP is stable but continues charging while
stopped; it belongs in infrastructure declarations.
2. Windows SSH, registration and desktop secrets
Install the Windows OpenSSH Server feature and configure sshd to start
automatically. Authorize the project's shared test-machine public key;
administrator accounts use
%ProgramData%\ssh\administrators_authorized_keys, restricted to
Administrators/SYSTEM. Disable password SSH authentication and verify the
effective config. Keep private keys out of user data, settings and evidence.
Check Windows network profile and firewall together: the default OpenSSH rule can be Private-only while the cloud interface is Public. Scope the rule to the applicable profile while retaining restricted source access; do not open the network broadly as a connectivity workaround.
Register test-machine:<name> through yoke test-machine settings-replace --project P --test-machine NAME --settings-file FILE --new; read --help for
updates with the as-read settings token. Declare resource_name, os=windows,
Windows host and user, and an absolute Linux golden_baseline_path
outside the WSL home. Desktop fields declare desktop_protocol=rdp,
desktop_route=ssh-forward (or an approved direct route), the provisioned
WSL xrdp port (default3390), and the dedicated WSL desktop_user. Optional cloud_instance_id (the
EC2 instance id; leases power the host by it) and operating notes record local
infrastructure facts. Capacity registration is independent.
On every QA execution workstation, store capability-owned credentials:
yoke projects capability secret set --project P --cap-type test-machine --key ssh_private_key --value-file KEY_FILE
yoke projects capability secret set --project P --cap-type test-machine:NAME --key desktop_password --value-file PASSWORD_FILE
yoke test-machine get --project P --test-machine NAME
Read each command's --help. Desktop and SSH usernames may differ; their
credentials are independent. No password/key bytes enter argv, logs, source,
artifacts or control-plane settings. A host leased by another session is a
wait, not permission to disturb it.
3. WSL2 Ubuntu and systemd
Follow Microsoft's WSL installation
for the selected Windows release. In elevated PowerShell enable
Microsoft-Windows-Subsystem-Linux and VirtualMachinePlatform, install WSL
(wsl --install --no-distribution --web-download for this documented path),
and reboot when required. Under the test Windows account install Ubuntu24.04,
select it as the default distribution, and prove version 2 with
wsl --list --verbose.
If WSL under a service context reports absent, verify the account identity;
use Microsoft's published offline MSI only with its published integrity proof.
Create the dedicated non-root Linux user and set /etc/wsl.conf:
[boot]
systemd=true
[user]
default=TESTUSER
Save work in all distributions, run wsl --shutdown from Windows, then reopen
Ubuntu. This stops every running WSL distribution, not just this fixture.
Prove the default Linux id -u is nonzero and PID1 is systemd. Registered host
operations enter wsl.exe --cd ~ -e /bin/bash -lc; home/baseline paths are Linux
paths and the operation preserves the Linux program's exit status.
Confirm from the workstation with yoke test-machine exec --project P --test-machine NAME -- 'id -u; uname -a'; read --help. Ad hoc exec uses its workstation SSH
agent and refuses another session's lease.
4. Linux tools and privileged prerequisites
Inside that Ubuntu home, an administrator installs:
sudo apt-get update
sudo apt-get install -y ca-certificates curl git tmux python3 python3-venv nodejs npm
sudo install -d -m 0700 -o TESTUSER -g TESTUSER /var/lib/yoke-golden/TESTUSER
Complete the project's manual runtime preparation inside WSL; if it declares
Docker, install/verify Compose v2, daemon/service access and real workloads, then
reopen the session after group changes. Give the dedicated test user
passwordless sudo for automatic Chromium library setup, through a validated
0440 /etc/sudoers.d/yoke-test-user drop-in containing
TESTUSER ALL=(ALL) NOPASSWD: ALL. Administrator runs visudo -c; the user
proves sudo -n true plus the project-declared runtime workload checks.
Runtime group membership is not the privileged-library prerequisite.
Require Ubuntu24.04, Git/tmux/Python versions, non-root ownership and supported
vendor Node/runtime versions. Packages/services are outside the saved home.
Keep repositories in the Linux filesystem; native Windows Yoke and Windows-side
harness desktop reach-in are not covered by this fixture.
5. Harness sign-in and standard acceptance
Install vendor CLIs as the Linux test user, with .local/bin on its login PATH:
curl -fsSL https://claude.ai/install.sh -o /tmp/claude-install
bash /tmp/claude-install
curl -fsSL https://chatgpt.com/codex/install.sh -o /tmp/codex-install
sh /tmp/codex-install
curl -fsSL https://cursor.com/install -o /tmp/cursor-install
bash /tmp/cursor-install
Confirm actual executable paths and versions. Golden capture records each CLI's version beside the golden in
GOLDEN.harness-clis. Before a QA plan's first reset on this host, Yoke
compares those versions with each vendor's latest release; when one is newer
it resets, installs the newer CLIs with each vendor's installer as the test user and
captures a new golden under the plan's own host lease. Do not hand-update
CLIs for QA; a failed refresh stops the plan with the step to repair.
Read current
Claude requirements,
Codex installation, and
Cursor installation.
Installation/sign-in take place in the WSL test user's terminal, not another
Windows account or native app credential store.
Follow the browser identity procedure
for native login. Use Claude's documented login flow; Codex
codex login --device-auth where account/workspace policy allows it;
Cursor NO_OPEN_BROWSER=1 agent login and its printed URL
(authentication).
Once per fixture,
accepts Claude's dangerous-mode prompt; require
skipDangerousModePermissionPrompt=true before capture.
Each harness must answer a real request without tool calls. Login status or a
present credential file cannot establish it; Cursor status may exit zero while
signed out. Standard Windows/WSL capture checks cover native requests and
bypass acceptance. A custom probes file must include all standard checks and
can add Docker/service/file assertions with absolute argv and positive output
expectations. Standard names are Claude real request, Codex real request,
Cursor real request, and Claude bypass accepted; missing names refuse as
baseline_standard_probes_missing. Capture seals canonical standard programs
plus extras, never implicitly carrying an old sidecar forward. Read the
capture command's served --help. Harness output stays on the host.
macOS Keychain checks do not apply to WSL.
6. Windows desktop and browser coverage
Registered terminal QA uses tmux transcripts. GUI commands and
yoke test-machine screenshot --project P --test-machine NAME --json start or
reuse the same WSL XFCE display through the existing Linux desktop helper;
the screenshot captures that display, including its actual headed browser.
Declare the dedicated WSL user as desktop_user and its loopback xrdp port
as desktop_port; the Windows SSH user remains the account owning the distro.
The capability-owned fixture password is streamed on private stdin. Another
user's ambiguous desktop refuses; no native Windows RDP login is needed.
For a requested, announced personal application sign-in handoff, run:
yoke test-machine desktop-access --project P --test-machine NAME --view
Read --help. Install FreeRDP's visible SDL client on the executing workstation
(macOS: brew install freerdp; Linux: the distribution's freerdp-sdl package).
The product opens the SSH forward and supplies the fixture credential on stdin,
with the existing XFCE dimensions and color depth so xrdp reconnects that display.
It creates no password file. Close the viewer or interrupt the retained command
to close its forward. The user types only their own password or credential.
Tests use agent captures; human viewing is never a test acceptance check.
A connected viewer counts as human adoption: baseline reset/capture must wait
until it closes, even when automation originally started XFCE.
For the selected Server route, reuse Yoke's Linux XFCE/xrdp provisioner
inside WSL with a separate loopback port. The executing operation runs its
stdlib-only helper in the dedicated Linux user's home. Copy both
provision_linux_desktop.py and provision_windows_wsl_desktop.py from the
controller's yoke_harness/host_provisioning/ package directory into that
working directory first:
python3 provision_windows_wsl_desktop.py --desktop-password-stdin
python3 provision_windows_wsl_desktop.py --verify
The wrapper requires Ubuntu24.04, non-root passwordless sudo, WSL2/systemd and
Windows interop, with WSL3.0.1 or newer (the verified configuration support floor).
It records actual Windows edition/build and WSL version,
installs the existing Linux desktop packages, including the Chromium snap as
the default browser that browser approval gates
drive, and proves loopback/localhost access.
The product streams its capability-owned desktop secret on private stdin, sets
the dedicated WSL fixture password with sudo -n chpasswd. The next retained
GUI-session operation uses the existing uploaded xrdp-sesrun helper; no host
harness-package import is needed. No secret enters a file, argv or output. The
operator never types sudo, sets a machine password or logs into either desktop.
Agents prove candidate Chromium renders there before requesting only personal
application sign-in. Success leaves headed_application_proved=false until that
independent check. Provisioning starts no session; --verify refuses the secret flag.
No Windows firewall, auto-logon or public listener change is made.
The provisioner also owns persistent distro setup: it sets only
[general] instanceIdleTimeout=-1 in the registered Windows user's .wslconfig,
preserving other keys and comments. This applies to that Windows user's WSL2
distros. A changed configuration reports restart_required=true; save work and
restart WSL before claiming it is applied. --verify checks the stored setting,
not its activation or desktop persistence. Prove XFCE and candidate Chromium
survive a gap longer than both default timers without a foreground keeper.
Microsoft's settings reference
distinguishes distro idle shutdown (15000ms) from VM idle shutdown (60000ms).
Systemd services alone do not keep WSL alive; no per-execution sleep is required.
Microsoft documents localhost access to WSL services
and systemd prerequisites.
WSLg is a separate route: its documented GUI prerequisites
name Windows10/11. A version string alone proves no Server GUI support.
Live browser identities inside WSL
Follow WSL browser identities before saving.
7. Save and prove the Linux home
For a first bare Linux home, keep Yoke/uv state absent before capture. With an existing golden, reset before refreshing signed-in fixture state and re-save. Choose a new absolute Linux destination outside the Linux home; never reuse a Windows path or overwrite a saved baseline:
yoke test-machine golden-capture --project P --test-machine NAME --destination /var/lib/yoke-golden-DATE-UNIQUE/home
yoke test-machine verify --project P --test-machine NAME
yoke test-machine reset --project P --test-machine NAME --baseline fresh-host
yoke test-machine get --project P --test-machine NAME
Read each command's --help. Capture runs standard probes before registering
the new golden. Failure leaves the current path unchanged. Its private Linux
home.tar.gz, manifest.json and sealed probes bind UID/home/archive digest.
Sockets and unsafe links are omitted; regular .sock files remain. Live Linux
SSH material and the browser identity store .yoke-browser-identities are
preserved. Windows settings/OpenSSH/WSL registration and OS
packages survive the Linux home reset and require independent assertions.
Verify is destructive: it reaches both baselines and ends with Yoke
installed in shell-preconfigured. The final reset restores only the clean
signed-in fresh-host. It validates the seal, stops owned services/home
writers, restores files/modes, proves product residue absent on Linux shells,
and reruns declared probes. The Linux live sign-in rule applies in WSL: the
Claude, Codex and Cursor login files never enter a golden and stay live through
every reset. It never changes Windows credentials.
Require new passing receipts and actual harness requests,
plus independent Windows desktop proof. No such new roundtrip was run here.
8. Recovery and cost-bound handoff
Read every refusal's cause/reason/recovery before retrying. An occupied golden needs a new destination; a foreign-owned entry needs operator repair. Failed requests require sign-in or their named network/timeout remedy. A live human desktop or mounted drive that blocks Linux clear must be logged out/unmounted through its owner, not forcibly killed. A surviving home writer or failed clear must be resolved before retrying the existing sealed baseline.
Linux capture/restore permits20minutes. A timeout/disconnected SSH does not
prove the remote process stopped: wait for or stop that exact process before
retry. A partially restored home is never a new baseline, and an unsealed
archive/edited manifest cannot earn proof. Never export auth material to repair
a check. When a sign-in expires, agents rerun the login, the operator types only
their password, and agents save it at once
with yoke test-machine golden-capture, which proves the real requests and
seals a new clean golden.
After restart, the local universe's documented startup path restarts its authority.
Record the exact saved-state proof. A host with cloud_instance_id stops when
its last lease ends; stop any other cloud compute outside the authorized test
window. A stopped host's old receipt cannot establish today's readiness.