Commit graph

5 commits

Author SHA1 Message Date
ddd8462f32 chore: re-home the control-plane dump to rig
scripts/dump-coolify-db.sh ran `docker exec coolify-db pg_dump` — it only works
ON the control-plane box, as root, under a scheduler. cast is the off-box tool:
its own README says "cast runs on your machine: it is an API client, and a
server should never install it." The script contradicted that invariant, and
cast's src/ never referenced it — it was payload that survived the rig/cast
split without being re-homed.

It now lives in rig as `rig coolify backup install` (heavy-duty/rig#9), which
also installs age + awscli, a systemd timer, and a templated 0600 bindings file
— none of which cast could do from off the box.

Two defects were fixed in the move rather than carried over: the script now
refuses to upload an empty artifact (a failed pg_dump piped into age still
yields a valid, tiny, encrypted file that looks exactly like a working backup),
and the unit defaults the aws-cli >= 2.23 checksum knobs that S3-compatible
backends reject.

The two scripts that remain are genuinely cast's: both drive the Coolify API or
reach a box over SSH, from off the box. README now states that boundary so the
next on-box script does not land here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-12 19:18:46 +00:00
4b1eed7b2a fix(install): wire BINDIR onto PATH in the shell profile, not a warning
A curl|bash installer runs in a subshell, so the old "add this to your PATH"
warning left every fresh workstation one manual step short of a working cast —
hit for real standing up the coolify box (prod-migration Task 6 step 0).

The installer now appends the export to whichever profile $SHELL actually
reads (.zshrc / .bashrc / .bash_profile on macOS / config.fish), once, marked
"# added by cast-install". Already-on-PATH is a no-op; CAST_NO_MODIFY_PATH=1
opts out and restores the old warn-only behaviour.

Also: the "age not found" warning now names the install command per platform —
age is required before any apply, and the bare warning read as ignorable.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 15:12:14 +00:00
75fdd7cfe4 docs: add semantics.md — the apply contract and Coolify 4.1.2 limits
This should have shipped with the extraction PR and did not: the file was
written after that commit was already pushed, so it landed nowhere. The
infra PR meanwhile removed the same content from that repo's README, so
between the two merges these notes existed only in git history.

They are the expensive part of the executor — apply's guarantees (never
deletes, never recreates a database, fails loudly on un-updatable drift),
the dockercompose build pack, hostname-overlay shapes, and each place
Coolify 4.1.2 misbehaves, with citations verified against coollabsio/coolify
v4.1.2 and the vendored OpenAPI rather than its published docs.

Linked from the README so it is found before someone changes apply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 13:13:19 +00:00
a10349d835 feat: cast — the Coolify executor, extracted from the infra state repo
Public tool, private state. cast holds no hostnames, no bindings, no
secrets: it joins a product repo's .infra/ manifest with a state directory
you point it at, and makes Coolify match.

Extracted from heavy-duty/infra, which was half tool and half state — the
inconsistency that made it impossible to say whether "infra" named a CLI
or a runbook. rig builds the boxes; cast fills them; infra is what they
are filled with.

Two changes were required to make it genuinely stateless and publishable:

- The implicit cwd contract (environments.yaml / secrets/ / .coolify.env
  resolved against the working directory, silently reading the wrong file
  from the wrong place) is now an explicit --state <dir> / $CAST_STATE.
- BANNED_IN_PROD — a hardcoded list of one product's ALLOW_* flags, the
  only product knowledge in the executor — becomes the generic, operator-
  owned environments.<env>.forbidden_var_patterns. The guard now lives in
  private state, so a product-side change cannot lower its own guard, and
  it is a pattern rather than a list, so it catches unforeseen siblings.

Age identities resolve as $CAST_AGE_KEY_FILE_<ENV> then
~/.config/cast/age-<env>.key — which is the entire attended-vs-unattended
apply mechanism, with no environment names known to the tool.

Instance identity (org names, the GitHub App name, founder domains) is out
of the fixtures and out of register-github-app.sh, which took APP_NAME and
ORG as arguments rather than baking them in.

69 tests green; bin/cast + curl installer mirror rig's shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 12:25:44 +00:00
Daniel Marin
c64c339b19
Create README.md 2026-07-11 13:15:25 +01:00