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
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_timeoutand attribution withset_config.
Setting it up
The recommended role is least-privilege — no superuser, noBYPASSRLS, 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
Setlease_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.
- Make a
NOLOGINbase role and grant it what agents may touch. This is the one place agent privileges are decided. - Install the helper functions that create, bar and drop lease roles, and let the gateway call them. The gateway itself never gets
CREATEROLE. - Name it in the connection’s options.
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.
- Issue. Each lease gets a role named
warden_lease_<lease id>:NOLOGIN, no superuser, noBYPASSRLS, noCREATEROLE, a member of the base role. - Execute. Every transaction under the lease runs after
SET LOCAL ROLEto 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.
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.
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 lintflags that combination.