node-002's root disk hit 100% (39.5 GB build cache + 16 GB unused images)
and the node silently stopped recording for 17 days: BUILD=1 runs
`docker compose build` on every update and nothing ever pruned the cache;
the existing prunes only drop dangling images and old src- pins.
After `up -d`: `builder prune -af --filter until=72h` and
`image prune -af --filter until=168h`. Neither touches anything a running
container uses; the age filters keep the next build's cache warm.
Disk-free reporting to the portal (the other half of #41) is not in this
change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
radio-box (INSTALL_DIR=/home/pi/node-26, owned by pi) failed every update:
root's git refused the repo as 'dubious ownership', so origin read as ''
and the origin check refused. Scope safe.directory to INSTALL_DIR via
GIT_CONFIG_* env (command-line scope) for the updater process only.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 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>
Nodes checked out v1 code but pulled :latest/:stable images built from main.
Each image is now tagged src-<git tree hash of its build directory>: CI adds
that tag in every build-*.yml, and scripts/image-tags.sh computes the same
value on the node and writes it into .env as DRB_TAG_*, which
docker-compose.yml uses (falls back to :latest/:stable for local dev).
Tree hashes stay exact even though each image only rebuilds when its own
directory changes.
- install.sh pins after checkout and fails loudly if a pinned image is
missing (compose pull only warns for services with a build: section).
- drb-update.sh pulls the target commit's pinned images BEFORE checkout,
using the target's own compose file, so a missing image changes nothing
(uvicorn --reload would otherwise run new code on old images). It then
checks out, writes the pins, recreates, and removes superseded src- tags.
- New "Release images" workflow (v* tags + manual dispatch) builds any
src- image a release ref is missing; it skips ones already published.
The live v1 tag is not moved.
Refs #19
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>