# Clawdie Operator USB Testing Guide

**Branch:** `xfce-operator-usb`
**Target artifact:** `clawdie-quindecim-0.11.0.img`
**Current desktop path:** XFCE via SDDM, interactive login as `clawdie`

---

## Overview

The current branch ships a **live operator USB**.

Testing should answer three questions:

1. Does the built image contain the expected live-session payload?
2. Does it boot to XFCE on representative hardware?
3. Do the operator tools work without `sudo`, using `mdo` where elevation is needed?

The most important runtime path is:

```text
USB boot
  -> rc.conf starts dbus + sddm
  -> /usr/local/etc/rc.d/clawdie_live_gpu runs before sddm
  -> SDDM greeter; operator logs in as clawdie / quindecim
  -> clawdie-xfce session launches XFCE
```

The live operator USB uses SDDM with interactive login. Autologin is
intentionally not part of the current operator-USB plan.

---

## Level 1: Static Artifact Verification

Run this on the FreeBSD build host after `build.sh` finishes.

### What to verify

- `/etc/rc.conf` contains:
  - `hostname="clawdie-live"`
  - `root_rw_mount="YES"`
  - `tmpmfs="NO"`
  - `varmfs="NO"`
  - `dumpdev_enable="NO"`
  - `sshd_enable="YES"`
  - `avahi_daemon_enable="YES"`
  - `pf_enable="YES"`
  - `sddm_enable="YES"`
  - `display_manager="sddm"`
  - `clawdie_live_gpu_enable="YES"`
  - `linux_enable="YES"`
  - `zfs_enable="YES"`
  - `kld_list="linux linux64 zfs"`
- `/etc/fstab` mounts `/dev/ufs/FreeBSD_Install` at `/` with `rw`, not `ro`
- `/etc/fstab` adds tmpfs for `/tmp` and `/var/log`, while `/var/tmp` stays on disk
- `/etc/fstab` contains no swap entries (no USB write wear, predictable behaviour under memory pressure)
- SDDM live config exists, does not configure autologin, and keeps `Numlock=off` for compact laptop keyboards
- base `sshd` includes `/etc/ssh/sshd_config.d/*.conf` and the live drop-in disables password and root login
- `/etc/nsswitch.conf` resolves hosts with `mdns_minimal`
- `/etc/pf.conf` exists and has no `log` rules
- `/usr/local/etc/rc.d/clawdie_live_gpu` exists
- `/usr/local/etc/rc.d/clawdie_live_wifi` exists
- `/usr/local/etc/rc.d/clawdie_live_seed` exists
- `/usr/local/etc/rc.d/clawdie_live_power` exists and applies the `power_profile` C-state policy once at boot
- `/usr/local/etc/rc.d/clawdie_tailscale_up` exists
- `/usr/local/etc/rc.d/clawdie` is absent on baseline live USB builds; `service clawdie` is reserved for deployed disk/server targets
- `/usr/local/etc/polkit-1/rules.d/49-clawdie-power.rules` exists
- XKB files exist, especially `.../xkb/keycodes/xfree86`
- XKB parent directories are traversable and `/var/lib/xkb` is writable for rootless Xorg cache generation
- `/tmp` and `/var` are not hidden by stock installer tmpfs overlays
- `/home/clawdie/.cache` is a symlink to `/tmp/clawdie/cache`
- `networkmgr` is installed without `sudo`
- `avahi-app`, `nss_mdns`, and `mousepad` are installed
- `wifi-firmware-kmod` and `FreeBSD-fwget` are installed
- `webcamd`, `v4l-utils`, `pwcview`, `hw-probe`, and `p5-libwww` are installed
- `gpu-firmware-amd-kmod-renoir` and `gpu-firmware-amd-kmod-green-sardine` are installed (Ryzen iGPU coverage)
- `webcamd` group exists and `clawdie` is a member
- `webcamd_enable="YES"` and `clawdie_live_wifi_enable="YES"` in `/etc/rc.conf`
- `clawdie_live_seed_enable="YES"` and `clawdie_live_power_enable="YES"` in `/etc/rc.conf`
- `kld_list` includes `cuse`, `hidbus`, `iichid`, `hms`, `hmt`, `hkbd`, `acpi_video`, `acpi_asus`, `acpi_asus_wmi`
- `sudo` is not installed in the image
- Image partition table has three slices: `s1` EFI 64M, `s2` FreeBSD, `s3` FAT32 64M labeled `CLAWDIESEED`
- `CLAWDIESEED` slice mounts cleanly as msdosfs and contains `README.txt` describing the seed contract
- `/usr/local/share/wayland-sessions/` contains no `.desktop` files (SDDM must not offer a Wayland session — no working Wayland stack on the live image)
- All installed XFCE panel plugins must have satisfied shared-library dependencies (`ldd` on each `.so` must report zero "not found")
- `gstreamer1-plugins-good` is installed (provides the GStreamer→OSS bridge required by `xfce4-mixer` to detect sound cards on FreeBSD)
- `/usr/local/etc/X11/xorg.conf.d/40-clawdie-noblank.conf` exists and disables Xorg blanking + DPMS at server level (BlankTime/StandbyTime/SuspendTime/OffTime all `"0"`)
- `~clawdie/.xprofile` contains `xset -dpms` and starts `clawdie-noblank-guard.sh` as a session-level belt-and-suspenders fallback
- `/usr/local/bin/clawdie-noblank-guard.sh` and its XFCE autostart desktop file exist; the guard reapplies no-blank/no-DPMS after xfsettingsd or xfce4-power-manager starts
- `xfce4-power-manager.xml` seeds display blanking, DPMS, idle sleep, and lock-on-suspend off for the live user and XDG defaults
- CPU power management:
  - `powerdxx` package is installed (drop-in replacement for base `powerd` with moving-average load sampling)
  - `powerdxx_enable="YES"` and `powerdxx_flags="-a hiadaptive -b adaptive -n adaptive"` in `/etc/rc.conf`
  - `powerd_enable="YES"` MUST NOT be present (both daemons share a pidfile; one would fail to start)
  - `performance_cx_lowest="C3"` and `economy_cx_lowest="C3"` in `/etc/rc.conf` — enables useful idle saving without Cmax/C6-style USB-root resume risk
  - `hw.usb.no_suspend="1"` in `/boot/loader.conf` — avoids USB autosuspend while the root filesystem is on USB
- `/opt/clawdie/npm-global` is recursively owned by `clawdie:clawdie` (build fails loud if not — chown runs at end of `install_live_npm_globals`)
- Agent CLI policy on the live image:
  - `codex` is installed via FreeBSD pkg and the binary is in `/usr/local/bin/codex`
  - `pi` is installed via the bundled npm tarball and symlinked into `/usr/local/bin/pi`
  - `gemini` MUST NOT be present on the live image
  - `claude` MUST NOT be present on the live image (skipped during `install_live_npm_globals`; native deps have no FreeBSD binary, broken CLI is worse UX than no CLI)

### Useful verification commands

```sh
sudo mdconfig -a -t vnode -f tmp/output/clawdie-quindecim-0.11.0.img
sudo mount -o ro /dev/md0s2a /mnt

egrep 'hostname|root_rw_mount|tmpmfs|varmfs|sshd|avahi|powerd|powerdxx|pf_enable|sddm|display_manager|clawdie_live_gpu|clawdie_tailscale_up|linux_enable|zfs_enable|kld_list' /mnt/etc/rc.conf
grep -E '^hostname="clawdie-live"' /mnt/etc/rc.conf
grep -E '^[[:space:]]*(127\.0\.0\.1|::1)[[:space:]].*clawdie-live' /mnt/etc/hosts
grep -E '^tmpmfs="NO"' /mnt/etc/rc.conf
grep -E '^varmfs="NO"' /mnt/etc/rc.conf
grep -E '^[[:space:]]*/dev/ufs/FreeBSD_Install[[:space:]]+/[[:space:]]+ufs[[:space:]]+.*rw' /mnt/etc/fstab
grep -E '^[[:space:]]*tmpfs[[:space:]]+/tmp[[:space:]]+tmpfs' /mnt/etc/fstab
grep -E '^[[:space:]]*tmpfs[[:space:]]+/var/log[[:space:]]+tmpfs' /mnt/etc/fstab
! grep -E '^[[:space:]]*[^#].*[[:space:]]swap[[:space:]]' /mnt/etc/fstab
grep -E '^dumpdev_enable="NO"' /mnt/etc/rc.conf
grep -E '^hosts:.*mdns_minimal.*dns.*mdns' /mnt/etc/nsswitch.conf
cat /mnt/usr/local/etc/sddm.conf.d/50-clawdie-live.conf
grep -E '^Numlock=off' /mnt/usr/local/etc/sddm.conf.d/50-clawdie-live.conf
test ! -e /mnt/home/clawdie/.dmrc
if chroot /mnt /usr/sbin/pw usershow sddm >/dev/null 2>&1; then echo sddm-user-ok; else echo sddm-user-missing; fi
grep -E '^Include /etc/ssh/sshd_config.d/\\*\\.conf' /mnt/etc/ssh/sshd_config
cat /mnt/etc/ssh/sshd_config.d/clawdie-live.conf
grep -E '^pf_enable="YES"' /mnt/etc/rc.conf
! grep -E '^pflog_enable="YES"' /mnt/etc/rc.conf
! grep -E '^pflogd_enable="YES"' /mnt/etc/rc.conf
test -f /mnt/etc/pf.conf
! grep -q '^[^#].*\<log\>' /mnt/etc/pf.conf
test -f /mnt/usr/local/etc/rc.d/clawdie_live_gpu
! grep -q '^i915_.*_load="YES"' /mnt/boot/loader.conf
grep -q 'vendor="0x1002"' /mnt/usr/local/etc/rc.d/clawdie_live_gpu
grep -q 'vendor="0x8086"' /mnt/usr/local/etc/rc.d/clawdie_live_gpu
test -f /mnt/usr/local/etc/rc.d/clawdie_live_wifi
test -f /mnt/usr/local/etc/rc.d/clawdie_live_seed
test -f /mnt/usr/local/etc/rc.d/clawdie_live_power
test -f /mnt/usr/local/etc/rc.d/clawdie_tailscale_up
test ! -f /mnt/usr/local/etc/rc.d/clawdie
test -z "$(ls /mnt/usr/local/share/wayland-sessions/*.desktop 2>/dev/null)"

# XFCE panel plugin shared-library diagnostic. If any plugin .so has
# unresolved deps, the panel will pop a "could not be loaded" dialog.
_panel_missing=0
for _so in /mnt/usr/local/lib/xfce4/panel/plugins/*.so; do
  _missing=$(ldd "$_so" 2>&1 | grep 'not found' || true)
  if [ -n "$_missing" ]; then echo "FAIL: $_so"; echo "$_missing"; _panel_missing=1; fi
done
test "$_panel_missing" -eq 0
pkg -r /mnt info -e gstreamer1-plugins-good
grep -q 'name="plugin-11".*value="mixer"' /mnt/usr/local/etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml
grep -q 'name="plugin-13".*value="clipman"' /mnt/usr/local/etc/xdg/xfce4/xfconf/xfce-perchannel-xml/xfce4-panel.xml

# X server-level no-blank / no-DPMS. Without this the screen blanks
# after 10 min and on some hardware DPMS-wake leaves the session
# hung-looking with an I-beam cursor.
test -f /mnt/usr/local/etc/X11/xorg.conf.d/40-clawdie-noblank.conf
grep -qE 'BlankTime.*"0"' /mnt/usr/local/etc/X11/xorg.conf.d/40-clawdie-noblank.conf
grep -qE 'OffTime.*"0"' /mnt/usr/local/etc/X11/xorg.conf.d/40-clawdie-noblank.conf
grep -q '^xset -dpms' /mnt/home/clawdie/.xprofile
grep -q 'clawdie-noblank-guard.sh' /mnt/home/clawdie/.xprofile
test -x /mnt/usr/local/bin/clawdie-noblank-guard.sh
test -f /mnt/usr/local/etc/xdg/autostart/clawdie-noblank-guard.desktop
test -f /mnt/home/clawdie/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml
grep -q 'name="dpms-enabled".*value="false"' /mnt/home/clawdie/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml
grep -q 'name="blank-on-ac".*value="0"' /mnt/home/clawdie/.config/xfce4/xfconf/xfce-perchannel-xml/xfce4-power-manager.xml

# CPU power management: powerdxx daemon + deep C-states.
pkg -r /mnt info -e powerdxx
grep -E '^powerdxx_enable="YES"' /mnt/etc/rc.conf
grep -E '^powerdxx_flags=' /mnt/etc/rc.conf
! grep -qE '^powerd_enable="YES"' /mnt/etc/rc.conf
grep -E '^performance_cx_lowest="C3"' /mnt/etc/rc.conf
grep -E '^economy_cx_lowest="C3"' /mnt/etc/rc.conf
grep -E '^hw\.usb\.no_suspend="1"' /mnt/boot/loader.conf

# Agent CLI policy: codex via pkg, pi via npm bundle, gemini/claude excluded.
pkg -r /mnt info -e codex
test -x /mnt/usr/local/bin/codex
test -L /mnt/usr/local/bin/pi
test ! -e /mnt/usr/local/bin/gemini
test ! -e /mnt/usr/local/bin/claude
test -z "$(find /mnt/opt/clawdie/npm-global -path '*gemini-cli*' -print -quit)"
test -z "$(find /mnt/opt/clawdie/npm-global -path '*claude-code*' -print -quit)"

# npm-global tree must be clawdie-owned (was root:wheel before 0f142b7 fix).
# Run the ownership checks inside the chroot so the image's passwd/group
# database resolves names, not the build host's UID/GID mapping.
test "$(chroot /mnt /usr/bin/stat -f '%Su:%Sg' /opt/clawdie/npm-global)" = "clawdie:clawdie"
test -z "$(chroot /mnt /usr/bin/find /opt/clawdie/npm-global \( ! -user clawdie -o ! -group clawdie \) -print -quit)"
grep -E '^webcamd_enable="YES"' /mnt/etc/rc.conf
grep -E '^clawdie_live_wifi_enable="YES"' /mnt/etc/rc.conf
grep -E '^clawdie_live_seed_enable="YES"' /mnt/etc/rc.conf
grep -E '^clawdie_live_power_enable="YES"' /mnt/etc/rc.conf

# Seed partition: verify s3 layout, FAT label, and embedded README.txt.
# Run AFTER the s2a mount above; assumes /dev/md0 is the build-host loopback.
gpart show /dev/md0 | grep -E '^[[:space:]]+[0-9]+[[:space:]]+[0-9]+[[:space:]]+3[[:space:]]+(!12|fat32lba|fat32)'
sudo mkdir -p /mnt-seed
sudo mount -t msdosfs -o ro /dev/md0s3 /mnt-seed
test -f /mnt-seed/README.txt
grep -q 'CLAWDIESEED' /mnt-seed/README.txt
sudo umount /mnt-seed && sudo rmdir /mnt-seed
grep -E '^kld_list=.*cuse' /mnt/etc/rc.conf
grep -E '^kld_list=.*hidbus.*iichid.*hms.*hmt.*hkbd' /mnt/etc/rc.conf
grep -E '^kld_list=.*acpi_video.*acpi_asus' /mnt/etc/rc.conf
chroot /mnt /usr/sbin/pw groupshow webcamd | grep -q clawdie
test -f /mnt/usr/local/etc/xdg/autostart/clawdie-bootstrap.desktop
test -f /mnt/usr/local/etc/polkit-1/rules.d/49-clawdie-power.rules
ls -ld /mnt/usr/local/etc /mnt/usr/local/etc/xdg /mnt/usr/local/etc/xdg/xfce4
test -x /mnt/usr/local/etc/xdg/xfce4/xinitrc
test -x /mnt/usr/local/bin/xinit
test -x /mnt/usr/local/bin/startx
test -x /mnt/usr/local/bin/clawdie-startx
test -x /mnt/usr/local/bin/clawdie-gui
test -x /mnt/usr/local/bin/hw-report
test -x /mnt/home/clawdie/.xinitrc
test -x /mnt/home/clawdie/.config/xfce4/xinitrc
pkg -r /mnt info -e xterm
pkg -r /mnt info -e mousepad
pkg -r /mnt info -e dmidecode
pkg -r /mnt info -e pciutils
pkg -r /mnt info -e usbutils
pkg -r /mnt info -e xfce4-desktop
pkg -r /mnt info -e xfdesktop
pkg -r /mnt info -e thunar
pkg -r /mnt info -e gsettings-desktop-schemas
pkg -r /mnt info -e avahi-app
pkg -r /mnt info -e nss_mdns
pkg -r /mnt info -e wifi-firmware-kmod
pkg -r /mnt info -e FreeBSD-fwget
pkg -r /mnt info -e webcamd
pkg -r /mnt info -e v4l-utils
pkg -r /mnt info -e pwcview
pkg -r /mnt info -e hw-probe
pkg -r /mnt info -e p5-libwww
pkg -r /mnt info -e smartmontools
pkg -r /mnt info -e lscpu
pkg -r /mnt info -e lsblk
pkg -r /mnt info -e hwstat
pkg -r /mnt info -e usbhid-dump
pkg -r /mnt info -e libinput
pkg -r /mnt info -e xinput
pkg -r /mnt info -e xrandr
pkg -r /mnt info -e mesa-demos
pkg -r /mnt info -e vulkan-tools
pkg -r /mnt info -e gpu-firmware-amd-kmod-renoir
pkg -r /mnt info -e gpu-firmware-amd-kmod-green-sardine
test -f /mnt/usr/local/share/glib-2.0/schemas/gschemas.compiled
test -f /mnt/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
grep -qi png /mnt/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
grep -E '^security.mac.do.rules=gid=0>uid=0,gid=0,\+gid=\*' /mnt/etc/sysctl.conf
grep -E '^devfs_system_ruleset="clawdie_live"' /mnt/etc/rc.conf
grep -E '^\[clawdie_live=' /mnt/etc/devfs.rules
grep -E '^video:.*clawdie' /mnt/etc/group
test "$(stat -f '%Su:%Sg' /mnt/opt/clawdie/npm-global)" = "clawdie:clawdie"
test -L /mnt/home/clawdie/.cache
readlink /mnt/home/clawdie/.cache
test -f /mnt/home/clawdie/.local/share/applications/mimeapps.list
test -f /mnt/usr/local/share/X11/xkb/keycodes/xfree86
find /mnt/usr/local/share/X11/xkb -type d -maxdepth 1 -exec ls -ld {} +
ls -ld /mnt/tmp /mnt/var/log /mnt/var/tmp /mnt/var/lib/xkb /mnt/var/run/user /mnt/var/run/user/*
grep -E '"tailscale_auth_key_baked": (true|false)' /mnt/usr/local/share/clawdie-iso/build-manifest.json
cat > "$PWD/tmp/xkb-default-test.xkb" <<'EOF'
xkb_keymap {
  xkb_keycodes { include "xfree86+aliases(qwerty)" };
  xkb_types { include "complete" };
  xkb_compat { include "complete" };
  xkb_symbols { include "pc+us+inet(evdev)" };
  xkb_geometry { include "pc(pc105)" };
};
EOF
/mnt/usr/local/bin/xkbcomp -w 0 -R/mnt/usr/local/share/X11/xkb "$PWD/tmp/xkb-default-test.xkb" "$PWD/tmp/xkb-default-test.xkm"
rm -f "$PWD/tmp/xkb-default-test.xkb" "$PWD/tmp/xkb-default-test.xkm"
pkg -r /mnt info -e bash
pkg -r /mnt info -e zsh
pkg -r /mnt info -e ohmyzsh
pkg -r /mnt info -e git
pkg -r /mnt info -e node24
pkg -r /mnt info -e npm-node24
test -x /mnt/usr/local/bin/bash
test -x /mnt/usr/local/bin/zsh
grep -qx /usr/local/bin/bash /mnt/etc/shells
grep -qx /usr/local/bin/zsh /mnt/etc/shells
/usr/sbin/pw -R /mnt usershow clawdie | awk -F: '{print $10}' | grep -qx /usr/local/bin/bash
test -f /mnt/etc/profile.d/clawdie.sh
test -f /mnt/home/clawdie/.bash_profile
test -f /mnt/home/clawdie/.bashrc
test -f /mnt/home/clawdie/.zprofile
test -f /mnt/home/clawdie/.zshrc
grep -q "oh-my-zsh.sh" /mnt/home/clawdie/.zshrc
grep -R "/opt/clawdie/npm-global/bin" /mnt/etc/profile.d/clawdie.sh /mnt/home/clawdie/.profile /mnt/home/clawdie/.bash_profile /mnt/home/clawdie/.bashrc /mnt/home/clawdie/.zprofile /mnt/home/clawdie/.zshrc
grep -q "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" /mnt/etc/profile.d/clawdie.sh
pkg -r /mnt info -e sudo || true
pkg -r /mnt info -d networkmgr
grep -R "mdo -u root networkmgr" /mnt/usr/local/etc /mnt/usr/local/share/applications 2>/dev/null

sudo umount /mnt
sudo mdconfig -d -u md0
```

### Pass criteria

- SDDM live path present
- no live autologin configured in SDDM
- XFCE global and user xinitrc files are executable
- `xinit`/`startx`, `clawdie-startx`, and `clawdie-gui` are present for tty-side graphical rescue testing
- `xterm` is present as a minimal Xorg rescue client
- `mousepad` is present for the seeded text/plain MIME handler
- root filesystem configured read-write in `/etc/fstab`
- `/tmp` and `/var/log` are configured as explicit tmpfs mounts while `/var/tmp` remains on disk
- default XKB keymap compiles against the image xkeyboard-config tree
- XKB cache and temp directories have runtime-safe permissions
- stock installer tmpfs overlays for `/tmp` and `/var` are disabled
- no swap entries in `/etc/fstab`; `dumpdev_enable="NO"` so the boot path does not scan for swap
- base `sshd` is enabled with the restrictive live drop-in and no password/root auth
- mDNS is wired through `avahi-app` + `nss_mdns`
- PF live baseline is present with no logging rules
- `bash`, `zsh`, `ohmyzsh`, and `git` are installed, `/usr/local/bin/bash` and `/usr/local/bin/zsh` are in `/etc/shells`, and `clawdie` uses bash as the login shell
- Clawdie shell profiles put `/opt/clawdie/npm-global/bin` on PATH and repair the standard FreeBSD `/usr/local` + `/usr` command paths for SSH, TTY, terminal, and `su - clawdie`; zsh sources packaged oh-my-zsh when present
- `.cache` is redirected to `/tmp/clawdie/cache`
- GPU pre-SDDM service present
- power-action policy present
- `networkmgr` does not depend on `sudo`
- native Wi-Fi firmware bundle is present
- `sudo` is absent from the live rootfs

---

## Level 2: bhyve Verification (on-demand)

bhyve is an **on-demand** verification lane, not a per-change gate — it's slow and
needs the ML350p, so run it when you want boot/runtime confidence before
committing to physical hardware, not on every iteration. The cheap gates that run
every change are the static checks (`sh -n`, staging self-tests) and `cargo test`.

When you do run it, treat bhyve as authoritative for boot/runtime plumbing and
still treat real hardware as the final authority for GPU, panel, input, Wi-Fi,
and audio polish.

### ML350p resource plan

| Resource    | Allocation                                                                   |
| ----------- | ---------------------------------------------------------------------------- |
| Host (16G)  | ZFS ARC 6G + Poudriere tmpfs 4G + headroom 6G                                |
| bhyve (16G) | FreeBSD ISO test 4G + Linux cross-compile 4G + FreeBSD builder 4G + spare 4G |

Planned bhyve roles:

- ISO boot verification after each build
- Linux target validation
- FreeBSD/Poudriere test lane

### Host preflight

```sh
./scripts/preflight-host.sh
which bhyve
kldstat | grep vmm
df -h /
```

Optional PF helper for guest internet:

```sh
sudo ./scripts/bhyve-pf-allow.sh
```

### Run the boot verification

```sh
cd /home/clawdie/ai/clawdie-iso
sudo ./scripts/bhyve-test.sh
```

### What to look for

- image boots at all
- SDDM/XFCE path appears instead of dropping straight to a dead console
- no immediate package/rootfs failure
- Clawdie/Colibri service staging does not crash the session at first boot

bhyve is useful for catching:

- broken bootcode
- missing live-session payload
- obvious SDDM/Xorg startup regressions
- obvious service-start regressions after build-time staging changes

It is **not** enough for final GPU confidence or laptop-specific UI/device
behavior.

---

## Level 3: Hardware USB Validation

This is the real acceptance path for the operator USB.

### Before flashing

If reusing an old USB stick, inspect it first. Old ZFS labels at the end of the
device can survive if a previous image was larger or used a different layout.

On Linux:

```sh
lsblk -f
sudo wipefs -n /dev/sdX
sudo fdisk -l /dev/sdX
```

If you see stale labels such as `nomadbsd_zroot`, wipe the whole stick first:

```sh
sudo umount /dev/sdX* 2>/dev/null || true
sudo sgdisk --zap-all /dev/sdX
dd if=/dev/zero of=/dev/sdX bs=16M status=progress conv=fsync
```

### Flash the image

Use [FLASHING.md](FLASHING.md) as the canonical flashing guide for Linux and
FreeBSD. For a published compressed image on Linux, the default path is:

```sh
curl -fL --continue-at - --retry 5 --retry-delay 5 --progress-bar -O \
  https://osa.smilepowered.org/downloads/iso/clawdie-quindecim-0.11.0.img.xz
curl -fL --retry 5 --retry-delay 5 -O \
  https://osa.smilepowered.org/downloads/iso/clawdie-quindecim-0.11.0.img.xz.sha256
sha256sum -c clawdie-quindecim-0.11.0.img.xz.sha256
set -o pipefail 2>/dev/null || true
xz -dc clawdie-quindecim-0.11.0.img.xz | dd of=/dev/sdX bs=4M status=progress conv=fsync && sync
sync
```

Use the whole USB disk (`/dev/sdX`), not a partition such as `/dev/sdX1`.
Then re-check the stick:

```sh
sudo wipefs -n /dev/sdX
lsblk -f /dev/sdX
```

You should no longer see stale `nomadbsd_zroot`-style labels.

### Expected boot behavior

1. USB boots.
2. `clawdie_live_gpu` detects display hardware and loads a conservative KMS path.
3. SDDM starts.
4. Operator logs in at the SDDM greeter as `clawdie`.
5. XFCE panel and bootstrap launcher appear.
6. NetworkMgr tray applet appears.

### Core runtime checks

From the live desktop or a tty:

```sh
id clawdie
hostname
grep -E '^[[:space:]]*(127\.0\.0\.1|::1)[[:space:]].*clawdie-live' /etc/hosts
mount | egrep ' on / | on /tmp | on /var '
df -h / /tmp /var/log /var/tmp ~/.cache
egrep 'hostname|root_rw_mount|tmpmfs|varmfs|sshd|avahi|powerd|powerdxx|pf_enable|sddm|display_manager|clawdie_live_gpu|clawdie_tailscale_up|devfs_system_ruleset|kld_list|zfs' /etc/rc.conf /etc/rc.conf.local 2>/dev/null
sysctl security.mac.do.rules
id
echo "$XDG_RUNTIME_DIR"
ls -ld "$XDG_RUNTIME_DIR"
ls -l /usr/local/share/glib-2.0/schemas/gschemas.compiled /usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache
ls -ld /dev/dri /dev/dri/* 2>/dev/null || true
ls -ld /var/lib/xkb /tmp /var/tmp /usr/local/share/X11/xkb /usr/local/share/X11/xkb/keycodes
ls -ld /usr/local/etc /usr/local/etc/xdg /usr/local/etc/xdg/xfce4
ls -l /usr/local/etc/xdg/xfce4/xinitrc /home/clawdie/.xinitrc /home/clawdie/.config/xfce4/xinitrc
/usr/local/bin/xinit -version
/usr/local/bin/startx --help >/dev/null 2>&1 || true
test -x /usr/local/bin/clawdie-startx
test -x /usr/local/bin/clawdie-gui
xterm -version
mousepad --version
service dbus onestatus
service sshd onestatus
service pf onestatus
service avahi-daemon onestatus 2>/dev/null || service avahi_daemon onestatus
service tailscaled onestatus
service sddm onestatus
swapinfo                                # expected: no swap devices listed
cat /usr/local/etc/sddm.conf.d/50-clawdie-live.conf
cat /etc/ssh/sshd_config.d/clawdie-live.conf
grep -E '^hosts:.*mdns_minimal.*dns.*mdns' /etc/nsswitch.conf
pfctl -sr
cat /var/log/clawdie-live-gpu.log
kldstat | egrep 'i915|amdgpu|radeon|vmwgfx|nvidia|linux|zfs'
ifconfig
sysctl net.wlan.devices 2>/dev/null || true
pciconf -lv
usbconfig
dmesg | grep -Ei 'iwl|rtw|ath|mt76|firmware|wlan|wifi|802\.11'
```

Expected filesystem state:

- `/` is mounted read-write from `/dev/ufs/FreeBSD_Install`
- `/tmp` and `/var/log` are tmpfs mounts from `/etc/fstab`
- `/var/tmp` stays on the root filesystem
- `~/.cache` is a symlink to `/tmp/clawdie/cache`
- `/var/lib/xkb` exists and is writable for XKB cache generation

Then verify operator tools:

```sh
echo "$SHELL"
command -v bash
command -v zsh
command -v git
command -v node
command -v npm
command -v pi
pi --help
tailscale version
tmux -V
python3 --version
bastille --help
mdo -u root bastille --help
```

For networking/UI:

- confirm the NetworkMgr tray icon appears
- confirm Firefox opens
- confirm `pcmanfm` opens
- confirm `mousepad` opens a plain-text file
- confirm panel power actions work for `clawdie`
- confirm `mdo -u root tailscale up` is available when you want to join a tailnet
- if built with `--ssh-key`, confirm SSH from another machine works with the baked key
- if built with `--tailscale-auth-key`, confirm `tailscale status` shows the node joined and `ssh clawdie@clawdie-live` works from the tailnet
- if on a multicast-friendly LAN, confirm `clawdie-live.local` resolves from another machine
- if a `wlan` device appears, confirm NetworkMgr can see it without any extra `wifibox` layer
- if Wi-Fi is missing, collect `ifconfig`, `sysctl net.wlan.devices`, `pciconf -lv`, `usbconfig`, and the Wi-Fi/firmware `dmesg` lines before changing package lists

### GPU expectations

The live USB now prefers broad boot success over aggressive proprietary loading and selects KMS drivers by numeric PCI display vendor ID, not broad text matches:

- AMD/ATI `0x1002` -> `amdgpu`, then `radeonkms` only if `amdgpu` did not load
- Intel `0x8086` -> `i915kms`
- VMware `0x15ad` -> `vmwgfx`
- NVIDIA `0x10de` -> proprietary modules only if `/boot/modules/nvidia.ko` is actually installed
- otherwise -> Xorg fallback path (`scfb` / `vesa`)

This means an NVIDIA machine may still boot graphically without using the
proprietary NVIDIA stack.

### Reference deploy machine — ASUS ZenBook UX325UA / Ryzen 5700U

Linux baseline probe:
[linux-hardware.org/?probe=efd5b5b389](https://linux-hardware.org/?probe=efd5b5b389)

This is a real deploy target. First-boot check set for this machine
(or any AMD Ryzen U-series laptop) once the image lands:

```sh
# AMD graphics + firmware
cat /var/log/clawdie-live-gpu.log
kldstat | grep -E 'amdgpu|drm|i915'
dmesg | grep -iE 'amdgpu|firmware|renoir|cezanne|green'
sysctl hw.dri.0.busid

# Backlight (acpi_video)
sysctl hw.acpi.video.lcd0.brightness

# Intel Wi-Fi 8265 — clawdie_live_wifi should have created wlan0
service clawdie_live_wifi status
sysctl net.wlan.devices                # expect iwm0 (or similar)
ifconfig -a | grep -A 2 ^wlan0
ifconfig wlan0 list scan | head

# Webcam — webcamd should have attached on first USB enumeration
service webcamd status
ls -l /dev/video*
v4l2-ctl --list-devices
# Optional interactive: pwcview         # close with q

# AMD I²C / HID-over-I²C touchpad
pciconf -lv | grep -B 1 -A 3 -iE 'i2c|smbus'
kldstat | grep -iE 'iic|hms|hmt|elan|hidbus'
dmesg | grep -iE 'iichid|hms|hmt|elan|psm'
sysctl kern.evdev.input

# Hardware baseline bundle for support/custom-ISO analysis. This is local-only.
mdo -u root hw-report

# Optional public hardware baseline upload — gives a permanent
# bsd-hardware.info URL for direct compare against the Linux probe above.
mdo -u root hw-report --upload-public-probe

# Input/video diagnostic clients. libinput works outside X; xinput/xrandr/glxinfo
# assume the desktop session is live on :0.
libinput list-devices
DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority xinput list
DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority xrandr --verbose | head -80
DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority glxinfo -B

# Agent CLI runtime probe. pi is required; gemini/claude should both be absent
# from the live image.
pi --version
command -v gemini || echo "gemini absent as expected (not bundled on the live image)"
codex --version
command -v claude || echo "claude absent as expected (skipped on FreeBSD live image)"

# X power management — must be disabled out of the box.
# Expect "timeout: 0", "DPMS is Disabled". Wait 11+ min idle to confirm
# screen does NOT blank (10 min was the stock timeout that caused
# the hang-looking session on wake). If XFCE tries to re-enable it,
# the no-blank guard should force it back off.
xset q | grep -E 'timeout|DPMS is'
xfconf-query -c xfce4-power-manager -l -v | grep -E 'blank|dpms|inactivity|presentation|lock-screen' || true
tail -20 ~/.clawdie-noblank-guard.log

# CPU power management runtime probe.
service powerdxx status              # expect "is running as pid ..."
service clawdie_live_power status    # expect power_profile applied at boot
sysctl hw.acpi.acline                # 1 = AC, 0 = battery
sysctl hw.acpi.cpu.cx_lowest         # expect C3 (matches live USB rc.conf)
sysctl dev.cpu.0.freq_levels         # available freq steps
sysctl dev.cpu.0.freq                # after ~1 min idle, should be at a low step
# Optional A/B vs default: stop the daemon, observe freq pinning at max
#   service powerdxx stop && sysctl dev.cpu.0.freq && service powerdxx start

# npm-global ownership at runtime — this was the silent regression in
# pre-0f142b7 images. Both should be empty.
find /opt/clawdie/npm-global \( ! -user clawdie -o ! -group clawdie \) -print
stat -f '%Su:%Sg' /opt/clawdie/npm-global

# Audio: OSS mixer + xfce4-mixer panel plugin. snd_hda should attach
# on most hardware. xfce4-mixer uses GStreamer→OSS; if the volume
# icon is missing from the panel, check gstreamer1-plugins-good.
cat /dev/sndstat
mixer -s | head                           # expect "Mixer vol      is currently set to ..."
pkg info -e gstreamer1-plugins-good

# Panel plugin visual: xkb should show flag icons (not text "si"),
# cpugraph should be ~36px wide, mixer + clipman should be configured,
# clock font should be ~14pt Noto Sans.
xfconf-query -c xfce4-panel -p /plugins/plugin-10/display-type   # expect 0 (flag)
xfconf-query -c xfce4-panel -p /plugins/plugin-9/size            # expect 36
xfconf-query -c xfce4-panel -p /plugins/plugin-11                # expect mixer
xfconf-query -c xfce4-panel -p /plugins/plugin-13                # expect clipman
xfconf-query -c xfce4-panel -p /plugins/plugin-12/font           # expect "Noto Sans 14"

# Clipman runtime gate — catches the "could not be loaded" popup that
# prompted the original removal in 2fc4ca1. Empty grep = clipman loaded OK.
grep -ri 'could not be loaded' ~/.cache/xfce4-session/*.log 2>/dev/null || echo "clipman: no load failures in session log"

# xfce4-mixer runtime gate. The plugin runs inside xfce4-panel (no
# separate long-lived process), so `pgrep xfce4-mixer` returns empty
# even when the plugin is loaded and working. The question that
# actually matters is "does the mixer have an audio backend to
# control" — empty /dev/sndstat → the panel icon will be present
# but non-functional regardless of whether GStreamer→OSS started.
cat /dev/sndstat                              # expect at least one pcm0 entry
mixer -s 2>/dev/null | head                   # base CLI sanity check
xfce4-panel --plugin-list 2>/dev/null | grep -i mixer || true
```

Known FreeBSD limitations on this machine class (not bugs, do not chase):

- **AMD ACP3x audio coprocessor (`1022:15e2`)** — no FreeBSD driver. Plain
  HDA path (`snd_hda`) handles speakers / headphones / mic. AI noise
  cancellation / beamforming features are absent.
- **Realtek RTS522A PCIe card reader (`rtsx_pci` on Linux)** — FreeBSD
  `rtsx(4)` coverage is patchy for this chip; SD card reader may not work.
  Not critical for an operator USB.
- **HID-over-I²C touchpad** — depends on AMD I²C controller probing
  successfully on FreeBSD. If `iichid` does not attach, the touchpad
  falls back to PS/2 — pointer + click work, multitouch gestures do
  not. This is an upstream FreeBSD support gap, not a Clawdie bug.

### CLAWDIESEED partition — Linux pre-flash workflow

The live USB ships with a 64 MiB FAT32 partition labeled `CLAWDIESEED`
at slice 3. Mount it from any Linux box (or FreeBSD, or macOS) after
flashing — but BEFORE booting the stick — to drop operator overrides.
Today the importer (`/usr/local/etc/rc.d/clawdie_live_seed`) honors
SSH `authorized_keys` only; `hostname` / Tailscale auth-key / Wi-Fi
env files are reserved for future passes.

```sh
# After 'dd' of the image to /dev/sdX completes:
sudo mount -t vfat /dev/sdX3 /mnt/seed
cat /mnt/seed/README.txt                  # confirm contract
cp ~/.ssh/id_ed25519.pub /mnt/seed/authorized_keys
sync && sudo umount /mnt/seed

# Boot the USB. On the live system:
service clawdie_live_seed status
cat /var/log/clawdie-live-seed.log         # expect "installed authorized_keys..."
ls -la ~clawdie/.ssh/                      # 0700 dir, 0600 authorized_keys
ssh -i ~/.ssh/id_ed25519 clawdie@clawdie-live.local  # from another box on LAN
```

The importer runs at every boot and is idempotent — edit the seed,
reboot, re-applied. There is no first-boot-only gate; removing a file
from the seed and rebooting does NOT remove it from the live system
(re-flash to wipe state).

---

## Supported Networking Hardware

Some target hardware (Gemini Lake mini-PCs, cheap fanless laptops, kiosk
boxes) ships with no internal ethernet and no internal WiFi — empty M.2
slots, USB-only I/O. For those, the live USB still boots and runs the
full local stack (`sshd`, Avahi, PF, daemons, mounts), but networking
has to come over USB.

### Recommended USB ethernet (preferred path)

| Chipset                   | Driver    | Notes                                                    |
| ------------------------- | --------- | -------------------------------------------------------- |
| ASIX AX88179 / AX88179A   | `axge(4)` | USB-A or USB-C → 1 GbE. Common in higher-end USB-C hubs. |
| Realtek RTL8153 / RTL8156 | `ure(4)`  | The most common 1 GbE chip in USB-C dock dongles.        |
| ASIX AX88772              | `axe(4)`  | 100 Mbit only. Older chipset, still works.               |

All three drivers are in base FreeBSD and **autoload on USB attach** —
no `kld_list` change or rc.conf edit needed. Plug in → `ue0` appears in
`ifconfig` within ~1 second → DHCP → working. If it doesn't autoload
(rare), `mdo -u root kldload if_axge` (or `if_ure` / `if_axe`).

USB ethernet is preferred over USB WiFi for development: more reliable,
faster, doesn't depend on driver-firmware support, and bypasses the
WiFi-chipset-roulette problem below.

### Recommended USB WiFi (when wired isn't possible)

| Chipset                       | Driver                    | Notes                                                                                             |
| ----------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------- |
| Atheros AR9271                | `otus(4)`                 | TP-Link TL-WN722N **v1 only** — v2/v3 silently switched to Realtek. Look for "v1" on the package. |
| Ralink RT5370 / RT2870        | `run(4)`                  | 2.4 GHz only, very stable.                                                                        |
| Realtek RTL8812AU / RTL8821AU | `net/rtl8812au-kmod` port | 802.11ac. Needs the port, not in base.                                                            |
| Realtek RTL8188EU             | `urtwn(4)`                | Cheap Realtek option that actually has a FreeBSD driver.                                          |

Linksys / D-Link / Netgear-branded dongles **silently switch chipsets
between revisions**. There is no way to predict which silicon is in a
given box without opening it. Avoid those brands; buy by chipset
(searched on `0bda:XXXX` / `148f:XXXX` etc. as vendor:device IDs).

### Recommended internal (PCIe / M.2)

| Chipset                                  | Driver                              | Notes                                                                                     |
| ---------------------------------------- | ----------------------------------- | ----------------------------------------------------------------------------------------- |
| Intel Wireless 7260 / 8260 / 8265 / 8275 | `iwm(4)` (or `iwlwifi(4)` LinuxKPI) | Wi-Fi 5 (ac). 8265 verified present on Ryzen 5700U laptops; works on FreeBSD via `iwm`.   |
| Intel AX200 / AX201 / AX210 / AX211      | `iwlwifi(4)`                        | Wi-Fi 6 / 6E. Best WiFi path on FreeBSD. Most laptops with an M.2 slot can take an AX210. |
| Intel I218 / I219 / I225 / I226          | `em(4)` / `igc(4)`                  | Default ethernet on most Intel-board systems.                                             |
| Realtek RTL8111 / RTL8168 PCIe           | `re(4)`                             | Common consumer-motherboard 1 GbE.                                                        |

### Known unsupported on FreeBSD

| Chipset                                         | Reason                                                                                                                                                                                              |
| ----------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Realtek **RTL8723BU** (`0bda:b720`)             | No driver in any FreeBSD version through 15. The **Bluetooth half** of this combo dongle does work via `ng_ubt` — verified on hardware. WiFi half: no path forward without out-of-tree Linux ports. |
| Realtek RTL8723BS                               | SDIO variant, same status.                                                                                                                                                                          |
| Realtek RTL8723DU                               | Same family, same status.                                                                                                                                                                           |
| MediaTek MT7921 / MT7922                        | No driver as of early 2026.                                                                                                                                                                         |
| Anything labelled "generic Realtek 802.11n USB" | Vendor swaps chipsets; chipset ID required to predict support.                                                                                                                                      |

### Identifying what's in the box

```sh
# USB attached devices
usbconfig
usbconfig -d ugen?.? dump_device_desc      # vendor:device of a specific one

# PCIe / internal
pciconf -lv | grep -B 1 -A 4 -iE 'wireless|ethernet|network'

# What FreeBSD's own probe suggests for installed firmware
mdo -u root fwget -n                       # dry run
```

### What to do when the chipset is unsupported

The live USB image is still useful — all daemons bind to `lo0` too, so
`sshd`, Avahi, and the local stack can be validated without network.
For end-to-end validation, swap to a supported dongle from the lists
above.

### Cross-platform hardware comparison

The live USB ships a FreeBSD-native hardware probe set instead of trying to
mirror Linux's `hwinfo` package name:

- `pciconf`
- `usbconfig`
- `devinfo`
- `kldstat`
- `dmidecode`
- `lspci` via `pciutils`
- `lsusb` via `usbutils`

This is enough to compare a FreeBSD boot against a Linux baseline on the same
chassis and spot missing drivers or firmware support.

**Capture matched reports on both sides:**

```sh
# FreeBSD live USB:
pciconf -lv > /tmp/fbsd-pci.txt
usbconfig > /tmp/fbsd-usb.txt
devinfo -rv > /tmp/fbsd-devinfo.txt
dmidecode > /tmp/fbsd-dmi.txt
kldstat > /tmp/fbsd-kldstat.txt
lspci -nn > /tmp/fbsd-lspci.txt
lsusb > /tmp/fbsd-lsusb.txt

# Linux baseline on the same chassis:
lspci -nnk > /tmp/linux-pci.txt
lsusb > /tmp/linux-usb.txt
sudo dmidecode > /tmp/linux-dmi.txt
```

What the same chassis should show under the FreeBSD live USB once
booted there:

| Linux shows                          | FreeBSD equivalent                                                           |
| ------------------------------------ | ---------------------------------------------------------------------------- |
| `wlp1s0` Intel Wireless 8265 / 8275  | `wlan0` after `ifconfig wlan0 create wlandev iwm0`; chip handled by `iwm(4)` |
| `ATI Lucienne` (AMD Renoir iGPU)     | `vgapci0` in `pciconf`; `amdgpu` in `drm-kmod`                               |
| `Intel Bluetooth wireless interface` | `ng_ubt` netgraph stack                                                      |
| `tailscale0 Network Interface`       | identical name on both platforms                                             |
| `/dev/nvme0n1p*` partitions          | `nvd0p*` (legacy) or `nvme0ns1*` (newer FreeBSD naming)                      |

**Reading the diff:**

- If a device class shows on Linux but is missing from the FreeBSD
  report, that's a driver gap. Common gaps: USB WiFi dongles whose
  Linux driver is out-of-tree (RTL8723BU, RTL8821CU, etc.). Internal
  PCIe / M.2 hardware usually behaves the same.
- If a device class shows on both but with different naming, that's
  expected — see the format-differences note below.

**Output-format differences not worth trying to normalize:**

- Linux: predictable interface names (`wlp1s0`, `enp2s0`,
  `wlx<mac>`). FreeBSD: driver-based names (`wlan0`, `iwm0`, `re0`,
  `em0`, `ue0`).
- Linux: HID surfaces as `/dev/input/event*`. FreeBSD: through `hmt`,
  `psm`, `kbdmux` instead.
- Linux: `/dev/nvme0n1p*`. FreeBSD: `nvd0p*` / `nvme0ns1*`.

Don't try to make the outputs byte-identical — compare _device
classes and counts_, not full lines.

### Extended diagnostic toolkit (live by default)

The live USB ships the FreeBSD-native probe set plus `pciutils`, `usbutils`,
`dmidecode`, and the `hw-report` collector. The collector writes a local
bundle under `/home/clawdie/hw-reports` and does not upload unless the
operator explicitly asks for a public probe.

**Recommended collection command:**

```sh
mdo -u root hw-report
```

**Optional public bsd-hardware.info upload:**

```sh
mdo -u root hw-report --upload-public-probe
```

**Manifest:**

| Package         | Version | Purpose                                                                                          |
| --------------- | ------- | ------------------------------------------------------------------------------------------------ |
| `hw-probe`      | 1.6.6_1 | Comprehensive hardware probe report; can submit anonymously to `bsd-hardware.info`.              |
| `smartmontools` | 7.5_2   | `smartctl` for SMART disk health; `smartd(8)` for monitoring (daemon left disabled on live USB). |
| `lscpu`         | 1.3.0   | Linux-style CPU summary — convenient when comparing against a Linux baseline.                    |
| `lsblk`         | 4.1_1   | Linux-style block-device listing — same.                                                         |
| `hwstat`        | 0.5.1_1 | Hardware statistics (CPU, mem, network, disk counters).                                          |
| `usbhid-dump`   | 1.4     | Raw HID report descriptor dump. Useful for touchpad/keyboard quirks.                             |
| `xinput`        | 1.6.4   | Exact X input device list and per-device properties (trackpad tap/scroll/DWT state).             |
| `xrandr`        | 1.5.3   | RandR output/mode/EDID probe from the running X session.                                         |
| `mesa-demos`    | 8.5.0   | Provides `glxinfo -B` to confirm real GL renderer vs `llvmpipe`.                                 |

Transitive deps of note: `p5-LWP-UserAgent-Cached` (Perl HTTP cache,
from `hw-probe`); `freeglut` and `libGLU` from `mesa-demos`.

**Useful direct commands:**

```sh
# Comprehensive local Clawdie bundle
mdo -u root hw-report

# Trackpad / mouse / video truth from the running session
libinput list-devices
DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority xinput list
DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority xrandr --verbose
DISPLAY=:0 XAUTHORITY=/home/clawdie/.Xauthority glxinfo -B

# Comprehensive snapshot — public upload only when explicitly requested
mdo -u root hw-probe -all
mdo -u root hw-probe -all -upload           # submit to bsd-hardware.info
mdo -u root hw-report --upload-public-probe # confirm public Probe URL is reported

# SMART disk health
/usr/local/sbin/smartctl -a /dev/ada0       # SATA
/usr/local/sbin/smartctl -a /dev/da0        # SCSI / USB-attached
/usr/local/sbin/smartctl -a /dev/nvme0      # NVMe

# Linux-familiar summaries
lscpu
lsblk

# Hardware stats
hwstat

# HID descriptor of a specific USB device
usbhid-dump -d <ugen>
```

**smartmontools daemon setup — operator opt-in on the installed system,
not the live USB:**

```sh
cp /usr/local/etc/smartd.conf.sample /usr/local/etc/smartd.conf
# edit to taste
echo 'smartd_enable="YES"'         | mdo -u root tee -a /etc/rc.conf
echo 'daily_status_smart_devices=' | mdo -u root tee -a /etc/periodic.conf
mdo -u root service smartd start
```

On the live USB `smartd` stays off — interactive `smartctl -a` is the
intended use. The daemon setup belongs on the disk-installed system if
the operator wants continuous monitoring.

**Promotion question — should some of these be baked in?**

The ~3 MiB cost is small relative to the image. `smartmontools` and
`hw-probe` are the two with the strongest case for becoming default
live-USB packages, since both come up regularly during hardware
validation. `lscpu` / `lsblk` are convenience aliases for Linux muscle
memory. `hwstat` and `usbhid-dump` are niche. Left as on-demand for
now; revisit in a future package-list pass.

---

## If Graphical Login Fails

Do not rebuild immediately. First capture runtime facts from the booted USB.

### Check service and Xorg state

```sh
service dbus onestatus
service sddm onestatus
tail -100 /var/log/sddm.log
tail -100 /var/log/Xorg.0.log
cat /var/log/clawdie-live-gpu.log
```

### Check XKB runtime files

```sh
ls -ld /usr/local/share/X11
ls -ld /usr/local/share/X11/xkb
ls -ld /usr/local/share/X11/xkb/keycodes
ls -ld /var/lib/xkb /tmp /var/tmp
ls -l /usr/local/share/X11/xkb/keycodes/xfree86
pkg info -e xkeyboard-config xkbcomp xorg-server xorg-minimal
```

If X is already running, compile the active keymap:

```sh
setxkbmap -print | xkbcomp -w 10 -xkm - /tmp/clawdie-xkb-test.xkm
echo $?
rm -f /tmp/clawdie-xkb-test.xkm
```

If X is not running, compile a default keymap against the installed XKB tree:

```sh
cat > /tmp/clawdie-xkb-default-test.xkb <<'EOF'
xkb_keymap {
  xkb_keycodes { include "xfree86+aliases(qwerty)" };
  xkb_types { include "complete" };
  xkb_compat { include "complete" };
  xkb_symbols { include "pc+us+inet(evdev)" };
  xkb_geometry { include "pc(pc105)" };
};
EOF
xkbcomp -w 10 -R/usr/local/share/X11/xkb /tmp/clawdie-xkb-default-test.xkb /tmp/clawdie-xkb-default-test.xkm
echo $?
rm -f /tmp/clawdie-xkb-default-test.xkb /tmp/clawdie-xkb-default-test.xkm
```

### Check the actual running config

```sh
egrep 'hostname|root_rw_mount|tmpmfs|varmfs|sshd|avahi|pf_enable|sddm|display_manager|clawdie_live_gpu|clawdie_tailscale_up|kld_list|zfs' /etc/rc.conf /etc/rc.conf.local 2>/dev/null
hostname
grep -E '^[[:space:]]*(127\.0\.0\.1|::1)[[:space:]].*clawdie-live' /etc/hosts
mount | egrep ' on / | on /tmp | on /var '
df -h / /tmp /var/log /var/tmp ~/.cache
readlink ~/.cache
```

### Live Wi-Fi diagnostics

If Ethernet works but Wi-Fi does not appear, collect:

```sh
ifconfig
sysctl net.wlan.devices 2>/dev/null || true
pciconf -lv
usbconfig
kldstat | grep -Ei 'iwl|rtw|ath|mt76|wlan|wifi'
dmesg | grep -Ei 'iwl|rtw|ath|mt76|firmware|wlan|wifi|802\.11'
```

Current packaging intent:

- native firmware bundle: `wifi-firmware-kmod`
- runtime firmware helper: `FreeBSD-fwget`
- network UI: existing `NetworkMgr`
- not included: `wifibox`

### Manual X session probe

If SDDM is dead but a tty works:

```sh
su - clawdie
startxfce4
```

If `startxfce4`/`startx` trips over xauth/serverauth setup while Xorg itself
starts, use the live rescue launcher. It bypasses `startx` xauth setup and starts
a local-only Xorg server for console debugging:

```sh
su - clawdie
clawdie-gui
```

If this fails, keep the exact error text. The common split is:

- Xorg/runtime filesystem issue
- SDDM/login issue
- GPU module selection issue

Avoid package-list changes until the runtime evidence says a package is truly
missing from the live rootfs.

---

## Acceptance Checklist

Mark the image good only when all of these are true:

- [ ] image boots from USB on real hardware
- [ ] SDDM greeter appears; logging in as `clawdie` launches Clawdie XFCE
- [ ] XFCE panel and desktop appear
- [ ] hostname is set to `clawdie-live`
- [ ] `/` is read-write, `/tmp` and `/var/log` are tmpfs, and `/var/tmp` stays on disk
- [ ] `~/.cache` points at `/tmp/clawdie/cache`
- [ ] `/usr/local/etc/xdg/xfce4/xinitrc` and `clawdie` xinitrc fallbacks are executable
- [ ] `xinit`/`startx`, `clawdie-startx`, `clawdie-gui`, and `xterm` are present for minimal Xorg rescue/startup checking
- [ ] `/var/lib/xkb` exists and XKB keymap compilation succeeds
- [ ] `XDG_RUNTIME_DIR` resolves to `/var/run/user/<uid>`
- [ ] `clawdie_live_gpu` log shows a sensible path or a clear fallback
- [ ] SSH policy is pubkey-only and root login is disabled
- [ ] PF baseline is active with no default logging stack
- [ ] `clawdie-live.local` resolves on a multicast-friendly LAN
- [ ] `mdo -u root id` succeeds for `clawdie`
- [ ] GSettings schemas and gdk-pixbuf PNG loader cache exist
- [ ] `/dev/dri/*` is usable by `clawdie` via the `video` group when DRM is present
- [ ] NetworkMgr tray icon appears
- [ ] Firefox opens
- [ ] `mousepad` opens plain-text files
- [ ] `pi --help` works
- [ ] `tailscale version` works
- [ ] `tmux -V` works
- [ ] `python3 --version` reports Python 3.12
- [ ] `bastille --help` works
- [ ] `sudo` is absent from the live image
- [ ] no stale-label confusion remains on the flashed USB stick

---

## See Also

- [README.md](README.md) — current branch overview and quick start
- [BUILD.md](BUILD.md) — build flags and artifact output
- [REQUIREMENTS.md](REQUIREMENTS.md) — build host, USB, and hardware requirements
- [FIRSTBOOT.md](FIRSTBOOT.md) — installed-system firstboot pipeline (philosophy, dependency graph, per-module reference)

---

**Last updated:** 16.maj.2026
