A database is part of the environment, not a footnote

Backing services as first-class objects — how they are declared, why they move by release rather than by commit, and the two ways they bite.

A Resource is a backing service the platform provisions: a Postgres, an object store, a Zulip bot identity, a whole Helm chart. It is declared once and bound per environment, exactly like a component — and it is deployed estate, not configuration.

Components and resources together, because that is what an environment is.

Everything in one cell →

It moves by release, not by commit

A component has a commit, because something built it. A resource does not — it moves by ResourceRelease, which is the identity a resource actually has.

That is why a resource row shows a release where a component shows component@sha. Rendering a database through the component columns gives an empty Commit, a dash for Built by, and "internal" for an endpoint it does not have: none of those is false on its own, and together they describe the wrong kind of thing.

Each release diffed against the one before it.

A resource's release history →

Two ways this bites

resourceRelease must be pinned explicitly — it is not resolved to "latest", and a binding without one refuses with spec.resourceRelease is unset.

So editing a resource type does not roll anything. The pins in apps/*/openchoreo/bindings-*.yaml have to be advanced by hand, and until they are, the cluster is running the old definition while the repository describes a new one. Sixteen ResourceReleases for Zulip alone have drifted this way.

A resource binding set to Retain survives the deletion of the thing that owns it. That sounds safe and is: a terminating resource breaks dependency resolution for everything that binds it, so the symptom shows up on a component that was never touched.

Review environments set Delete deliberately — a pull request's database should not outlive the pull request.

An environment can be nothing but resources

chat owns Zulip through a Helm chart resource and declares no components at all. A board built from component rows alone reported "nothing is bound in any environment yet" about an environment with a running Zulip in it.

note

Which is the general shape of the mistake worth avoiding here: treating backing services as configuration attached to a component, rather than as things that are deployed, versioned, and capable of being the only occupant of an environment.