Giving an agent a database is risky two ways
AI agents increasingly need to read production data — but the two obvious ways to grant access both leak something you cannot take back.
🔑 Credentials leak
Handing an agent a raw connection string exposes the real host, user, and password. Once it lands in a prompt, a log line, or a tool call, the keys to the whole database are out — and rotating them breaks everything downstream.
🫥 Sensitive data leaks
Even with read-only access, one SELECT * pulls emails, phone numbers, tokens, and PII straight into the model's context — where it gets summarized, cached, and potentially memorized.
One gateway in the middle — nothing installed on your DB
maskdb.ai holds the real connection. The agent only ever speaks a structured REST API, authenticated by a scoped token. Every result passes through the masking layer on the way out. You point it at an existing read-only connection string — no extensions, no views, no schema changes.
real creds stay here
+ token · read-only
What the proxy does
Structured queries, no raw SQL
Agents send a JSON query — table, columns, filters — never a SQL string. With no expression surface to exploit, the whole class of substring() / row_to_json / oracle bypasses simply doesn't exist.
Token-scoped & read-only
Each API token is read-only by design and can be scoped, rotated, and revoked independently. The agent never sees the database password, host, or network.
Column-level masking
Configure which columns to redact, hash, or partially reveal. Masking is applied as the query is compiled, so raw values never cross the wire — and masked columns can't be filtered on.
Two planes, two tokens
An admin token provisions; an agent token queries. The control-plane API below is also exactly what the web console runs on. The whole flow is three steps.
Self-register
One API call returns an admin_token — instant, no human gate, abuse-controlled only. An agent can bootstrap itself.
Add a DB + set masking
Register a database by {name, connection_string}, then set one masking baseline per column. This is the single place masking is configured.
Mint agent tokens
Issue read-only agent_tokens scoped to a set of DBs. Each inherits that DB's masking baseline. Revoke any token anytime.
# Authorization: Bearer mk_admin_… { "tables": [{ "table": "users", "columns": [ { "name":"id", "enabled":true }, { "name":"email", "enabled":true, "mask":"email" }, { "name":"api_key", "enabled":true, "mask":"hash" }, { "name":"ssn", "enabled":true, "mask":"redact" }, { "name":"notes", "enabled":false } ] }] } # mask: none | hash | redact | email | null
A small, structured surface
What the agent token uses. Every capability is its own endpoint backed by a fixed, parameterized query — never agent-supplied SQL. A token can span multiple DBs, so each endpoint is db-scoped. Agents introspect the schema, then build queries against it.
# Authorization: Bearer mk_agent_… — no host, no password { "table": "users", "select": ["id", "name", "email", "plan"], "where": { "and": [ { "col":"status", "op":"eq", "value":"active" }, { "or": [ { "col":"plan", "op":"eq", "value":"pro" }, { "col":"credits", "op":"gte", "value":100 } ]} ] }, "order_by": [{ "col":"id", "dir":"asc" }], "limit": 100, "offset": 0 }
{
"rows": [
{
"id": 1042,
"name": "Ethan Zhang",
"email": "e***@vm0.ai",
"plan": "pro"
},
{ /* … */ }
],
"masked": ["email"],
"page": { "limit":100, "offset":0, "returned":37 }
}
You decide what the agent is allowed to see
Masking is set once per column when you register the database — a single baseline every agent token inherits. There's no per-token override to get wrong: if a column is masked, it's masked for every agent that touches that DB.
| id | api_key | |
|---|---|---|
| 1042 | ethan@vm0.ai | sk_live_9f3a2b… |
| 1043 | lancy@vm0.ai | sk_live_77c1de… |
| id | api_key | |
|---|---|---|
| 1042 | e***@vm0.ai | •••••••• |
| 1043 | l***@vm0.ai | •••••••• |
Mask strategies mix per column: full redaction for secrets, partial reveal for emails and phones, deterministic hashing to keep values joinable without exposing them, or drop to omit the column entirely.
Two guarantees, by construction
The real password never leaves the proxy
Agents authenticate with disposable, read-only tokens. Compromise one and you revoke one token — the database credentials, host, and network stay invisible and intact.
Sensitive columns are masked at the source
PII and secrets are redacted as the query compiles, before serialization. Raw values never reach the model's context, logs, or any cache it might write to — and can't be reconstructed through a filter.