Veil Keys Docs

Database proxy

Letting an AI agent query your production database is genuinely useful — and genuinely terrifying. The agent needs to read real rows to debug a problem, but one stray DROP TABLE, one leaked connection string, or one customer’s email pasted into a chat log is a bad day. The Veil database proxy makes the useful half safe: an agent can run one read-only query at a time against a Postgres service, but it can never write, never escape into dangerous functions, never see the connection string, and never see raw PII.

The agent reaches it through the query_db tool on the agent broker.

AI agent
query_db(SQL)
a SELECT statement
Veil Keys
read-only · masked
DSN injected · ████
Postgres
your database
The agent sends SQL; Veil opens the connection with your stored DSN and returns masked rows. The connection string never travels back to the agent.

Enforced read-only — two locks, not one

A read-only intent isn’t enough; the proxy enforces it structurally. Every statement runs inside a READ ONLY transaction and passes a statement guard before it executes. The guard refuses:

  • writes and DDL: INSERT, UPDATE, DELETE, DROP, ALTER, TRUNCATE, CREATE, GRANT
  • multiple statements in one call (no ; smuggling)
  • SELECT … INTO (a disguised write)
  • volatile, file, and network functions — pg_sleep, dblink, lo_*, pg_read_file, and friends

Only genuine reads get through: SELECT, WITH, EXPLAIN, and SHOW. A per-service statement timeout stops runaway queries, and results are capped (a per-service default, up to 1000 rows).

PII masking — before the agent sees a single row

Read-only isn’t private. A plain SELECT email FROM users would still leak every customer’s address. So the proxy masks sensitive data after the query runs and before the result is serialized to the agent. It masks on three independent signals:

  • Column name looks like PIIemail, password, ssn, card, phone, and similar — even when hidden behind a column alias.
  • Value looks sensitive — anything shaped like an email address, a JWT, or a card number, wherever it appears.
  • Whole JSON / record columns — masked entirely, since structured blobs routinely carry secrets.
query_db · masked result
agent: query_db("SELECT id, full_name, email FROM users LIMIT 2")
→ 200 OK · 2 rows · masked
id | full_name | email
----+----------------+----------------------
1 | Ada Lovelace | ad••••st@ex••••.com
2 | Alan Turing | al••••ng@ex••••.com
// values masked before the rows ever reached the model

The agent learns the shape of your data — which rows exist, how columns relate, how many records match — without learning the sensitive contents. That’s usually exactly enough to debug.

What never crosses the wire

Set it up

  1. In the Veil app, add a database service and paste the Postgres connection string (ideally for a read-only role). It’s encrypted at rest immediately.
  2. Grant your agent’s token use on the workspace that holds it. To make the agent strictly read-only across the board, scope its token to query_db and GET.
  3. The agent discovers the service via list_services and queries it with query_db. No host, no DSN, no password ever appears on the agent’s side.

Pair this with policies & approvals to require human sign-off on database access, and see the agent broker for the full tool set.