A deploy workflow, started only by hand (or through the API) and only
from master, pulls the chosen image (release tag, sha or latest) and
runs that image's own update.sh against VM103's compose directory
through the Docker socket. update.sh now copies the database through the
running server first, installs the image's compose.yaml and update.sh
with --install, and restores the previous compose file together with
the previous image when the new version does not come up. CI also
builds release tags v*.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A read-only check of the VM103 database (2026-09-23) found nothing that
uses them: every draft, published version and run snapshot is v2, every
function and privileged-action node is schema version 2, and no policy
rule, document or invocation names docker.inspect, preflight, apply or
imagePrune @1. Gone, with their tests moved to v2 fixtures:
- the opaque v1 workflow document and snapshot (and the instance store's
lifecycle-only paths for them; claimNext now always starts a node),
- function and privileged-action nodes of schema version 1,
- docker.inspect/preflight/apply/imagePrune @1 and the v1 prune rule
(with its recentBackup gate); V2 names became the plain ones.
The v1 execution projection stays: one finished run on VM103 has it.
update.sh asks only about running workflow runs now (/api/steps is gone).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The guard only asked for a running step of the old chain, so a deploy
could cut a node of a workflow run in half (the run then waits on
'recovery'). It now also refuses while a workflow instance is running;
a run waiting at a gate or queued between nodes survives a restart and
does not block a deploy. The probe was checked against a mocked fetch.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The compose split 'model' into model-claude/model-codex and added
function-runtime; update.sh still waited for 'model', so every deploy
of the new compose would have rolled back. Also records the VM103
deployment in TASK_STATE.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Dockerfile pins @anthropic-ai/claude-code 2.1.267 in /opt/claude and ships bin/hl. compose.yaml adds
the model service (uid 1000, read-only, no capabilities, no socket, /claude volume for the owner's login,
networks default + executor) and gives the driver MODEL_URL; HA_URL/HA_TOKEN/HA_NOTIFY and NIGHTLY_AT
come from .env for the driver and the server's watchdog. update.sh changes only the TAG line of .env
(it used to overwrite the file, which would have dropped HA_TOKEN) and checks the model container.
The CLI runs with HOME at its login and without non-essential traffic (no auto-update, no telemetry).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
compose.yaml: the executor (same image, root, no capabilities, read-only root, docker.sock,
/opt/homelab/compose at the same path, backups 0700, /var/lib/docker read-only for free space) on an
internal network only the driver joins; EXECUTOR_PROJECTS=jackett,ombi,orcaslicer. The driver gets
EXECUTOR_URL and the host record read-only. update.sh also checks the executor. deploy/proxmox: an
hourly timer on the Proxmox host writes the newest vzdump of VM103 (pvesh, JSON) into the VM through
the guest agent, checking the guest's exit code.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
tecnativa/docker-socket-proxy writes haproxy.cfg next to its template at every
start, so read_only made it restart in a loop on VM103 and update.sh still said
ok (it only checked server and driver). The proxy keeps cap_drop ALL,
no-new-privileges, the :ro socket and POST=0.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
compose.yaml now takes the image from the Gitea registry (TAG from .env, default latest).
update.sh refuses to restart while a step is running, waits for healthy and rolls back to
the previous image if the new one does not come up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>