ProtectSystem=full makes /etc read-only, so Idunn could not create Heimdall's
runtime bundle under /etc/gamecult/heimdall/runtime. A target whose roots are
not in ReadWritePaths is a target Idunn cannot deploy. Every migration adds
exactly three paths -- runtime root, state root, release root -- which keeps
the write set enumerated and is the honest blast radius of running as root.
Also records the per-target preprovisioning Idunn requires rather than
creates, each discovered by a failed deployment: the state root must be
root:<state_group> mode 2770 so the workload's dynamic user inherits group
access through setgid; the runner cache must be 0700 owned by the runner's
container uid, not the source identity, with a root-owned parent (/srv/build
is idunn:idunn and fails); the frozen source stage must be root-owned,
because source is frozen as idunn and then copied into a root-owned
actuation stage; and the binding's docker network must exist.
None of these are Idunn being fussy. Each is a check that a binding cannot
widen access by naming a path someone else can write.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>