vmbox: assert destruction host-side; README gets the measured constants

The destructive demo asked the guest to confirm its own destruction, one step
after deleting /usr -- which removes bash, sshd and agetty. Nothing would have
been left to answer, so the check would have FAILED on a step that actually
succeeded. It now proves breakage from the host (the overlay grew, and the
guest can no longer answer at all), which is the same evidence the shutdown
verdict relies on and for the same reason: a corpse cannot report its own
death.

Step 3 also no longer recreates the sandbox. `vm reset` in step 4 is the claim
under test; recreating first would have quietly tested something easier.

README's verdict table now carries the MEASURED qmp events per guest command
rather than the inferred ones, and states the finding that motivates the whole
design: ssh returned an identical "Connection closed by remote host" for the
poweroff, the reboot AND the panic.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VdyMBGCrGVGDWtJ9xe48f3
This commit is contained in:
Krzysztof Rudnicki 2026-08-22 21:13:41 +02:00
parent efe77d4454
commit 60571adb40
2 changed files with 31 additions and 10 deletions

View File

@ -47,13 +47,19 @@ is closed, and there is no verdict to read.
### Five outcomes, all host-observable
| Observation | Verdict | Exit |
|---|---|---|
| `SHUTDOWN guest:true` + serial reaches `reboot: Power down` | clean poweroff | 0 |
| `SHUTDOWN guest:true`, serial truncated | **dirty** — stopped mid-sequence | 3 |
| `SHUTDOWN reason=guest-reset` | **rebooted**, did not power off | 4 |
| `GUEST_PANICKED` | **crashed** | 5 |
| no event, liveness probe fails | **hung** | 6 |
Every row below was measured against a real Arch guest, not inferred:
| Guest command | QMP event | Verdict | Exit |
|---|---|---|---|
| `systemctl poweroff` | `SHUTDOWN {guest:true, guest-shutdown}` + serial reaches `reboot: Power down` | clean poweroff | 0 |
| a stop that dies mid-sequence | `SHUTDOWN {guest:true}`, serial truncated | **dirty** | 3 |
| `systemctl reboot` | `SHUTDOWN {guest:true, guest-reset}` | **rebooted**, did not power off | 4 |
| `echo c > /proc/sysrq-trigger` | `GUEST_PANICKED` | **crashed** | 5 |
| a script that wedges | no event, liveness probe fails | **hung** | 6 |
**ssh returned the identical `Connection closed by remote host` for the
poweroff, the reboot AND the panic.** That is the entire argument for reading
the verdict from the host rather than from the connection.
`guest:true` alone is not enough for "shuts down *completely*" — `poweroff -f`
and a script that kills systemd mid-sequence both produce it. The serial tail is

View File

@ -47,11 +47,26 @@ out="$("$VM_CLI" run "$VM" 'cat /etc/os-release | head -1' 2>&1)"
check "guest is Arch Linux" "Arch Linux" "$out"
step "2. DESTROY: wipe /etc and /usr"
overlay="$HOME/.local/share/vmbox/vms/$VM/overlay.qcow2"
size_before="$(stat -c %s "$overlay" 2>/dev/null || echo 0)"
"$VM_CLI" run "$VM" 'sudo rm -rf --no-preserve-root /etc /usr 2>/dev/null; true' 2>&1 | tail -3
step "3. Confirm the guest really is broken"
out="$("$VM_CLI" run "$VM" 'cat /etc/os-release 2>&1 || echo GUEST_IS_BROKEN' 2>&1)"
check "guest is destroyed" "GUEST_IS_BROKEN" "$out"
step "3. Confirm the guest really is broken -- from the HOST"
# Deliberately NOT by asking the guest: rm -rf /usr removed bash, sshd and
# agetty, so nothing is left in there to answer with. A corpse cannot report
# its own death. Two host-side facts prove it instead.
size_after="$(stat -c %s "$overlay" 2>/dev/null || echo 0)"
check "overlay recorded the destruction" "GREW" \
"$([[ "$size_after" -gt "${size_before:-0}" ]] && echo GREW || echo "same:$size_after")"
# The guest can no longer answer at all -- that unreachability IS the proof.
# Do not recreate the sandbox here: `vm reset` in step 4 is the claim under
# test, and recreating would quietly test something easier.
if timeout 150 "$VM_CLI" run "$VM" 'echo alive' >/dev/null 2>&1; then
check "wiped guest is unreachable" "UNREACHABLE" "still answering"
else
check "wiped guest is unreachable" "UNREACHABLE" "UNREACHABLE"
fi
step "4. RESET -- one command"
time_start=$SECONDS