Every statement an agent sends through the API or MCP is parsed with the Postgres parser and checked against its key before it runs.
The firewall is the second layer: the first is the agent's own Postgres role, so anything the firewall would miss still fails with "permission denied" in Postgres.
#Always refused
- Transaction control (
BEGIN,COMMIT, savepoints): each request runs in one transaction. SET/RESET(includingSET ROLEandset_config()).DOblocks,COPY,PREPARE/EXECUTE,CALL,LOCK,LISTEN/NOTIFY, rules, roles, ownership changes,VACUUM.- SQL from strings (
query_to_xmland friends), file and large-object functions,dblink, HTTP from SQL, backend control functions. - Platform schemas (
authunless allowed,vault,cron,net,pgmq, the undo log), and denied or not-allowed tables. - Schema changes, unless the key has the migrations preset; disabling triggers and StitchDB's own triggers, even for migrations keys.
#Checked per key
- Operations:
SELECT/INSERT/UPDATE/DELETE, including writes hidden in aWITHclause andEXPLAIN ANALYZE. UPDATE/DELETEwithout a realWHERE(alsoWHERE trueandWHERE 1 = 1).- Rows changed per statement (counted by the database, including rows changed by triggers).
- Calls to your own functions.
#What the agent sees
JSON
HTTP 403
{ "error": "Table public.payments is denied for this agent key",
"code": "agent_firewall", "rule": "table_denied", "statement_index": 0 }Clear reasons let an agent correct itself instead of retrying blindly.