Agent access.

Give each AI agent an allowlist on a database: the tables and columns it may use, read only or read-write. Everything else is denied for that identity. This page says what each connection mode guarantees, and what it does not.

Agent access: product overview →

Summary

An agent with database access can receive instructions injected into any text it reads: a ticket, an email, a row. Vericto never sees the prompt, only the SQL, so it does not try to detect the injection. It limits what a successful one can do: the agent uses only the tables and columns you gave it.

The decision is taken on the AST of each query, like any other rule: the same query always gets the same decision. Whatever falls outside the allowlist is reported under rule VERICTO-087.

Availability. Team and Enterprise plans. Policies are enforced in the Vericto TCP proxy and evaluator. The name rules on this page (entries without a schema, case by dialect, PREPARE/EXECUTE and the default schema) come with vericto-engine 3.8.1, vericto-proxy 4.8.1 and vericto-eval 2.4.1, which are rolling out.

The model

A policy belongs to one identity in one database and denies by default. Each entry is a table (schema optional), its columns (all, *, or a list) and the access: read or read_write. An identity with a policy and no entries can use no table. An identity without a policy behaves as before: allowlists are opt-in per identity.

  • Observe (observe): what falls outside the allowlist is flagged (FLAGGED) and never blocked. A new policy starts this way.
  • Enforce (enforce): it is blocked (BLOCKED, VERICTO-087).
  • DDL (CREATE, ALTER, DROP, TRUNCATE, GRANT…) is always denied to an identity with a policy, and so are the statements that change identity or name resolution (SET ROLE, SET search_path, USE).
  • PREPARE/EXECUTE are not allowed under a policy. The SQL statements PREPARE, EXECUTE and DEALLOCATE are denied (needed: ddl), because the allowlist cannot see the statement they run. Protocol-level prepared statements, the ones drivers and ORMs use, are not affected: the proxy evaluates their text when they are prepared.

What counts as access

  • Every column the statement mentions: the projection, WHERE, JOIN, GROUP BY, ORDER BY, HAVING, subqueries, CTEs, RETURNING and the columns INSERT and UPDATE write. A filter counts because it lets an agent probe values. This is stricter than sensitive columns, which count the projection only.
  • Every table mentioned needs its entry, even when no column is read (SELECT count(*) FROM t).
  • SELECT *, t.* and whole-row references need the table's entry to be *.
  • Writing (INSERT, UPDATE, DELETE, MERGE, COPY … FROM) needs read_write on every target table.
  • System catalogues (information_schema, pg_catalog, mysql, performance_schema, sys) are denied unless an entry names that schema.
  • No schema = the default schema. An entry without a schema is the table in the default schema (public on Postgres, or the one you configure), qualified or not in the query; not the table in any schema. A table in another schema needs an entry naming that schema. When in doubt, it is denied: an unqualified column in a multi-table query must be allowed in all of them. Qualify the column when the message asks.
  • Names compare the way your database compares them. On Postgres, User (created quoted) and user are two tables, and two columns too; the tree shows them as traffic does. On MySQL, table and database names compare exactly and column names ignore case.
  • Under an enforce policy, a query that cannot be parsed is blocked: nobody can show it stays inside the allowlist.

Precedence with sensitive columns

A blocked or masked column stays blocked or masked even when allowed. In order:

  1. A sensitive block tag always wins (VERICTO-085).
  2. Outside the allowlist: denied (VERICTO-087). Under observe it is flagged and evaluation continues.
  3. Allowed but tagged mask: returned masked, and the event is attributed to VERICTO-085.
  4. Otherwise, allowed.

The identity in each mode

  • Runtime API, CLI, Validation API and MCP: the API key making the call. Use one key per agent. In the CLI, the Validation API and MCP, pass the database_id: without it the key's only policy in the workspace applies, and if it has policies on several databases the request is refused.
  • TCP proxy: the database user of the session (the Postgres startup user, the MySQL handshake user), compared exactly. A policy for user X applies only to sessions as X. For a proxy dedicated to the agent, create the policy for the user it connects as.

The guarantee in each mode

In every mode Vericto returns the decision and the application executes the query. Only the TCP proxy sits between the query and the database.

ModeWhat happens
TCP proxyEnforced for sessions as the policy's database user: what is denied under enforce gets a native error and never reaches the database
Runtime APIThe decision for the calling API key is returned (403 with VERICTO-087 and access_denied); the application executes
MCPThe decision for the agent's API key is returned; the agent executes
CLI / Validation APIThe check fails when a statement falls outside the allowlist under enforce

The allowlist is a control at the TCP proxy and a decision in the other modes. An agent "cannot" use a table only if it reaches the database solely through the proxy, as the user the policy names.

How to configure it

In Databases → a database → Data access → Agent access: choose the identity (an API key of the workspace or a database user), the default access (read only or read-write) and check tables and columns in the observed schema. Sensitive columns are marked in the same tree (◐ mask, ⛔ block). Creating and editing policies is for the owner and admins; the rest of the team sees them.

The schema comes from traffic. Vericto never connects to your database: the tree shows the tables and columns your recent queries mentioned. A table that is not there can be added by hand. Two templates help you start: "read-only: these tables" and "everything except these tables".

Changes reach every proxy on its next rule sync, without a restart, and the API uses them on the next evaluation.

Default schema

In Agent access, Default schema/database says what an entry without a schema means in this database. MySQL: the database your app connects to. Postgres: usually public, which is what applies when it is empty. On MySQL, empty means an entry without a schema only covers unqualified names. The owner and admins set it, and every change goes to the audit log.

For TCP proxy sessions on MySQL, the connection's database wins. The proxy uses, for each session, the database the client connected with, because it is the one that session uses; the dashboard value applies when the client connects without a database, and in the Runtime API, the CLI, the Validation API and MCP. A later USE is denied.

Prisma. On MySQL, Prisma qualifies every table with the database name (`shop`.`User`): set shop as the default database and entries without a schema cover it. On Postgres, Prisma quotes every name ("public"."User"): the table is User with a capital U, a different table from user, and it shows that way in the tree. When traffic shows a table both with and without the default schema (public.orders and orders), the templates add a single entry without a schema.

Simulate before enforcing

Simulate replays that identity's queries on that database from the last 7 days (up to 2,000) with the draft, without saving it, and says how many it would have blocked, with links to each event. It is an evaluation only: nothing is sent to your database and nothing is recorded. With telemetry in sanitized mode the sanitized text is replayed: literals change, but tables, columns and statement kinds are kept, which is what an allowlist decides on.

The recommended order: create the policy in observe, let the agent work, simulate, adjust and switch to enforce.

Audit trail

Every query carries its identity in the audit trail: the API key or database user that issued it, the mode of its policy and what was denied (tables and columns, never values). Every change to a policy (created, edited, mode switched, deleted) goes to the workspace's administrative audit log, with who made it and the before and after.

Limits

  • It is defence in depth: your database's roles, grants and row-level security remain the first line.
  • Vericto does not know your schema: a view, function or trigger over a table is not resolved to that table. A view is allowed on its own.
  • Function calls are not restricted here.
  • If you rename a table or column, the allowlist does not update itself.