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

LOGIN

Analytics read-only role

is granted

CONNECTDatabasemydb

Lets the role open a connection to the database.

USAGESchemapublic

Lets the role reach the objects that live inside the schema.

SELECTTablespublic.*

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