Skip to main content
PostgreSQL is Warden’s reference connector. Everything the documentation describes works here, and is proven against a live server in CI.

What it provides, and who provides it

Parsing

Warden links PostgreSQL’s own parser (pg_query_go), so its understanding of a statement cannot drift from the server’s. A statement is parsed into the server’s parse tree, edited, and deparsed back to SQL. A protobuf reflection walk finds every table reference in that tree — including CTEs, subqueries, FROM lists, USING clauses, and set operations — so nothing reaches the database unaccounted for. Schema-qualified names are checked against the catalog, which is what caught a homoglyph table and a nonexistent one during certification.

Governed relations

Scope values are bound as parameters, never written in as literals. Masking uses HMAC-SHA256 with pads bound as parameters, so the masking key never appears in a statement the server logs.

Execution

  • Reads run in read-only transactions.
  • Changes that need review are previewed in a transaction that is rolled back, then committed in a second transaction that re-verifies the fence with the rows locked.
  • Per-agent concurrency slots with fail-fast checkout, so one agent cannot starve the others.
  • Each transaction sets its own statement_timeout and attribution with set_config.

Setting it up

The recommended role is least-privilege — no superuser, no BYPASSRLS, access only to the schemas it governs:
make pg-fixture creates exactly this for a development database.
Set schemas. Without it the catalog includes every system table — useless to an agent, and a map of the database for anyone who should not have one.

A role per lease

Set lease_role and every lease becomes a real PostgreSQL role. The server then checks each statement against what the lease may do, not what the gateway may do, so a statement that somehow got past every one of Warden’s checks still cannot touch a table agents were never granted.
  1. Make a NOLOGIN base role and grant it what agents may touch. This is the one place agent privileges are decided.
  2. Install the helper functions that create, bar and drop lease roles, and let the gateway call them. The gateway itself never gets CREATEROLE.
  3. Name it in the connection’s options.
Then install warden.create_lease_role, warden.bar_lease_role and warden.drop_lease_role. Copy them from the leased-roles section of certification/profiles/postgres/fixture.sql, changing only the role names if yours differ. They’re SECURITY DEFINER, owned by warden_provisioner, and executable only by the gateway.
What happens then:
  • Issue. Each lease gets a role named warden_lease_<lease id>: NOLOGIN, no superuser, no BYPASSRLS, no CREATEROLE, a member of the base role.
  • Execute. Every transaction under the lease runs after SET LOCAL ROLE to it. That includes the agent’s statement, its preview, and its fence. On this connection a transaction with no leased role is refused; it never runs as the gateway.
  • Revoke or expire. The gateway’s membership in the role is removed first, so no gateway replica can switch to it any more. Every transaction running under it, on any replica, is found by the advisory lock it holds and terminated. Objects the lease created are reassigned to the gateway, so revoking access never deletes data. Then the role is dropped.
  • Restart. Leases that were live when the gateway stopped are revoked, and their roles dropped, before it serves anything.
The gateway proves all of this at startup. It creates a role through the helper, assumes it, and rolls the whole thing back. If any step fails, including missing helper functions, the connection refuses to start and says what is missing. It never falls back to emulated leases. A base role that can log in, is a superuser, bypasses row-level security, or can create roles is also refused, because every lease would inherit it. A lease can carry grants of its own beyond the base role, such as an elevation an operator approved. The gateway can only pass on privileges it holds WITH GRANT OPTION, and a grant it cannot make is refused rather than silently dropped. Only row-level access (read, append, write, delete) is grantable, and a delete grant can’t be limited to fields, because PostgreSQL grants DELETE per table. Schema and admin access never are. Each role carries a comment naming its lease, database and connection. The helper adds the gateway login itself, so the server records who made the role rather than taking the caller’s word for it. The helpers only act on roles made on the current database by the calling login, and Warden also requires the exact lease and connection before it assumes, reuses or drops a role. The warden_lease_ prefix alone proves nothing. If a drop fails, the role stays blocked on the server. The lease is already ended, so the reaper retries the drop on every sweep and at startup, working from the lease store. It only acts on leases its own store says have ended. A role it has no lease for may belong to another gateway replica, so it’s never dropped automatically; drop it by hand if the state it belonged to is gone.
Don’t give the gateway CREATEROLE. A gateway that can create roles can, once compromised, create itself a login role that no lease governs and that survives rotating its own password. The startup ladder flags a gateway that has it.

Known gaps

  • Native row-level security is not used as a second layer beneath governed relations. Planned.
  • Relationship scoping — scoping a child table through its parent — is not supported, so a table with no scopable column of its own cannot be granted safely. warden policy lint flags that combination.