Securing Autonomous AI: Data Governance for Agents

An autonomous agent with a live database connection and no write boundary isn’t an efficiency gain — it’s a production incident that hasn’t happened yet. The fix isn’t “trust the agent less.” It’s the same fix a DBA has applied to every new hire for thirty years: read access everywhere on day one, write access nowhere until it’s earned. Multi-agent systems didn’t invent this problem. They just made it move fast enough that skipping the control finally gets noticed.
Why “just don’t let it write” isn’t a real answer
The instinct when someone raises this risk is to say the agent shouldn’t have write access at all. That instinct is right for exactly as long as the agent is a demo. The moment it’s expected to actually do something — update a record, close a ticket, apply a migration — someone grants it a connection string with full DML rights because scoping permissions felt like a later problem. It always feels like a later problem, right up until it isn’t.
This is not hypothetical. Replit’s agent deleted a production database during a code freeze in 2025 — one of the reference incidents the OWASP Gen AI Security Project cites in its Top 10 for Agentic Applications, which formally names “Identity & Privilege Abuse” as the agentic evolution of excessive agency: a contained LLM mistake that an agent with broad tool access turns into a chain of high-impact, hard-to-reverse actions OWASP Top 10 for Agentic Applications, via NeuralTrust. The application-layer fix — better prompting, a system message that says “be careful with destructive operations” — doesn’t hold, because a prompt is a suggestion and a database permission is a fact. An agent that can run DROP TABLE will eventually run DROP TABLE, whether or not you asked it nicely not to.
The other common fix is worse: revoke write access entirely and route every change through a human ticket queue. That doesn’t scale. Manual review was already the bottleneck before agents existed; agents just increased the volume of proposed changes past what any queue can absorb. Neither extreme — unrestricted access or no access — is a governance model. They’re both an admission that nobody designed one.
The database-first fix: least agency, enforced at the connection
The actual fix lives at the same layer the problem does: the database’s permission system, not the application code calling it. This is what current guidance in the field is converging on. Security teams building agent control planes in 2026 are moving toward “default-deny tool access, grant only what each task needs, issue short-lived scoped credentials, and require human approval on any irreversible action” — with the oversight level set per action, by risk, not once for the whole agent Preloop, AI Agent Control Plane 2026. Read the language carefully: per action, by risk. That’s a database access-control statement wearing an AI-governance vocabulary. A SELECT against a reporting schema and an UPDATE against a billing table are not the same risk, and they should not require the same authority to execute.
Concretely, this means the agent’s database role is deliberately asymmetric:
- Read tier — broad, autonomous, no approval required. The agent’s role gets
SELECTacross the schemas it needs to reason over. This is where most of an agent’s value comes from — querying state, checking constraints, readinginformation_schemabefore it writes anything — and it should be fast and unblocked. - Write tier — narrow, gated, human-approved by default. Any
INSERT,UPDATE,DELETE, or DDL statement routes through an approval checkpoint before it executes against the real connection. Not before it’s proposed — before it’s executed. The distinction matters: a code review that happens after the agent already ran the statement isn’t a guardrail, it’s a post-mortem.
The mechanism enforcing this should be the database’s own role and grant system — a role like agent_readonly_role with SELECT grants and nothing else, and a second role, agent_writer_role, whose grants are only usable through a proxy or stored-procedure interface that pauses for approval. Enforcing this in the database itself matters because it survives a bug in the application layer. If the approval logic lives only in your orchestration code and that code has a defect, the safety net has a hole in it exactly where you need it not to. If it lives in the grant, the database itself refuses the statement regardless of what the orchestration layer intended.
Evidence: what the gate actually looks like
Here’s the pattern in practice, tying back to the Agent State Ledger this series introduced in its first post — the same table that tracks durable session state is the natural place to log pending write requests awaiting approval.
-- Two roles, asymmetric privilege by design
CREATE ROLE agent_readonly_role;
GRANT SELECT ON ALL TABLES IN SCHEMA app TO agent_readonly_role;
CREATE ROLE agent_writer_role;
-- No direct table grants. Writes only flow through the gated procedure below.
-- Pending writes are logged, not executed, until a human approves them
CREATE TABLE agent_write_requests (
request_id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
agent_id TEXT NOT NULL,
session_id UUID REFERENCES agent_state_ledger(session_id),
statement_type TEXT NOT NULL CHECK (statement_type IN ('INSERT','UPDATE','DELETE','DDL')),
target_table TEXT NOT NULL,
proposed_sql TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending'
CHECK (status IN ('pending','approved','rejected','executed')),
requested_at TIMESTAMPTZ NOT NULL DEFAULT now(),
reviewed_by TEXT,
reviewed_at TIMESTAMPTZ
);
-- The agent can only ever call this — never run raw DML directly
CREATE OR REPLACE FUNCTION request_agent_write(
p_agent_id TEXT, p_session_id UUID, p_type TEXT, p_table TEXT, p_sql TEXT
) RETURNS UUID AS $$
INSERT INTO agent_write_requests (agent_id, session_id, statement_type, target_table, proposed_sql)
VALUES (p_agent_id, p_session_id, p_type, p_table, p_sql)
RETURNING request_id;
$$ LANGUAGE sql;
A human — or a policy engine acting on a human-defined risk threshold — reviews rows in agent_write_requests with status = 'pending', and only an approval step flips the row to 'approved' and triggers execution against a connection that actually holds write privilege. The agent never holds that connection itself. It can propose. It cannot apply.
The analogy: this is RBAC for a junior engineer’s first week
If this feels familiar, it should. This is exactly how you’d onboard a junior database engineer on day one: read access to everything so they can learn the schema and build context, write access to nothing until they’ve demonstrated they understand the blast radius of a bad statement. Nobody hands a new hire production DELETE rights on their first Monday, and nobody considers that an insult to their competence — it’s just how access earns trust over time. An autonomous agent deserves exactly the same treatment, and for the same reason: competence at generating a plausible SQL statement is not the same thing as trustworthiness to execute it unsupervised.
This is also the point where “Identity & Privilege Abuse” stops being an abstract OWASP category and becomes a concrete database design decision. The category exists because most agent frameworks were never built by people who’d designed a grant hierarchy under real load. You already have. This is the second post in this series to say some version of that sentence, and it holds here too: the skill that keeps agents safe already exists in your job title.
This closes the loop on what this series has been building toward. Post one established Durable State — an agent’s memory has to survive a restart, or it isn’t production-grade. Post two established Verified Execution — an agent’s SQL has to be checked against a real schema and self-corrected on failure, not trusted on the first guess. This post is the third pillar: Bounded Autonomy — an agent’s authority to act has to be scoped and gated, proportional to the risk of what it’s about to do. Durable State, Verified Execution, Bounded Autonomy: that’s Database-First Agent Architecture, and none of the three works as a substitute for the other two. A ledger with no write gate still lets a corrupted agent overwrite good state. A self-correcting query loop with no privilege boundary just means the agent corrects its way into a more confident mistake before executing it.
This also isn’t new ground for this site — it’s the same territory covered in AI Agent Guardrails for Databases, which goes deeper on where in the delivery pipeline these approval gates belong. Read that one for the pipeline mechanics; read this one for why the database’s own grant system, not just your CI pipeline, has to be the backstop.
Key takeaways
- Never grant an agent a single database role with both read and write privilege — split it into a broad read-only role and a narrow, gated write role from the start
- Route every INSERT, UPDATE, DELETE, and DDL statement through an approval step that executes after human sign-off, not one that reviews a change log after the fact
- Enforce the boundary in the database’s grant system, not only in application or orchestration code — a bug in your agent framework should not be able to bypass a hard privilege boundary
- Treat agent database access exactly like RBAC for a new hire: full read access to build context, zero write access until trust is demonstrated and scoped
- Bounded Autonomy is the third and final pillar of Database-First Agent Architecture — durable state and verified execution both assume the agent’s authority to act was scoped correctly in the first place
If your agents can execute a write against production without a human or a policy gate in the path, that isn’t autonomy — it’s an unmonitored credential with a language model attached. Get in touch if your database’s access model needs to catch up to what your agents are already capable of doing.
Ivan Lima is a data engineer specializing in database modernization for AI systems. Get in touch if your database needs to be ready for what’s next.