# matrix — guide
> **Class:** `guide` · audience: an engineer running the live multi-OS install matrix. The *what*
> is in [`matrix-design.md`](matrix-design.md); the *why* in [`matrix-reasoning.md`](matrix-reasoning.md).
## Build the probe binaries first
The matrix injects two static-musl binaries into each guest. Build them:
```sh
cargo build --release --target x86_64-unknown-linux-musl \
-p skidbladnir-probe --bin skidbladnir-probe --bin skidbladnir-testsvc
```
## Run the live matrix
It is a heavy, `#[ignore]`d test that boots real KVM guests, so run it explicitly:
```sh
export SKID_ISOS=/path/to/cloud-images # ubuntu-24.04, debian-12, fedora-42, alpine-3.21 qcow2s
export SKID_VMS=/var/tmp/skid-vms # scratch for per-cell VM disks
export WORKSPACE_SKIDBLADNIR=/path/to/warehouse # where rows are sunk
cargo test --features matrix --test install_matrix_live -- --ignored --nocapture
```
Optional knobs: `SKID_ONLY=alpine-3.21` (subset), `SKID_BOOT_TIMEOUT=360` (seconds).
## What a run does, per cell
1. `renderer_for(backend)` — if the backend has no renderer, the cell is `Skipped` (honest).
2. build the payload FAT image (`skidbladnir-probe` + `skidbladnir-testsvc` + the rendered units).
3. boot the KVM guest; cloud-init installs `skidtest`, runs the renderer's enable/start verbs, runs
the probe, and tees the `ProbeReport` to the `org.skidbladnir.report` virtio-serial port.
4. the host polls that serial file for a parseable `ProbeReport` and classifies the cell
(`Passed` / `Unhealthy` / `NoReport` / `Skipped`).
Results sink to `<WORKSPACE_SKIDBLADNIR>/probe_reports.jsonl` and to `airgap_events`
(`op = matrix_probe`).
## Drive the machine layer directly
```rust
# #[cfg(feature = "kvm")] {
use skidbladnir::machine::{MachineController, MachineSpec, NetMode, Readiness, kvm::KvmController};
let ctrl = KvmController::new();
let spec = MachineSpec::vm("probe-vm", disk).net(NetMode::Isolated).readiness(Readiness::CloudInitDone);
let h = ctrl.start(&spec)?;
ctrl.wait_ready(&h, &Readiness::CloudInitDone, std::time::Duration::from_secs(360))?;
let out = ctrl.exec(&h, &["systemctl", "is-active", "skidtest"])?; // over the guest agent
ctrl.stop(&h)?; // idempotent
# }
```
## Gotchas
- The matrix is **server-side and heavy** — it needs qemu, the musl probe binaries, and the base
cloud images. It is never compiled into the lean embedded client.
- `NetMode::Isolated` gives a guest with **no NIC**; the probe still reports back over
virtio-serial, so the assert works with zero egress.
- Base images are booted under `snapshot=on` — the qcow2 you point `SKID_ISOS` at is never mutated.
- The matrix **grows automatically**: as new `renderer_for` backends land, their cells stop being
`Skipped` and start running — no matrix code changes.