cast destroy — a scoped teardown verb, because "resolve manually" is a UI deletion with no guardrails #43
Labels
No labels
blocked
blocker:ci-red
blocker:conflict
blocker:drill-pending
blocker:unrequested
bug
claimed
documentation
enhancement
epic
merge-next
needs-triage
ready
release
scope:apply
scope:capture
scope:coolify-api
scope:fleet
scope:manifest
scope:secrets
stale
state:addressing
state:bots-reviewing
state:building
state:needs-human
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: heavy-duty/cast#43
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The gap, in cast's own words
applyfails closed on an immutable-field change, and tells you what to do about it (apply.ts:74):There is no runbook, and "resolve manually" is a hand-deletion in the Coolify UI — unscoped, unconfirmed, and against an instance whose token can see every project on it. Battle-testing the first apply means doing that repeatedly. In practice it means deleting the wrong project sometimes, which is how this issue got filed.
The same gap shows up in three other places:
build_pack,type,version, and placement (destination_uuid) are create-time. Getting one wrong means delete + recreate — the only path cast offers is "go do it by hand".What it must NOT be
Not an instance wipe. Servers here are multi-project by design — Coolify gates servers by team, so a box per project is not affordable, and projects share a box with a Docker network each. An instance-scoped destroy on such a box is one wrong
--instanceaway from deleting somebody else's production. The legacy box in this fleet hosts two third-party client sites. There is no use for instance-wide destruction that project-scoping does not cover.Not a flag on
apply. apply never deletes is the invariant that makes it safe to run on a schedule — orphan env vars are reported, never removed; orphan resources are counted, never touched. A--pruneturns one stale manifest plus one routine apply into a wiped production. Separate verb, separate gates, or nothing.Proposed shape
Manifest-scoped. It deletes only the resources the manifest declares, in that project and that environment. Resources it finds but did not declare are reported and left standing — loudly, because that report is also how you discover something was created outside cast.
Reverse dependency order — applications, then services, then databases.
Plan, then typed confirmation of the environment name — the same ceremony
capturealready uses. Names and kinds, never values.Refuses, rather than no-ops:
COOLIFY_READ_ONLYinstance → refuse, asapply/smoke/server addalready do--all→ refuse always. This is the one verb that must never iterate a fleet.A prod interlock that lives in state, not in argv. A
--yes-style flag is not a gate; it is a thing you type without reading. Put it where a human has to commit it:Same reasoning as
forbidden_var_patternsliving in private state rather than the product manifest: a change on one side must not be able to lower its own guard. It istrueon prod today, while the box is empty and being battle-tested. The cutover checklist deletes the line the moment prod carries real data — and from then on destroying prod requires an operator to edit state, commit it, and merge it. That is the correct amount of friction for a verb that ends companies.Say what is being destroyed, in terms of what it costs. A database line should carry whether it has a backup schedule and when the last backup landed — the difference between "recreate this" and "this is unrecoverable" is exactly the thing the operator needs at the confirmation prompt and cannot see in a UUID.
Not in scope, but adjacent
Once
destroyexists, the natural follow-on for the immutable-field case isapply --recreate <resource>— destroy one resource and recreate it from the manifest in a single act, so the fix for "you setbuild_packwrong" stops being a two-verb dance with a window where prod is missing an app. Worth a separate issue, and worth not building untildestroyhas been used a few times.