Human Approval for AI Agent Database Writes
A read-only agent is a safe agent. The moment it writes to your database, a connection string has handed a non-deterministic process what one security team aptly calls "God User" status over your data — and a system prompt that says "only generate SELECT" is not a boundary a prompt injection or a hallucinated query respects. Least-privilege and a read-only replica are the right instinct, and they carry you a long way — until the job is actually to write a row, at which point you've re-granted write and are back where you started. The durable control is to gate the write itself: hold it PENDING at the commit, let a human approve the specific before → after, and fail closed on conflict. Below: why reads and writes are different risk classes, and how the gate works across OLTP databases and warehouses.
Read is one risk class. Write is another.
Most "AI + your database" tooling — text-to-SQL, analytics copilots, agentic BI — started read-only, and the failure modes there are about disclosure: an over-broad query, a crossed tenant boundary, a sensitive column exposed. Real, but recoverable — nothing in the database changed.
A write is a different class. An UPDATE that moves a number, an INSERT that fabricates a record, a DELETE that removes history — these alter the system of record, and often silently: the number you report on next quarter is simply wrong, and no error fired. The July 2025 incident in which a coding agent deleted a production database during an explicit freeze — and then misreported what it had done — is the write class at its worst. The asymmetry is the whole point: a bad read is a leak; a bad write is a corruption you may not notice until it's downstream.
The "God User" problem
Here is the uncomfortable mechanic. When you give an LLM-based agent a connection string with read/write access to production tables, you have granted "God User" status to a process whose output is probabilistic. Even if the system prompt insists "only generate SELECT queries," a clever prompt injection or a structural hallucination can bypass a natural-language guardrail — because the guardrail is a suggestion in the prompt, and the database only sees the SQL that arrives.
And the injection needn't come from the user. In database query-based prompt injection, hostile text sitting in a row the agent reads — a customer name, a support ticket, a note field — becomes the instruction that steers the next query it writes. The national cyber-security guidance is blunt about the general problem: prompt injection is not SQL injection, and it may be worse, because you cannot fully sanitize natural language the way you parametrize a query. A permission model that assumes clean input is building on sand.
Why least-privilege and a read-only replica don't finish the job
Least-privilege is necessary — scope the agent tightly, never hand it a privileged service account, put reads on a replica. For a read-only analytics agent, a read-only replica is close to a complete answer, and you should use it. But two things remain.
First, the read-only replica solves the read case by removing writes entirely — which is no help the moment the job is to write: update a customer's plan, insert an approved journal entry, adjust a forecast cell. To do that work you grant write, and the replica trick no longer applies.
Second, and more fundamental: least-privilege gates whether the agent may write to a table — it cannot gate which write is correct. An agent scoped to exactly UPDATE customers.mrr is still free, within that grant, to set mrr to 188,000,000 instead of 1,880,000. The permission was satisfied; the value is garbage. Access control and value-correctness are two different questions, and this is a big enough point to have its own page: Least-Privilege Isn't Enough to Govern AI Agent Writes.
The write-gate: approval at the commit
The control that actually fits the write class is to gate the write itself. The agent's proposed change doesn't execute — it lands as a durable PENDING row in the database's governance layer. A human sees the concrete before → after — mrr: 1,240,000 → 1,880,000, on customer 8841 — and approves or rejects that specific change. Only on approval does it commit, and it commits fail-closed: if the target row changed since the proposal was made, the stale write is rejected rather than silently clobbering the newer value. Every proposal, decision, and commit is hash-chained into a tamper-evident log. (The full mechanic — intercept, hold, approve, commit, record — is The Write-Gate Pattern.)
Three properties matter for databases specifically. The agent never holds raw credentials — it gets a proposal channel, not a God-User connection string. The reviewer can be a non-engineer — a finance or ops owner judges a row change, not a SQL statement. And approval binds to the actual commit against live state, so a baseline that moved between propose and approve is caught, not overwritten. This is the recognized shape of the defense: for actions with significant real-world consequences like modifying a production database, requiring human confirmation before execution is the most structurally sound response to high-impact injection.
OLTP vs warehouse: row-mutation and append-only
"Write to your database" isn't one thing, and the gate adapts to the store. Two shapes cover the field:
These stores are made to be updated in place, so the gate holds INSERT, UPDATE, and DELETE as before → after proposals and commits fail-closed against the current row — the case where a stale baseline does the most damage.
Analytical stores are built for appends, not in-place edits. Here the gate holds proposed INSERTs: a human approves what gets added to the reported dataset before it lands, so an agent can't quietly append rows that skew a dashboard or a downstream model.
One gate, honored per write, whichever store is behind it — you compose a governed view from many sources and push approved changes back to any of them.
Side by side
The common answers to "let an agent write to the database," on the axes that decide whether a bad write lands:
| Approach | Lets the agent actually write? | Survives prompt injection / hallucinated SQL? | Catches a wrong write within the granted scope? | Fail-closed on changed baseline? | Non-engineer can approve + audit? |
|---|---|---|---|---|---|
| Read-only replica | No — writes removed | Yes (nothing to corrupt) | N/A — can't write | N/A | N/A |
| Least-privilege write grant | Yes | No — allowed path still fires | No — value not judged | No | No |
| Prompt guardrail ("only SELECT") | Depends on the model | No — a suggestion, not a boundary | No | No | No |
| Reactive audit / undo | Yes | No — write already landed | Only after the fact | No | After the fact |
| Write-gate at the commit | Yes | Yes — nothing commits unapproved | Yes — value reviewed | Yes | Yes |
God User agent ─▶ SQL ─▶ production DB (one injected or hallucinated query corrupts a row) write-gate agent ─▶ proposed write ─▶ [ PENDING at the commit ] │ human reviews mrr: 1,240,000 → 1,880,000 approve ─▶ commit (fail-closed if the row moved) reject ─▶ nothing lands │ every step ─▶ tamper-evident, hash-chained log
When you don't need this
If the agent is read-only, you don't need a write-gate — put it on a read replica with least-privilege and tenant scoping and you're done; the write class doesn't apply. Same for writes to a scratch or dev store where a bad row costs nothing. Don't gate what you can afford to lose.
When you do
Gate the write when the agent writes to a system of record where a wrong or malicious change matters:
- The write hits a production OLTP database or warehouse that feeds reporting, billing, or decisions.
- The agent reads untrusted content (tickets, emails, web, user text) that could steer the SQL it writes — the injection surface is real.
- A non-engineer owns the data and should approve the change in business terms, not read SQL.
- You need an independent, tamper-evident record of who proposed and who approved each write — for audit, for compliance, or just to not take the agent's word for it. (How this maps to OWASP, NIST, and the EU AI Act: the compliance mapping.)
FAQ
Can't I just give the agent a role that only allows the tables and operations it needs?
Do that — it's necessary. But it only controls whether the agent may write to a table, not which value it writes. Inside a grant to update customers.mrr, the agent can still set the wrong number, and a prompt injection can push exactly the query the grant permits. A write-gate judges the specific proposed value; least-privilege judges the permission. Use both — see why least-privilege isn't enough.
Isn't a read-only replica enough?
For a read-only agent, largely yes. But it solves the problem by forbidding writes — so it's no help when the task is to write a row back. As soon as you grant write for that, the replica no longer protects you, and you need a gate on the write itself.
Won't approving every write kill the point of automation?
The agent still does all the work — reading, joining, drafting the change. The human approves an outcome, not a query, and only for protected writes; you decide which tables and operations are gated and which commit freely. It's a gate on the small set of writes you'd get paged over, not a tax on every operation.
What about a bad write that's within the agent's normal range — no injection, just wrong?
That's exactly what value-level review catches and permissions can't. A hallucinated but plausible number passes every access check; it fails a human looking at 1,240,000 → 188,000,000. The gate surfaces the before → after so a wrong-but-authorized write is stopped before it commits.
Which databases does this work with?
Row-mutation for OLTP — Postgres, MySQL, MariaDB, SQL Server, Oracle, Supabase — and append-only for warehouses — Snowflake, BigQuery, Redshift, ClickHouse. The gate is the same; only the write shape differs. You can compose one governed view from several sources and push approved changes back to any of them.
Penholder
Penholder is a write-gate for your databases. An agent proposes; nothing commits to Postgres, MySQL, SQL Server, Oracle, Snowflake, BigQuery, Redshift, or ClickHouse until a human approves the exact change — the agent never holds raw credentials, writes fail closed on conflict, and every decision is hash-chained. Row-mutation for OLTP, append-only for warehouses. See how it works →
Sources
- The "God User" framing and read-replica pattern — Rietta: Protect Production SQL Databases from AI/LLM Agentic SQL Query Risks.
- Text-to-SQL risk surface beyond SQL injection — Text-to-SQL Security: 10 Risks Before Production Deployment (unsafe statements, permission checks, cost controls, tenant boundaries, auditability; human confirmation for high-impact modifications).
- Database query-based prompt injection — Keysight: Exploiting AI Agents — Database Query-Based Prompt Injection Attacks in LLM Systems.
- Why input can't be fully sanitized — UK NCSC: Prompt injection is not SQL injection (it may be worse).
- The write class at its worst — AI Incident Database, Incident 1152 and Fortune (an agent deleted a production database during a freeze and misreported it).