{ "name": "pg_agent_gate", "abstract": "Agents propose, PostgreSQL decides: a native gate between LLM agents and the database", "description": "An LLM agent connected to PostgreSQL today usually goes through an MCP server that hands it tools and runs whatever it asks. pg_agent_gate inverts that inside the database. An agent does not run SQL: it has seven verbs -- discover what it may touch (derived from the live catalog and its own privileges), propose one statement, dry_run it to see the exact effect with the before and after of every row, commit it, propose_and_commit both in one call and one WAL flush, read its own acts, and ask whoami. PostgreSQL verifies every proposal against itself before anything runs: its own parser (exactly one statement), the kind of statement against what the agent may do, its own analyzer and rewriter (every table, column, type and function resolves, privileges first) and, on that tree and before anything is planned, the refusals: a writing CTE, set_config(), an unsafe cascade, trigger or rule, an opaque function. Only then does EXPLAIN plan it, because planning runs functions. It then executes with the agent's own privileges inside a subtransaction and keeps or undoes it: a row limit, deferred constraints fired inside the gate, and bound assertions from pg_living_assertions that must still hold after the change. Every proposal and every execution is recorded append-only, including what was checked and why something was refused. The part plain SQL cannot do is why this is an extension with a shared library: in a session that belongs to an agent, parser and executor hooks refuse everything except the verbs -- direct DML, DO blocks, CALL, PREPARE, EXPLAIN ANALYZE, COPY, cursors, writing CTEs, foreign functions next to a verb or as its argument, and direct access to the record. Verified, not claimed: make verify runs 265 checks -- the hand-written suites, attacks on purpose, each judged from a superuser's side of the database -- and passes in a fresh container that only gets the repository. An LLM proposing through the gate against a two-tenant database found two holes (a CTE that writes dodged max_rows; set_config() inside a proposal moved the tenant), and 0.2.1 closes both; 0.2.2 and 0.2.3 then closed an amplification class the same shape -- a cascading foreign key that crossed tenants, a trigger, a rule, an opaque function -- whose regressions run on every push via make fuzz (teeth plus a differential max_rows oracle), not under make verify. 0.2.4 refuses a utility a function reaches from inside the verified statement (a TRUNCATE a CHECK fires, which pg_stat_xact does not count). 0.2.6 decides every refusal before planning (a SECURITY DEFINER function used to run, and leak what it raised, while EXPLAIN constant-folded it) and withholds estimated_rows under row-level security; 0.2.7 stops a SECURITY DEFINER function wherever the agent's SQL reaches it -- through another function, a constraint, a default or a trigger -- with a function-manager hook, before its body runs; 0.2.8 judges the estimate on every relation the plan reads too (an inlined SQL helper, a view that isolates tenants) and keeps the agent's SQL out of parallel workers; 0.2.9 names pg_temp last in every function of the gate (an allow_ddl agent's empty temporary pg_constraint or pg_rewrite hid a cascade or a rule from no_amplification) and refuses to run a bound assertion through a pg_living_assertions older than 0.5.5, which a temporary table of the agent could answer for; 0.2.10 writes to the server log every row of the record that a ROLLBACK, a rolled-back savepoint or a disconnect takes with the caller's transaction (it used to leave nothing that said so). Measured with make bench against an identical role that is not an agent: 0.93 ms extra per read act and 1.8 ms per kept write (the gate's own work in the server is 0.48 ms; most of the rest is writing the record), and 0.24% throughput lost by sessions that are not agents.", "version": "0.2.10", "maintainer": [ "Manuel Reyes " ], "license": "apache_2_0", "provides": { "pg_agent_gate": { "abstract": "Agents propose, PostgreSQL verifies, runs and records -- and an agent session cannot do anything else", "file": "src/lib.rs", "docfile": "README.md", "version": "0.2.10" } }, "prereqs": { "runtime": { "requires": { "PostgreSQL": "18.0.0" }, "recommends": { "pg_living_assertions": "0.5.5" } } }, "resources": { "bugtracker": { "web": "https://github.com/Manuelreyesbravo/pg_agent_gate/issues" }, "repository": { "url": "https://github.com/Manuelreyesbravo/pg_agent_gate.git", "web": "https://github.com/Manuelreyesbravo/pg_agent_gate", "type": "git" } }, "generated_by": "Manuel Reyes", "meta-spec": { "version": "1.0.0", "url": "https://pgxn.org/meta/spec.txt" }, "tags": [ "agents", "llm", "mcp", "ai", "security", "verification", "guard", "auditing", "hooks", "pgrx" ] }