> ## Documentation Index
> Fetch the complete documentation index at: https://usenaive.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Tutorial — boot a Tower on a local VM

> Build the x86_64 golden image, boot it under QEMU + swtpm, watch firstboot seal /data to a software TPM, claim it against a local control plane, and run the demo.

This is the real artifact: a bootable disk image that comes up as a configured
Naive Tower. Locally we boot it under QEMU with a **software TPM** (`swtpm`), so
`systemd-cryptenroll` PCR-7 sealing and the vault unlock run exactly as they
will on NitroTPM or bare metal.

## Prerequisites

* `qemu-system-x86_64`, `swtpm`, `packer`, OVMF (UEFI firmware).
* KVM makes this fast; on macOS it runs under QEMU **TCG** (works, slow).

## 1. Build the golden image

```sh theme={"theme":"css-variables"}
cd image
./build-tarballs.sh                     # docker save the preloaded images
OVMF_CODE=/path/to/OVMF_CODE.fd PACKER_ACCELERATOR=kvm packer build tower.pkr.hcl
```

Packer runs the Ubuntu 24.04 autoinstall (`ds=nocloud-net`), lays down the GPT
`ESP / rootA / rootB / /data` layout, installs Docker + gVisor (`runsc`),
Tailscale (off), the preloaded image tarballs, and the firstboot units. Output:
`image/output/naive-tower.raw`.

## 2. Boot it

```sh theme={"theme":"css-variables"}
scripts/run-vm.sh image/output/naive-tower.raw
```

On **first boot** you'll watch:

1. **`/data` sealed to the TPM** — `systemd-cryptenroll --tpm2-pcrs=7` creates a
   LUKS2 volume bound to the vTPM and prints a one-time **owner recovery key**
   (store it). Subsequent boots auto-unlock; no passphrase.
2. **The region comes up** — `naive-region.service` runs `docker compose up`
   with `vaultd` (reveal compiled out), the governor, api, gateway, and the
   `runsc`-sandboxed agent runtime.
3. **A claim QR + code** rendered to the console by the device-agent.

`curl http://localhost:3000/health` returns 200 once `/data` is unlocked and the
region is live.

## 3. Claim it against a local control plane

Run the CP (the API in cloud mode) on your workstation:

```sh theme={"theme":"css-variables"}
pnpm cp:dev          # pg + governor + api on :3111
```

Scan the QR (or open the claim URL) and claim as the owner. KYC runs via
Footprint; without a sandbox key set `NAIVE_DEV_KYC_BYPASS=1` on the CP — a
**loud, logged, dev-only** bypass that is refused in Tower/prod builds (the KYC
leg is otherwise [BLOCKED](/docs/self-hosting/claim-and-provisioning#kyc)). The device
polls, attests (PCR-7 quote; CP verification is beta), provisions, and comes up
live in **sandbox**.

## 4. Prove it

```sh theme={"theme":"css-variables"}
scripts/demo.sh          # targets the VM's forwarded :3000 / :8443
```
