Introduction
CLI quick start
Get up and running with pgroles in a few minutes.
Prerequisites
- PostgreSQL 16, 17, or 18 — the versions supported and tested in CI
- Rust toolchain (for building from source)
Installation
Build from source using Cargo:
cargo install --git https://github.com/thepartly/pgroles pgroles-cli
This installs the pgroles binary.
Starting from an existing database?
Use pgroles generate --database-url ... > pgroles.yaml first, then refine the generated flat manifest into profiles and schema bindings.
Create a manifest
Before typing any YAML, here is the role graph the manifest below describes — a single login role with read-only access to one schema.
The role graph you are about to build
One login role, analytics, granted read-only access to the public schema in mydb.
Role
analytics
Analytics read-only role
is granted
Lets the role open a connection to the database.
Lets the role reach the objects that live inside the schema.
Read-only access to every table currently in the schema.
No membership edges yet
This manifest declares no memberships, so analytics holds every privilege directly. When you do add one, the edge runs member -> role: the member inherits the privileges of the role it is granted, never the other way around.
If schema USAGE, table SELECT, and membership still feel like separate pieces, start with The permission chain, then return here to apply the model.
Create a file called pgroles.yaml:
roles:
- name: analytics
login: true
comment: "Analytics read-only role"
grants:
- role: analytics
privileges: [CONNECT]
object: { type: database, name: mydb }
- role: analytics
privileges: [USAGE]
object: { type: schema, name: public }
- role: analytics
privileges: [SELECT]
object: { type: table, schema: public, name: "*" }
Validate the manifest
Check the manifest is valid without connecting to a database:
pgroles validate
Manifest is valid.
1 role(s) defined
3 grant(s) defined
0 default privilege(s) defined
0 membership(s) defined
Plan changes
See what SQL would be generated against a live database:
pgroles diff --database-url postgres://localhost/mydb
This shows the exact SQL statements needed to converge the database to match your manifest.
No changes are made
The diff command (also available as plan) is read-only. It connects to your database to inspect the current state but does not execute any changes.
Apply changes
When you're happy with the plan, apply it:
pgroles apply --database-url postgres://localhost/mydb
Or preview without executing:
pgroles apply --database-url postgres://localhost/mydb --dry-run
Using environment variables
Instead of passing --database-url every time, set the DATABASE_URL environment variable:
export DATABASE_URL=postgres://localhost/mydb
pgroles diff
pgroles apply