All three reviewers, same substance: 'command -v box' proves the CLI
landed, not that setup-host took effect — and the success line claimed
both. The claim is now split to match its proofs: PATH proves the
install; 'box doctor' (box's own effective-state verdict — the daemon
stays box's domain) gates "host set up". A failed doctor WARNS with the
verdict verb and the manual path, and claims nothing it cannot prove.
Also: the coolify marker guard matched 'role=control-plane ' by its
trailing space, coupling it to the marker's field formatting — a bare
'role=control-plane' line now reads the same (claude-bot's nit), with a
fixture proving it.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Issue #25 named this consumer when it introduced /etc/rig/role: 'rig
<cmd> sanity warnings later (e.g. coolify install on a non-control-plane
box)'. Both coolify verbs now read the marker through the lib's
read_role_marker (RIG_ROLE_MARKER overrides the path for fixtures, repo
precedent) and warn when it names any role but control-plane — the
likeliest story is the wrong SSH session about to put a control plane on
a workload box.
The marker stays advisory: it may be absent (pre-marker boxes,
hand-built boxes) and absence stays silent — warning there would nag
every legitimate run — and a present-but-different marker warns and
proceeds, because an advisory file must never outrank the operator
(contrast close-root, where the marker IS the gate: shutting the root
door blind is irreversible in a way an extra Coolify is not). The check
sits after arg validation and before the root check, so exit codes are
untouched (usage stays 2, the root refusal stays 1) and the harness
proves it non-root.
Tests drive the live matrix through fixture markers (warns on workload,
silent on control-plane and on absence, still exits 1 at the root
check) and pin the warning's presence in both shipped scripts for
root-run environments.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The Coolify control-plane database holds the GitHub App private key, every
registered server's SSH key, and every environment value for every environment
it manages. Backing it up was a manual runbook step, and the dump script lived
in cast — the off-box tool, whose src never references it. It runs on the box,
as root, under a scheduler: that is rig's job description.
It matters beyond tidiness. The dump is forensics, not a restore path — a lost
control plane is rebuilt fresh and reconciled from the manifest. So there will
be a next control-plane box, and as a runbook step it was born un-backed-up,
depending on someone remembering mid-incident. Now it is backed up from birth.
rig installs the machinery and templates /etc/coolify-dump.env empty at 0600,
never reading it back — no credential passes through rig. The script's own
guards make an unfilled file fail the unit loudly rather than ship plaintext.
systemd timer over cron: EnvironmentFile is the right idiom for 0600 secrets,
failures surface in systemctl status instead of being mailed into the void, and
Persistent=true catches a run missed while the box was down.
Two hazards the cast script missed, carried into the unit:
- aws-cli >= 2.23 enables default upload checksums that S3-compatible backends
reject; Debian 13 ships 2.23.6, so the unit defaults both checksum knobs to
when_required.
- A failed pg_dump piped into age still yields a valid, tiny, encrypted file
that uploads cleanly every night and looks exactly like a working backup. The
script now refuses to upload an empty artifact.
Closes#8
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>