Show HN: Vericto – Check AI agents' SQL before Postgres or MySQL runs it Vericto released vericto-proxy 4.15.2, a source-available SQL firewall that sits between clients and PostgreSQL or MySQL on the database's own wire protocol and blocks statements matching configured rules before they execute. The proxy parses each query with the vericto-engine AST parser and returns a native error — SQLSTATE 42501 on PostgreSQL or ERROR 1142 on MySQL — so applications only change the host and port in their connection string; a demo shows a DELETE without a WHERE clause blocked with error VERICTO-001 while a DELETE with a WHERE clause runs. The software is licensed under FSL-1.1-ALv2, which permits free self-hosting including production use at for-profit companies but bars competing commercial products, with each release converting to Apache-2.0 two years after release; one instance fronts one database and speaks one protocol set at deploy time via VERICTO_WIRE_PROTOCOL, and Oracle, SQL Server, Snowflake and SQLite are evaluated through the HTTP API, CLI or MCP instead. Stops destructive SQL before it reaches your database, whether it comes from AI agents, scripts or people, with no code changes. AI agents, scripts and people all run SQL against production with real credentials. Nobody reads each statement before it runs, and a database role decides what a user may touch, not whether this particular statement should run. One DELETE without a WHERE empties a table. A SELECT of the wrong columns copies emails or card numbers to wherever the result goes. vericto-proxy sits between your clients and the database, on the database's own wire protocol. It parses every query with the vericto-engine https://github.com/vericto/vericto-engine AST parser, checks it against your rules, and either forwards it to the real database or blocks it with a normal SQL error. Your application only changes the host and port in its connection string. License. Source-available under the FSL-1.1-ALv2 https://github.com/vericto/vericto-proxy/blob/main/LICENSING.md . You can self-host it and use it for free, including in production at a for-profit company. What it does not allow is a Competing Use: making it available to others in a commercial product or service that substitutes for it or offers substantially similar functionality, such as a SQL firewall service built on it. Each release also becomes Apache-2.0 two years after Vericto makes it available. LICENSING.md https://github.com/vericto/vericto-proxy/blob/main/LICENSING.md answers the common questions. One proxy instance fronts one database and speaks exactly one wire protocol, chosen at deploy time with VERICTO WIRE PROTOCOL : | VERICTO WIRE PROTOCOL | Databases | Block response | |---|---|---| | postgres default | PostgreSQL | native ErrorResponse SQLSTATE 42501 | | mysql | MySQL | native ERR Packet ERROR 1142 | Everything else — configuration, evaluation, telemetry — is the same across engines . Only the value above and optionally the default ports change. No AI, no stochastic heuristics — the same input always produces the same result. Other dialects Oracle, SQL Server, Snowflake, SQLite : evaluate via the HTTP API https://vericto.com/integration-guide http-setup , the CLI or MCP; there is no wire proxy for them. Snowflake has no TCP wire protocol at all, so VERICTO WIRE PROTOCOL=snowflake stops the proxy at startup with an error. SQLite is a library linked into the application, with no wire protocol either: its SQL is checked in the application, before it runs. All you need is Docker: no account, no API key, no build. This starts a throwaway Postgres, puts the proxy in front of it with the built-in ruleset, and sends a few queries through it with the psql that ships in the Postgres image. The password demo is only for this disposable database. docker network create vericto-demo docker run -d --name vericto-demo-db --network vericto-demo \ -e POSTGRES PASSWORD=demo postgres:17 docker run -d --name vericto-demo-proxy --network vericto-demo \ -e UPSTREAM HOST=vericto-demo-db vericto/vericto-proxy:4.15.2 until docker exec vericto-demo-db pg isready -q -h localhost -U postgres; do sleep 1; done docker run --rm --network vericto-demo postgres:17 \ psql postgresql://postgres:demo@vericto-demo-proxy:5433/postgres \ -c "CREATE TABLE users id int PRIMARY KEY, email text " \ -c "INSERT INTO users VALUES 1, 'ana@example.com' , 42, 'bo@example.com' " \ -c "DELETE FROM users" \ -c "DELETE FROM users WHERE id = 42" \ -c "SELECT id, email FROM users" The image comes from Docker Hub; ghcr.io/vericto/vericto-proxy:4.15.2 serves the same digest see Docker docker . The DELETE without a WHERE never reaches the database; the one with a WHERE does: CREATE TABLE INSERT 0 2 ERROR: Vericto blocked this query VERICTO-001 — AST node: DeleteStmt WhereClause = NULL. Suggestion: DELETE FROM users WHERE id = $1 DELETE 1 id | email ----+----------------- 1 | ana@example.com 1 row docker logs vericto-demo-proxy shows the decision. To put the proxy in front of your own database, see Docker docker and Connection strings connection-strings . Clean up with: docker rm -f vericto-demo-db vericto-demo-proxy && docker network rm vericto-demo Your app / ORM │ │ SQL wire protocol proxy listen port, default 5433 ▼ vericto-proxy ──── vericto-engine lib ────► ALLOWED / BLOCKED │ │ if ALLOWED forwards query ▼ Real database upstream Point your DATABASE URL host at vericto-proxy instead of your real database. No code changes required — your ORM/driver is unaware of the proxy. With the built-in ruleset, these never reach the database: - DELETE / UPDATE without WHERE - DROP TABLE , DROP SCHEMA , TRUNCATE - OR-tautology SQL injection WHERE id = $1 OR 1=1 - ALTER TABLE DROP COLUMN / RENAME - … full rule list → https://vericto.com/rules-reference Lower-severity findings — SELECT without WHERE , SELECT without LIMIT , INSERT without an explicit column list — are forwarded and recorded rather than blocked. With the control-plane link on, the workspace policy decides the action for each severity. A blocked query is returned as a native error for the active protocol see the table above , so no special handling is needed in your application. Columns can be tagged as sensitive per database in the Vericto dashboard Team and Enterprise plans . The tags reach the proxy with the ruleset /sync/rules , so the control-plane link and VERICTO DATABASE ID are required and are enforced on the wire, before the database sees the query. When a query reads several tagged columns the strictest policy wins block mask flag . The proxy enforces: unlike the Validation API or the CLI, which only advise, what the proxy decides is what the database receives. | Policy | Postgres | MySQL | |---|---|---| | block | The query never reaches the database: ErrorResponse , SQLSTATE 42501 | Never reaches the database: ERR Packet , ERROR 1142 | | flag | Forwarded as sent, recorded as FLAGGED | Same | | mask | The proxy forwards a rewritten query in which the column is masked full , last4 , email or hash under its own name, and never the original. The client receives masked values. | Same: the proxy forwards the rewritten query in the same COM QUERY or COM STMT PREPARE , and never the original. | If a rewrite does not hold together, the proxy blocks with VERICTO-085 rather than forward the original. SELECT over a table with a masked or blocked column is blocked; list the columns. The full behaviour both protocols, prepared statements, fail-safe cases, parse errors, telemetry, known limits is in docs/SENSITIVE COLUMNS.md https://github.com/vericto/vericto-proxy/blob/main/docs/SENSITIVE COLUMNS.md . An allowlist per database user Team and Enterprise plans , set in the Vericto dashboard: which tables and columns that user may read, and which it may write. Everything else is denied VERICTO-087 . The policies reach the proxy with the ruleset /sync/rules , so the control-plane link and VERICTO DATABASE ID are required , keyed by database user, with an optional " " default for every user not listed. The identity of a session is the database user it authenticated as, so give each agent its own database user and a policy for that user. Under a policy, every reference counts WHERE , JOIN , subqueries, RETURNING , not only what the query returns , DDL is always denied, and the identity cannot be changed from inside the session SET ROLE , USE , COM CHANGE USER and the like are denied . A policy in observe mode records what it would deny and forwards the query. The full behaviour name resolution, writes and locks, policy changes on open sessions, telemetry, known limits is in docs/AGENT ACCESS.md https://github.com/vericto/vericto-proxy/blob/main/docs/AGENT ACCESS.md . All variables are dialect-agnostic — the same names apply to every engine. | Variable | Description | |---|---| | UPSTREAM HOST | Hostname of the real database to proxy to | | Variable | Default | Description | |---|---|---| | UPSTREAM PORT | protocol default¹ | Port of the upstream database | | PROXY LISTEN PORT | protocol default¹ | Port the proxy listens on | ¹ Defaults follow VERICTO WIRE PROTOCOL : Postgres 5432 upstream / 5433 listen; MySQL 3306 upstream / 3307 listen. The rest is optional and documented in docs/CONFIGURATION.md https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md : - Control-plane link https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md control-plane-link-telemetry--rule-sync VERICTO API URL , VERICTO API KEY , VERICTO DATABASE ID , all three together : reports decisions to the Vericto API and syncs the workspace's rules and policy. Without it the proxy still protects with the built-in ruleset, and reports nothing. Also covers the telemetry buffer, over the monthly quota https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md over-the-monthly-quota , disk spool size https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md disk-spool-size and several proxies on one volume https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md several-proxies-on-one-volume . - TLS https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md tls-optional on each hop, client→proxy and proxy→database. On MySQL, TLS must be on both hops or neither. - Query size limit https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md query-size-limit VERICTO MAX QUERY BYTES , 10 MiB by default . - Business policy rules https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md business-policy-rules-preview-off-by-default VERICTO POLICY RULES , off by default : a preview, off unless you turn it on; with it off the proxy behaves exactly as 4.14.3. - Health check https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md health-check-optional for load balancers VERICTO HEALTHZ PORT . - Observability https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md observability : logs, and metrics https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md metrics as Prometheus or CloudWatch EMF. - Engine-specific notes https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md engine-specific-notes , including MySQL details https://github.com/vericto/vericto-proxy/blob/main/docs/CONFIGURATION.md mysql-details TLS, protocol compression . To run from source and run the tests, see CONTRIBUTING.md https://github.com/vericto/vericto-proxy/blob/main/CONTRIBUTING.md run-from-source . Release images are published on Docker Hub as vericto/vericto-proxy https://hub.docker.com/r/vericto/vericto-proxy from 4.14.1 on and at ghcr.io/vericto/vericto-proxy , for linux/amd64 and linux/arm64 , and pull without credentials. The release workflow copies the multi-arch index it pushed to GHCR, so both registries serve the same digest under the same tags