fix(frank-dragoon): stage the NPU lane inside the payload mount

The lane's first boot died at container init, not at the engine:

  runc create failed: ... create mountpoint for /models/npu mount:
  mkdirat .../merged/models/npu: read-only file system

The image ships no /models, and the serve script binds the payload dir
there read-only, so a nested `-v $NPU_DIR:/models/npu:ro` had to create
its own mountpoint underneath a read-only parent — impossible. The
engine's own entrypoint confirms the container path is fixed: it rejects
HALOGEN_NPU_DIR outright ("our NPU models live under /models/npu/<id>/").

Fix is the mount shape, not the engine: FRANK_DRAGOON_NPU_DIR now lives
inside FRANK_DRAGOON_MODEL_DIR, so the payload's single read-only mount
puts the files at /models/npu/<id>/ by itself and the nested bind is
gone. Verified on the running container: after the move, `podman exec
frank-dragoon-halogen ls /models/npu/qwen3-embedding-0.6b` answers with
the staged tree while the lane is still armed off.

Both gates refuse the old shape by name (serve + deployer parity) so a
future payload swap that moves the model dir cannot reintroduce a crash
that lands after the deployer has already stopped the running unit.
This commit is contained in:
Frank
2026-10-19 21:42:31 -07:00
parent b1ca4e02fa
commit b19f1c49d6
5 changed files with 74 additions and 13 deletions
+20 -4
View File
@@ -125,9 +125,17 @@ fi
# Empty = the endpoint exactly as it ran before the lane existed: no device, no
# extra mounts, no gate below. Set = three host preconditions, each refused BY
# NAME (the 2026-10-01 law: the serve gate and the deployer gate must agree):
# 1. the staged model files (the engine checks size+sha256 against its own
# catalog at boot; it cannot fetch them here because /models is read-only
# and HALOGEN_DOWNLOAD stays unset — no outbound connection at boot),
# 1. the staged model files under $FRANK_DRAGOON_NPU_DIR — a path that MUST
# live INSIDE $FRANK_DRAGOON_MODEL_DIR, because the container's only
# handle on them is the read-only /models mount. A nested bind at
# /models/npu cannot work: runc has to mkdir its mountpoint and the parent
# bind is ro (`mkdirat .../merged/models/npu: read-only file system`,
# measured 2026-10-19), and the image ships no /models at all for it to
# land on. `$MODEL_DIR/npu/<id>` is visible at /models/npu/<id> through
# the existing mount, which is also where the vendor's catalog expects it.
# The engine re-checks size+sha256 against that catalog at boot; it cannot
# fetch anything here because /models is read-only and HALOGEN_DOWNLOAD
# stays unset — no outbound connection at boot),
# 2. the distro XRT's three libraries, each mounted twice (where the image
# looks for XRT, and at its own path — the libraries find each other there;
# an /opt/xilinx/xrt of symlinks into the system dir dangles inside the
@@ -144,6 +152,15 @@ if [ -n "$NPU_MODELS" ]; then
NPU_DIR="${FRANK_DRAGOON_NPU_DIR:?HALOGEN_NPU_MODELS is set, so FRANK_DRAGOON_NPU_DIR must name the staged model dir}"
XRT_DIR="${FRANK_DRAGOON_XRT_DIR:-/usr/lib/x86_64-linux-gnu}"
SYSFS="${FRANK_DRAGOON_SYSFS:-/sys}"
# The lane's files have exactly one door into the container: the read-only
# payload mount. A staging dir OUTSIDE it would need a nested bind whose
# mountpoint runc cannot create through that read-only parent, so refuse the
# shape by name here rather than let runc die at container init (2026-10-19).
case "$NPU_DIR/" in
"$FRANK_DRAGOON_MODEL_DIR"/*) ;;
*) echo "frank-dragoon: FRANK_DRAGOON_NPU_DIR=$NPU_DIR is outside the payload dir $FRANK_DRAGOON_MODEL_DIR — the lane's files reach the container only through the read-only /models mount; move the staging dir inside it" >&2
exit 1 ;;
esac
if [ ! -e "$DEV_ACCEL" ]; then
echo "frank-dragoon: $DEV_ACCEL missing — the NPU lane needs the amdxdna node (is iommu=pt still on the boot line?)" >&2
exit 1
@@ -183,7 +200,6 @@ if [ -n "$NPU_MODELS" ]; then
exit 1
fi
NPU_ARGS+=(--device "$DEV_ACCEL" \
-v "$NPU_DIR:/models/npu:ro" \
-e HALOGEN_NPU_MODELS="$NPU_MODELS")
echo "frank-dragoon: NPU lane armed: $NPU_MODELS on $DEV_ACCEL (fabric clock held)" >&2
fi