- Status: implemented; VM-boot acceptance pending on the maintainer env. The mkosi build + QEMU boot are blocked on this contributor box (#189, mkosi-26 / Ubuntu-24.04
PR_CAPBSET_DROPsandbox EPERM), so the lane was authored and statically verified here (bash -nclean; it reuses the C2/#220 boot harness whose 3-boot PASS shape already passed on the maintainer env) but not executed. The acceptance bar issudo make test-cloud-qemugreen on the maintainer env (KVM), per the C2 (#205, cloud-vm-boot-proof.md) and #220 (cloud-seed-delivery.md) precedent. - Date: 2026-06-21
- Specs realized:
TESTING.md(# Full-stack control-plane integration — the cloud lane now drives the first-run wizard to completion, not only the/setupgate),FIRST_RUN.md(# Phase 2/Phase 3 — the trimmed hosted wizard's steps + the first-run-complete marker asserted end-to-end),ENVIRONMENT.md(# Provisioning & first-boot — the seed → admin → served-box arc). No spec file edited (the flows already match as-built); tooling-only beyond this entry.
Closes the cloud track's boot-to-served-dashboard arc (#209/C5), the "boots as a proper hosted OS" bar that C2 (#205, control plane up) + C3a (#206/#220, seed + /setup gate) + C4 (#208, the trimmed wizard) were building toward. C2 + #220 already boot the hosted image, deliver the seed over SMBIOS, and assert the admin-bootstrap gate's four properties; this slice extends the seeded boot past first-admin creation through the rest of the wizard, and the frozen reboot to prove the wizard does not reappear. No new product capability — it proves the ones already built compose into a working box.
Chained on C4 (#208/PR #229), not main. C5 drives C4's POST /api/v1/system/{timezone,telemetry,first-run-complete} endpoints and the extended GET /auth/state, none of which are in main yet, so this branch is cut from the C4 branch (rebased onto current main so it also carries the C2/#220 cloud lane). The PR targets the C4 branch; it retargets to main once C4 merges.
All changes are in dev/cloud/ (tooling) plus this entry — no Go/web-ui change.
The in-VM, serial-driven self-check gains, in the seeded scenario, the wizard-completion block that runs after the existing correct-secret /setup 200 (first admin created):
- PAM
/login— the freshly-created admin authenticates against/etc/shadowvia host-agentverify-password(the real PAM path, distinct from the seed secret), and themalmo_sessioncookie is kept for the admin-gated wizard steps. - Wizard step — time zone —
POST /system/timezone {"timezone":"Europe/Stockholm"}⇒ 200. This is a real pass-through to host-agenttimedatectl set-timezonein the hosted build (a.Timezone = timezone.New()inwiring_hosted.go), so it proves the step works on the real box, not only the fake host-agent. Its status is the first C4-specific signal the lane sees, so the failure message discriminates the two likely setup mistakes (404 ⇒ stale pre-C4 brain image; 502 ⇒ tzdata missing — see below). - Wizard step — telemetry —
POST /system/telemetry {"enabled":false}⇒ 200 (the founding admin's box-level choice;TELEMETRY.md). - Wizard step — Done —
POST /system/first-run-complete(bodyless) ⇒ 200 withfirst_run_complete:true. - Wizard will not reappear — the public
GET /auth/statenow reportsfirst_run_complete:true(the dashboard gates the wizard on this flag, nothas_users). - box-id persisted/surfaced —
GET /me(authenticated) carries the provisionedbox_id, proving it is persisted on the brain identity, not merely echoed by the one-shot/setupresponse.
The frozen reboot scenario additionally asserts first_run_complete survives the power-cycle (the marker written on the seeded boot persists across an actual reboot — the wizard does not reappear after a restart).
Added cookie-aware HTTP helpers in the existing /dev/tcp idiom (no curl/jq in the lean image): session_cookie (extract malmo_session=<tok> from Set-Cookie), auth_post (cookie + JSON or bodyless), auth_get, status_of.
The wizard's time-zone step is a real timedatectl set-timezone, which rejects any zone not present under /usr/share/zoneinfo. systemd only Recommends tzdata and mkosi builds with Install-Recommends=false, so the lean image shipped no zoneinfo — the wizard's time-zone step would 502 on a real hosted box. Driving the wizard end-to-end surfaced this; tzdata is now an explicit dependency. It is not on the appliance cut-list (network-manager/avahi/samba/mergerfs/cryptsetup/tpm2-tools/openssh-server/nftables), so the lean-check still passes.
run-cloud-tests.sh— header + per-boot narration updated to describe the wizard; no driver logic change (the seed delivery + 3-boot overlay + serial verdict grep are reused verbatim from C2/#220). The verdict mechanism (MALMO_CLOUD_ASSERTIONS: PASS) is unchanged.dev/cloud/test/bootstrap.sh—CANARY_VERSIONv14 → v15 to force a clean image rebuild (the bakedcloud-assertions.sh+ thetzdatapackage both changed; rebased over CL6/#236, which had bumped it to v14).- The
make test-cloud-qemutarget (added by C2) already runs the driver; no Makefile change needed.
- VM-boot acceptance not run here. Blocked by #189 on this box; must be run on the maintainer env (
sudo make test-cloud-qemu, KVM) to land green, as C2/#220 were. Until then this is implemented-but-unproven. - The baked brain image must include C4.
dev/cloud/test/bootstrap.shreuses an existing.dev/control-planebundle unlessMALMO_REBUILD_CP=1. A bundle left over from a pre-C4 run carries a brain without the/system/*wizard endpoints, so the first C5 run needssudo -E MALMO_REBUILD_CP=1 make test-cloud-qemu(or a clean.dev/control-plane) to rebake the brain. The time-zone step's404failure message points at this. - No telemetry/timezone read-back beyond the endpoints. The lane asserts the wizard's writes return 200 and that
first_run_complete/box_idare surfaced; it does not separately read/etc/localtimeor the telemetry consent value back (the endpoints' own 200 + the brain unit tests in C4 cover persistence). Adequate for the end-to-end bar; a deeper read-back is possible later if needed. - Single env, no CI. Like the medium lane and the C2/#220 cloud lane, this is not wired into automated CI (blocked by #189 on 24.04 runners).
- Run
sudo make test-cloud-qemuon the maintainer env and record the PASS (the acceptance bar) once C4 (#229) merges and this retargets tomain. - Wire the cloud lane into automated CI (shared blocker #189).
- B-track (#196 B4/B5): the same wizard shell gains the network/WiFi step for bare metal; the bare-metal end-to-end lane (B5) mirrors this one over the swtpm+LUKS rig.