QueryShield

How do I implement row-level security for LLM agents?

Two layers, both required:

Database RLS (Postgres CREATE POLICY, MSSQL CREATE SECURITY POLICY) ties row visibility to a session variable like current_setting('app.tenant_id'). The agent's DB connection sets this on every request. This catches the agent if it manages to issue a raw query — but only for SELECT/UPDATE/DELETE against tables you've protected.

AST-level policy (the QueryShield approach) inspects the parsed query *before* it hits the DB and rejects anything that doesn't include the required predicate. Example policy:

agents:
  customer-support:
    tables:
      orders: { require: "tenant_id = :ctx.tenant_id" }
      users:  { require: "id = :ctx.user_id" }

The AST layer is faster (no round trip on rejected queries), produces clear error messages the LLM can recover from ("missing required predicate tenant_id"), and works on databases without native RLS (SQLite, MySQL pre-8.0 in some configs, DuckDB). Run both: AST layer rejects 99% of bad queries with explainable errors; RLS is your last-line guarantee.