Reference
Architecture
pgroles is a Rust workspace with four crates, each with a clear responsibility.
Crate overview
pgroles-core
The core library with no database dependencies. Contains:
- Manifest parsing (
manifest.rs) -- YAML deserialization, profile expansion, validation. IncludesAuthProvidermodel for managed PostgreSQL provider metadata. - Role graph model (
model.rs) -- normalized representation of roles, grants, default privileges, and memberships - Diff engine (
diff.rs) -- compares twoRoleGraphinstances and produces an ordered list ofChangeoperations. Changes areserde::Serializefor JSON output. - SQL generation (
sql.rs) -- rendersChangeoperations into PostgreSQL DDL statements. UsesSqlContextfor version-dependent rendering (PG 14/15 legacy syntax vs PG 16+WITH INHERIT/WITH ADMIN). - Export (
export.rs) -- converts aRoleGraphback into a flatPolicyManifestfor brownfield adoption (generatecommand).
All types use BTreeMap and BTreeSet for deterministic output ordering.
pgroles-inspect
Database introspection via pg_catalog queries. Connects to a live PostgreSQL database and builds a RoleGraph representing the current state. Uses sqlx with tokio for async database access.
Inspects:
- Role attributes (
pg_roles) - Object privileges (
information_schema.role_table_grants, etc.) - Default privileges (
pg_default_acl) - Memberships (
pg_auth_members)
Also provides:
- Version detection (
version.rs) -- queriesserver_version_numto determine PG major version for syntax adaptation - Cloud provider detection (
cloud.rs) -- detects whether the connecting role is a true superuser, an explicitly recognized managed-service admin role (rds_superuser,cloudsqlsuperuser,alloydbsuperuser,azure_pg_admin), or a regular user. Validates planned changes against detected privilege level. - Unscoped introspection (
inspect_all) -- discovers all non-system roles for thegeneratecommand
pgroles-cli
The command-line interface. Thin wrapper that:
- Reads and validates the manifest (via core)
- Inspects the database (via inspect)
- Computes a diff (via core)
- Renders and/or applies the changes
pgroles-operator
A Kubernetes operator that reconciles PostgresPolicy custom resources against PostgreSQL databases using the same core manifest, diff, and SQL engine as the CLI.
Data flow
Workspace data flow
The CLI and operator both feed the same manifest, inspection, diff, and SQL rendering pipeline.
Inputs
Desired state
- YAML manifest or PostgresPolicy spec
- Profiles, schemas, grants, retirements
- Validation at parse time
pgroles-core
Manifest -> desired RoleGraph
- Parse PolicyManifest
- Expand profiles across schemas
- Normalize into desired RoleGraph
pgroles-inspect
Database -> current RoleGraph
- Inspect roles, grants, defaults, memberships
- Detect managed provider constraints
- Detect PostgreSQL version for SQL context
Diff + render
Convergent change plan
- Compare current vs desired graphs
- Order changes safely
- Render SQL with version-aware syntax
Outputs
Execution surfaces
- CLI diff / apply / generate
- Operator apply or plan mode
- SQL script, status, metrics, and Events
Shared engine
The operator is not a second implementation. It wraps the same manifest expansion, diffing, and SQL rendering code used by the CLI.
Convergent model
Desired state is complete. Anything missing from the manifest is treated as drift and planned for revocation or removal in dependency order.
Version-aware SQL
Inspection discovers the PostgreSQL version before rendering, so SQL stays compatible across the supported server versions.
Convergent diff model
The diff engine treats the manifest as the entire truth. It produces changes in dependency order:
- Creates before grants (roles must exist first)
- Alters for attribute changes on existing roles
- Grants for new privileges
- Default privileges for new default rules
- Membership removes
- Membership adds
- Default privilege revocations
- Revocations for removed privileges
- Drops after revocations (roles must have no privileges first)
apply then executes the rendered plan inside a single transaction so the database does not commit a partially-applied change set.