The armed boot reached the engine and died in its own preflight:
halogen npu: /dev/accel/accel0 is here, but this process cannot open it
(crw-rw---- nobody:nogroup).
The node is root:render 0660 and a rootless container maps exactly one uid/gid,
so `render` is unmapped inside it and `--group-add keep-groups` — the vendor's
advice, and the ComfyUI pattern for /dev/kfd — cannot grant a group the
namespace does not map. Measured 2026-10-19 from a `dank` shell already in
`render`: without the flag denied, with the flag denied, with the udev mode
0666 the open succeeds (OPEN_OK). Same wall 99-comfyui-gpu-access.rules already
solved for the GPU compute nodes, so the accel node gets its counterpart:
dankclaw/udev/98-halogen-npu-access.rules — KERNEL=="accel[0-9]*",
SUBSYSTEM=="accel", MODE="0666"
The deployer installs the rule, reloads rules, re-triggers the node and only
then checks the mode; the serve script refuses any node that is not
other-writable, naming the rule, so this cannot return as a crash loop (189
restarts while chat was down is the failure mode being closed).
Both checks read the permission bit directly: `[ -o FILE ]` is not
"other-writable" — it means "exists" and `-o` doubles as the OR operator inside
test, which is why the first cut of the gate refused everything.
Dragoon seam: 33 -> 35 tests. The fixture node is 0666 like the installed one,
a 0660 node is refused with the rule named and nothing rendered, and the rule
file's shape plus the deployer's install/trigger are asserted.
27 lines
1.6 KiB
Plaintext
27 lines
1.6 KiB
Plaintext
# /etc/udev/rules.d/98-halogen-npu-access.rules
|
|
#
|
|
# Why the NPU node is world-writable: `frank-dragoon` runs rootless podman under a
|
|
# unit with `User=dank`, and the container's user namespace maps exactly one uid and
|
|
# one gid (dank). `/dev/accel/accel0` is `root:render 0660`, so inside the container
|
|
# the node reads `nobody:nogroup` and the engine dies with its own diagnosis:
|
|
#
|
|
# halogen npu: /dev/accel/accel0 is here, but this process cannot open it
|
|
# (crw-rw---- nobody:nogroup).
|
|
#
|
|
# `--group-add keep-groups` is the vendor's advice and it does NOT work in this
|
|
# shape: measured 2026-10-19 from the host as a `dank` shell that was already in
|
|
# `render`, with and without the flag, the node still read `nobody:nogroup` and the
|
|
# open was refused — an unmapped gid cannot be granted to a process inside a
|
|
# namespace that does not map it. Same conclusion `99-comfyui-gpu-access.rules`
|
|
# reached for `/dev/kfd` from the systemd-user-spawn direction: the mode is the only
|
|
# spawn-context-independent door short of running the stack rootful.
|
|
#
|
|
# Blast radius: any process in this host's single uid domain (every rootless
|
|
# container here shares it) can submit to the XDNA2 NPU. The NPU holds no user data
|
|
# and no privileged context — the same trade already accepted for the GPU compute
|
|
# nodes beside it. Installed by `deploy-remote.sh` when the NPU lane is armed and left
|
|
# in place afterwards — the mode costs nothing while the lane is idle. `serve-frank-dragoon.sh`
|
|
# refuses to arm the lane on a node that is not other-accessible, so a host that lost this
|
|
# file fails loudly at boot instead of crash-looping.
|
|
KERNEL=="accel[0-9]*", SUBSYSTEM=="accel", MODE="0666"
|