Cluster-level surface
pgevolve manages cluster-level state — roles (v0.3.0), with tablespaces, cluster settings, foreign servers, and user mappings planned — through a parallel project type and command family separate from per-database projects.
Project shape
my-cluster/
pgevolve-cluster.toml
roles/
app.sql
ops.sql
Commands
pgevolve cluster init [path]— scaffold a new cluster projectpgevolve cluster diff— show diff between source and live clusterpgevolve cluster plan— write a cluster plan directorypgevolve cluster apply [<plan_id>]— apply a cluster planpgevolve cluster status— list applied/pending plans
Tests: tier-4: crates/pgevolve/tests/cluster_api.rs::build_cluster_plan_empty_roles_dir, build_cluster_plan_surfaces_role_loses_superuser_finding, build_cluster_plan_creates_new_role; tier-1: crates/pgevolve/src/commands/cluster/*::tests, crates/pgevolve/src/cluster_config.rs::tests.
Currently managed
| Object | Status | Tests |
|---|---|---|
| Roles (CREATE/ALTER/DROP ROLE, CREATE USER) | ✅ v0.3.0 | tier-1: crates/pgevolve-core/src/ir/cluster/role.rs::tests, parse/cluster/*::tests; tier-2: crates/pgevolve-core/tests/cluster_parse.rs, cluster_catalog.rs; tier-C: cluster/roles/create-simple-role, cluster/roles/create-login-user, cluster/roles/alter-role-attributes, cluster/roles/drop-role-intent-gated |
| Role membership (GRANT role TO target) | ✅ v0.3.0 | tier-C: cluster/roles/add-membership; tier-4: crates/pgevolve/tests/cluster_api.rs::build_cluster_plan_creates_new_role |
| Tablespaces | 🔮 Future | |
| Cluster GUCs (postgresql.conf) | 🔮 Future | |
| Foreign servers / user mappings | 🔮 Future | |
| Databases list | 🔮 Future |
Passwords
Passwords are not stored in source. The catalog reader skips
rolpassword; the source parser drops PASSWORD '…' clauses
silently. Set passwords out-of-band (psql, secret manager, etc.).
Tests: tier-1: crates/pgevolve-core/src/parse/cluster::tests (PASSWORD-clause silent drop); tier-2: crates/pgevolve-core/tests/cluster_parse.rs.
Bootstrap roles
The [bootstrap].roles list in pgevolve-cluster.toml names roles
that pgevolve treats as PG-owned and never diffs in or out. Defaults
to ["postgres"]. Cloud Postgres (RDS, Cloud SQL, etc.) typically
needs additional entries (e.g. ["postgres", "cloudsqlsuperuser"]).
Tests: tier-1: crates/pgevolve/src/cluster_config.rs::tests; tier-1: crates/pgevolve-core/src/diff/cluster.rs::tests (bootstrap-role filter behavior).
Linking from per-DB projects
Per-DB projects can lint-check grantee role names against the cluster
project by setting [cluster].project = "../my-cluster" in
pgevolve.toml. See docs/spec/grants.md for details.
Known limitations (v0.3.0)
- Cluster apply does not yet write to a per-DB-style
pgevolve.apply_log. Thepgevolve cluster statuscommand lists plan directories rather than reading applied state from the DB. Will be addressed when the cluster executor reaches feature-parity with the per-DB one. - DROP ROLE steps are marked
destructive: truein the emit pipeline, but cluster apply does not yet readintent.tomlto gate them — it executes whatever is inplan.sql. Operators should reviewcluster-plans/<id>/plan.sqlbefore runningpgevolve cluster apply. - No advisory lock is taken during cluster apply; concurrent applies against the same cluster are not protected.
- Object-level GRANT/REVOKE (e.g.
GRANT SELECT ON TABLE) is per-DB, not cluster. It ships in v0.3.1. - Row-level security policies ship in v0.3.2.