- test_host_updater: stub git/docker now model cat-file/show/rev-parse
--verify and compose config/image inspect. The two update-path tests
were failing on Linux CI (skipped locally on macOS, no flock). Adds
tests for pull-before-checkout + .env pins, a missing pinned image
leaving the node unchanged, and a pre-pinning target still updating.
- drb-update.sh: load image-tags.sh via a variable + eval and confirm the
functions exist; `source <(git show ...)` "succeeded" with nothing
defined when git show failed, wedging every later update.
- install.sh: skip pinning (warn) when the checked-out ref predates
scripts/image-tags.sh instead of dying on the missing file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
install.sh (#14): after enrolling it now BLOCKS until an admin approves,
printing "Please have an admin approve node '<id>' at <app>/settings/nodes
to continue", polling GET /nodes/{id}/credentials with the pickup secret
(no default timeout; Ctrl-C safe; re-run resumes from configs/pickup_secret).
A re-run with a key on disk re-checks it against C2 and replaces a stale
(reissued) key. credentials.json is merged atomically so the dashboard auth
salt/secret survive. edge-node is ALWAYS force-recreated at the end, so a key
written after the container started is loaded. --wait-approval N caps the
wait; 0 = don't wait. git fetch uses --force so a re-cut v1 tag is accepted.
edge-node (#14): with no key at startup, or on a broker auth refusal
(CONNACK 4/5/134/135), mqtt_manager re-reads credentials.json, else fetches
the key over HTTPS (key_pickup.py, pickup secret from /configs), persists it
and rebuilds the paho client in place. Single-flight, 30s..15min backoff.
A key pushed over MQTT while connected is applied for the next reconnect.
OTA (#15): node_update no longer os._exit()s. It drops
configs/update-requested; install.sh installs scripts/install-updater.sh's
drb-update.{service,timer,path}. The path unit fires on the sentinel, the
timer (~15 min) does an HTTPS check-in (POST /nodes/{id}/update-check,
server-26 PR) so offline/locked-out nodes still update. drb-update.sh only
installs the repo/ref/registry in root-owned /etc/drb/updater.conf; C2's
answer is just a timestamp; origin and registry mismatches abort; creds go
to curl via a 0600 header file.
Tests: test_credential_recovery.py, test_host_updater.py (runs the real
updater script with stub git/docker/curl).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>