Fold the AppArmor/libvirt rule into scripts/prereqs -- it gated every VR1 VM boot
`/var/lib/libvirt/vr1/** rwk,` in /etc/apparmor.d/local/abstractions/libvirt-qemu had
been applied BY HAND on vcloud and existed ONLY as host state. Nothing in the repo
carried it, so a rebuild or a second host walked straight back into the same wall.

Why it matters: libvirt's stock abstractions/libvirt-qemu grants qemu only the DEFAULT
pool path (/var/lib/libvirt/images). VR1 uses a custom pool parent, which is not in that
abstraction, so AppArmor blocks qemu even with perfect POSIX permissions. The domain
DEFINES fine, then fails to start with a bare qemu "Permission denied" -- and NOTHING in
the libvirt error names AppArmor. It cost a session on 2026-07-12 (DOCFIX-186), was
logged as a PREREQ candidate, and was never actioned until the ledger collapse resurfaced it.

New: scripts/prereqs/install-apparmor-libvirt.sh, wired into install-all.sh (after
install-virtualization -- it edits libvirt's own profile dir) and reported by
check-prereqs.sh.

- Idempotent and deliberately QUIET on a good host: if the rule is present it exits 0
  having touched nothing -- it does NOT reload apparmor or bounce libvirtd. Those happen
  only on the run that actually adds the rule.
- Writes to local/, the vendor-sanctioned override include, so it survives package upgrades.
- Pool parent is a variable (VR1_POOL_PARENT, default /var/lib/libvirt/vr1), not a literal.
- Non-AppArmor hosts report N/A and SUCCEED -- no manufactured failure on a host it
  does not apply to.

Verification: tests/prereqs 32/32 PASS (was 24/24) -- the new cases drive DETECTION off a
pool parent guaranteed absent from the profile, so the negative path is deterministic on
any host: a false OK from --check IS the bug, --dry-run must plan and mutate nothing, and
the planned rule must be exactly `<parent>/** rwk,`. Full gauntlet ALL GREEN (52 harnesses),
repo-lint 0 fail. Run live on vcloud: --check reports OK and leaves the profile byte-identical,
confirming the already-satisfied path is a true no-op.

No host state was mutated by this change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 20e52e6 commit 4a32dde1533875b26215d85a8522b21daab0e9f3
@JANeumatrix JANeumatrix authored 5 hours ago
Showing 7 changed files
View
docs/changelog-20260713-apparmor-libvirt-prereq.md 0 → 100644
View
docs/session-ledger.md
View
scripts/prereqs/README.md
View
scripts/prereqs/check-prereqs.sh
View
scripts/prereqs/install-all.sh
View
scripts/prereqs/install-apparmor-libvirt.sh 0 → 100644
View
tests/prereqs/run-tests.sh