":Z" relabels what it mounts to container_file_t, recursively and on disk, and
init.sh lives in /etc/quadlets/zwave-js-ui. Mounting that directory into the
container therefore left a script systemd could no longer execute:
zwave-js-ui-init.service failed with 203/EXEC at every boot after the first
start of the container, and the container's Requires= on it kept the container
down too. A machine that survived its installation stopped surviving a reboot.
Found on the target machine, not in review: the first installation works,
because Ignition writes /etc with the right labels and the init service runs
before the container. The fault only appears at the second boot.
The README also gains a "Clearing the store" section. The seed is a first-boot
mechanism, so an operator who deletes settings.json -- after rotating the
security keys, for instance, since Z-Wave JS UI keeps its own copy of the old
ones -- has to restart the init service explicitly. "systemctl restart
zwave-js-ui.target" does not re-run it: it is RemainAfterExit=yes and already
active. Getting that wrong leaves a container unhealthy forever with a happy
driver, and authentication silently off.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Z-Wave JS UI exposes a Z-Wave controller twice: a web control panel on 8091 and
the Z-Wave JS Server websocket on 3000, which is what the Home Assistant
zwave_js integration connects to. MQTT stays off.
The controller is taken as /dev/zwave, a name a udev rule of the site gives it,
so no site value is baked into the cookbook. The container path is spelled out
in AddDevice: a udev SYMLINK is a symbolic link, and a device passed without one
arrives under its resolved name, which is not the name ZWAVE_PORT asks for.
The security keys, the Z-Wave JS Server settings and the log level come from an
operator-provided file through ZWAVE_EXTERNAL_SETTINGS, so Z-Wave JS UI marks
them managed externally and refuses to edit them. The two settings that file
cannot carry -- authEnabled and mqtt.disabled -- are seeded into the store once,
following the home-assistant cookbook pattern.
The health probe sends Accept: text/plain. Without it the single-page-application
fallback answers 200 for any request that accepts HTML, driver or no driver.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>