TROUBLESHOOTING
Troubleshooting
Real issues, with the actual fix — not a generic checklist. If your symptom
isn't here, check journalctl -u zyvor-device-agent -f first (or
docker logs/podman logs for the container path), then
open an issue.
Deploy/verify scripts report HEALTH_FAIL / INVENTORY_FAIL / METRICS_FAIL¶
This is expected, not a broken deployment, once server.tls.enabled =
true. scripts/deploy-remote.sh's and scripts/verify-deployment.sh's
built-in checks curl plain http://127.0.0.1:PORT — against a daemon now
correctly speaking TLS-only, that fails closed exactly as it should.
Service-active plus the deploy step itself succeeding already confirm the
deployment worked. Verify by hand instead:
curl -sSk https://127.0.0.1:9188/api/v1/health
(-k accepts the auto-generated self-signed cert; drop it if you've
mounted a real one.) See the README's TLS
section.
Browser shows "Your connection is not private" / a certificate warning¶
Expected the first time you reach a server.tls.enabled = true listener —
the daemon auto-generates a self-signed certificate if none is configured
(see src/tls.rs). Either click through the browser's
warning once, or replace it with a real certificate by mounting it at
server.tls.cert_path/key_path (an existing cert/key there is never
overwritten).
Lost or forgotten the bearer token¶
The daemon never stores the raw token — only its SHA-256 hash
(auth.bearer.token_hash_file), by design. There is no way to recover a
lost token; reissue a new one:
FORCE_BEARER_TOKEN_RESET=1 ./scripts/deploy-remote.sh user@host --auth-mode bearer
This prints the new token once — save it immediately. See 3. Securing your agent.
Camera snapshot/stream returns 503, or the capture log shows "Permission denied"¶
/dev/video* needs either root (the daemon's default) or membership in the
video group if you've de-privileged the daemon — see
docs/HARDWARE_PERMISSIONS.md's camera row. A
503 with no permission error in the log usually just means the capture
thread hasn't produced a frame yet — check GET /api/v1/camera for
capturing/last_error before assuming a permissions problem.
Dashboard is stuck showing "Authentication required"¶
Confirm auth.mode on the agent and that you've entered the correct
token in the dashboard's prompt (Settings → Authentication) — it's stored
per-browser in localStorage, so a new browser/incognito window needs it
re-entered. Note the dashboard's static shell always loads without a
token, even in auth.mode = "bearer"; only its API calls need one, so a
loading dashboard with a persistent auth banner is normal, not a sign
something else is broken.
deploy-remote.sh refuses to deploy with --bind set to a non-loopback address¶
This is intentional: auth.mode = "none" (the default when seeding a new
config) is refused on anything but a loopback bind, to stop an
unauthenticated agent from accidentally landing on a reachable network.
Pass --auth-mode bearer, or configure server.tls.enabled = true and set
auth.mode yourself before binding non-loopback.
Sent SIGHUP but a config change didn't take effect¶
Not every field is hot-reloadable. server.listen, server.unix_socket.*,
and server.dashboard_dir are bound once at startup; the Nodra MQTT
connection itself and which interfaces industrial.can_capture has open
are also only read once. A reload applies everything else and logs a
warning naming which of these it couldn't apply — restart the service for
those. See the README's
Config hot-reload section.
doctor (or the dashboard's Diagnostics page) reports failing checks¶
Expected in a container, CI runner, or dev VM with no real GPIO/I2C/CAN
hardware attached — doctor reports what it actually finds, and an empty
bus is a real (if uninteresting) result there, not a bug. This is exactly
why ci.yml's container smoke test treats a non-zero doctor exit as
expected rather than a failure.
Nothing in this list matches¶
Check the relevant reference doc first — docs/CAMERA.md,
docs/CAN_CAPTURE.md, docs/MTLS_ENROLLMENT.md, docs/PLUGIN_PROTOCOL.md
each have their own safety-contract and gotcha sections — then
open an issue with your config (redact secrets) and the
relevant journalctl/log output.