Reference

Manifest reference

Complete field reference for pgroles manifests and CLI bundle files.


Top-level fields

All top-level manifest fields are optional:

default_owner: app_migrator      # Owner for ALTER DEFAULT PRIVILEGES
auth_providers: []                # Cloud IAM provider declarations
profiles: {}                      # Reusable privilege templates
schemas: []                       # Managed schemas and schema-profile bindings
roles: []                         # Role definitions
grants: []                        # Object privilege grants
default_privileges: []            # Default privilege rules
memberships: []                   # Role membership edges
retirements: []                   # Safe role removal workflows

auth_providers

Declare cloud authentication providers to document how IAM-mapped roles connect to the database. This is currently informational metadata used for validation and documentation purposes.

auth_providers:
  - type: cloud_sql_iam
    project: my-gcp-project
  - type: alloydb_iam
    project: my-gcp-project
    cluster: analytics-prod
  - type: rds_iam
    region: us-east-1
  - type: azure_ad
    tenant_id: "00000000-0000-0000-0000-000000000000"
  - type: supabase
    project_ref: abcd1234
  - type: planet_scale
    organization: my-org
TypeDescription
cloud_sql_iamGoogle Cloud SQL IAM authentication. Optional project field.
alloydb_iamGoogle AlloyDB IAM authentication. Optional project and cluster fields.
rds_iamAWS RDS/Aurora IAM authentication. Optional region field.
azure_adAzure Active Directory authentication. Optional tenant_id field.
supabaseSupabase PostgreSQL metadata. Optional project_ref field.
planet_scalePlanetScale PostgreSQL metadata. Optional organization field.

Managed service metadata is intentionally narrow

The auth_providers block models the provider types listed above, but not every variant has provider-specific runtime behavior yet. Today the privilege-warning path has explicit detection for RDS/Aurora, Cloud SQL, AlloyDB, and Azure. Supabase and PlanetScale PostgreSQL entries are currently documentation and validation metadata.

default_owner

The default_owner field specifies which role is used as the owner context for ALTER DEFAULT PRIVILEGES statements. This is typically the role that creates objects in your database.

default_owner: app_migrator

Individual schemas can override this with their own owner field.

profiles

Profiles are reusable templates that expand into concrete roles, grants, and default privileges when bound to schemas.

profiles:
  editor:
    login: false
    inherit: false
    grants:
      - privileges: [USAGE]
        object: { type: schema }
      - privileges: [SELECT, INSERT, UPDATE, DELETE]
        object: { type: table, name: "*" }
    default_privileges:
      - privileges: [SELECT, INSERT, UPDATE, DELETE]
        on_type: table
FieldTypeDefaultDescription
loginboolfalseLogin attribute for generated roles
inheritbooltrueInherit attribute for generated roles
grantslist[grant template][]Grants expanded into each bound schema
default_privilegeslist[default privilege template][]Default privileges expanded into each bound schema

The generated role attributes apply only to roles created from schema x profile expansion. One-off roles under roles: still declare their own attributes directly.

schemas

The schemas section declares schemas pgroles should manage and binds profiles to those schemas.

schemas:
  - name: inventory
    owner: app_owner
    profiles: [editor, viewer]
FieldTypeDefaultDescription
namestringrequiredSchema name
profileslist[string][]Profiles to expand for this schema
ownerstringdefault_ownerDesired schema owner; if omitted and default_owner is unset, pgroles only ensures the schema exists
role_patternstring"{schema}-{profile}"Naming pattern for profile-generated roles

When a schema is declared under schemas:, pgroles can create it if it does not exist and can converge its owner with ALTER SCHEMA ... OWNER TO .... pgroles does not drop schemas or reassign ownership of objects inside the schema.

roles

Each role definition specifies a PostgreSQL role and its attributes:

roles:
  - name: analytics
    login: true
    comment: "Analytics read-only role"
  - name: app-service
    login: true
    createdb: false
    connection_limit: 10
    password:
      from_env: APP_SERVICE_PASSWORD
    password_valid_until: "2026-12-31T00:00:00Z"
AttributeTypeDefaultDescription
namestringrequiredRole name
loginboolfalseCan the role log in?
superuserboolfalseSuperuser privileges
createdbboolfalseCan create databases
createroleboolfalseCan create other roles
inheritbooltrueInherits privileges of granted roles
replicationboolfalseCan initiate replication
bypassrlsboolfalseBypasses row-level security
connection_limitint-1 (unlimited)Max concurrent connections
commentstringnoneComment on the role
passwordobjectnonePassword source
password_valid_untilstringnonePassword expiration (ISO 8601)

Roles with login: true can declare a password source. The password value is never stored in the manifest; it is resolved at apply time from an environment variable in CLI mode or from a Kubernetes Secret in operator mode.

Only login: true roles may have a password. Declaring a password on a non-login role is a validation error.

Passwords and drift detection

Because PostgreSQL does not expose password hashes for comparison, password changes always appear in the plan. The diff --exit-code flag treats password-only changes as non-structural; they do not trigger exit code 2.

grants

Grants define object privileges:

grants:
  - role: analytics
    privileges: [SELECT]
    object: { type: table, schema: public, name: "*" }
  - role: analytics
    privileges: [USAGE]
    object: { type: schema, name: public }
  - role: analytics
    privileges: [CONNECT]
    object: { type: database, name: mydb }

The object field specifies the grant target:

FieldDescription
typeObject type
schemaSchema name; required for most types except schema and database
nameObject name, "*" for all objects, or omit for schema-level grants

Supported object type values: table, view, materialized_view, sequence, function, schema, database, type.

pgroles also accepts a quoted legacy "on" key when parsing older manifests, but object is the supported spelling for new manifests and generated output.

default_privileges

Default privileges configure what happens when new objects are created:

default_privileges:
  - owner: app_migrator
    schema: app
    grant:
      - role: app_migrator
        privileges: [SELECT, INSERT, UPDATE, DELETE, TRUNCATE, REFERENCES, TRIGGER]
        on_type: table
      - role: app_migrator
        privileges: [USAGE, SELECT, UPDATE]
        on_type: sequence
      - role: analytics
        privileges: [SELECT]
        on_type: table

If owner is omitted, the top-level default_owner is used.

memberships

Memberships declare which roles are members of other roles:

memberships:
  - role: editors
    members:
      - name: "user@example.com"
        inherit: true
      - name: "admin@example.com"
        admin: true
FieldDefaultDescription
inherittrueMember inherits the role's privileges; omit for PostgreSQL default behavior
adminfalseMember can administer the role

retirements

When removing a role that owns objects, declare a retirement workflow so pgroles can safely clean up before dropping it:

retirements:
  - role: legacy_app
    reassign_owned_to: app_owner
    drop_owned: true
    terminate_sessions: true
FieldTypeDefaultDescription
rolestringrequiredThe role to retire and ultimately drop
reassign_owned_tostringnoneSuccessor role for REASSIGN OWNED BY ... TO ...
drop_ownedboolfalseRun DROP OWNED BY before dropping the role
terminate_sessionsboolfalseTerminate other active sessions for the role before dropping it

Retired roles are included in the inspection scope even though they are absent from the desired role list. The generated plan inserts session termination, REASSIGN OWNED, and/or DROP OWNED immediately before the DROP ROLE statement.

Bundle mode

The CLI can compose a bundle from one root file plus multiple scoped policy documents. Use this when different teams own different parts of the same database policy.

# pgroles.bundle.yaml
shared:
  default_owner: app_owner
  profiles:
    editor:
      grants:
        - privileges: [USAGE]
          object: { type: schema }
sources:
  - file: platform.yaml
  - file: app.yaml

Each source file is a PolicyFragment:

# platform.yaml
policy:
  name: platform
scope:
  roles: [app_owner]
  schemas:
    - name: inventory
      facets: [owner]

roles:
  - name: app_owner

schemas:
  - name: inventory
    owner: app_owner
# app.yaml
policy:
  name: app
scope:
  schemas:
    - name: inventory
      facets: [bindings]

schemas:
  - name: inventory
    profiles: [editor]

Bundle composition is currently a CLI/core feature. The Kubernetes operator still reconciles a single PostgresPolicy resource.

Shared bundle fields

FieldDescription
shared.default_ownerDefault owner context shared across source documents
shared.auth_providersShared auth provider metadata
shared.profilesShared profile registry used by source documents
sourcesRelative file paths to policy documents that will be composed together

Policy fragment fields

FieldDescription
policy.nameHuman-readable source label used in conflict and plan output
scopeThe ownership boundary this document is allowed to manage

Schema scope is split into explicit facets:

FacetDescription
ownerManage schema creation and ownership convergence
bindingsManage profile expansion, grants, and default privileges tied to the schema

Two source documents may reference the same schema only when they manage disjoint facets. If two documents claim the same role, grant, default-privilege rule, membership selector, or schema facet, composition fails before any database inspection begins.