Back to Explore
GuideVibeFix 编辑部Updated Oct 24, 2026

Don't Hand the Agent the Keys to the Database: Data-Access Guardrails for Vibe Projects

Before connecting a coding agent to your database, build the guardrails: a read-only account, a human-approved write path, least privilege with deny rules, consent gates for irreversible actions, full audit logging, and preview mode — all in one afternoon.

Real photograph of a technician working on a laptop in front of server racks at NERSC, symbolizing human oversight of data access.

Connecting a coding agent to your database is the easiest way to feel productive in vibe development — and the easiest way to regret it later. Once a project is live, you will want the agent to check a user record, fix a dirty row, or run a migration. It asks, you paste the production connection string into the chat, and just like that you have handed it the keys to the whole warehouse: read, write, delete, change the schema, all on its say-so. Worse, an agent does not hesitate the way a person does. The faster it executes, the more completely it can get things wrong.

This guide skips the theory and gives you a set of guardrails one solo developer can build in an afternoon: a read-only account, a human-approved write path, least-privilege roles, consent gates before irreversible actions, an audit trail for every data action, and a preview mode for rehearsals. After this, the agent can still help you, but it can no longer touch what it should not touch — and nothing gets deleted without going through you first.

Boundary first: what this guide does not cover

To avoid overlapping guides already published on this site, let us draw the boundary clearly. This guide is only about an agent's privilege, approval, and audit when it accesses project data: how much data permission the agent gets, how write operations pass through a human, how gates are set before irreversible actions, and how every action is logged. It does not cover general secret storage and rotation — that belongs to the published secrets-management guide. It does not cover privacy law and compliance duties — that belongs to the published privacy-compliance guide. It does not cover third-party API timeouts, retries, and circuit breakers — that belongs to the published API-resilience guide. And it does not cover data isolation between tenants — that belongs to the published multitenancy guide. Nor does it cover permission models between people on a team, which is the RBAC guide's territory. The only protagonist here is the agent: a data accessor that is extremely fast and never doubts itself.

First principle: never give the agent the master account

Many projects have a single superuser account for the database: it can create tables, drop the database, and change permissions, and its connection string sits in an environment variable. During development it feels convenient to let the agent use that same account. That is the most dangerous default. The right shape is layered: one account for you, a much smaller one for the agent, and a third path for production writes. Once the layers exist, even if the agent is misled, manipulated, or simply misunderstands your intent, the damage it can cause is capped by its permission ceiling.

Before building anything, do a ten-minute permission audit. List every database account and its permissions, and ask three questions: can this account drop tables? Can it change the schema? Can it see every sensitive user field? If any answer is yes and the agent can use that account, the guardrails are not built yet. The audit itself is the foundation for everything that follows.

Step 1: give the agent a read-only account

Ninety percent of what an agent needs from data is reading: why is this record abnormal, how much is a feature used, what does the data look like before a migration? A read-only account covers all of that and cannot cause a disaster. Read-only must be real at the database layer, not a polite request in a prompt.

In PostgreSQL, create a dedicated role for the agent with SELECT only — no INSERT, UPDATE, DELETE, and certainly no DDL:

-- 1. Create a dedicated role for the agent
CREATE ROLE agent_reader LOGIN PASSWORD 'a strong password from your password manager';
-- 2. Connection and read access only, no write access
GRANT CONNECT ON DATABASE app_db TO agent_reader;
GRANT USAGE ON SCHEMA public TO agent_reader;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO agent_reader;
-- 3. Future tables are read-only by default, so nothing slips through later
ALTER DEFAULT PRIVILEGES IN SCHEMA public
  GRANT SELECT ON TABLES TO agent_reader;
-- 4. Explicitly revoke dangerous privileges — saying it twice is not too much
REVOKE INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public FROM agent_reader;

Three details are worth getting right. First, cap the read-only account's connection pool — say, five connections at most — so a bad query from the agent cannot exhaust the pool and take down real users. Second, handle sensitive columns separately: if the users table holds phone numbers, emails, or ID numbers, do not rely on the agent choosing not to look. Give it a view that hides those columns, or a redacted view exposing only the columns it actually needs. Third, keep the agent's connection string physically separate from yours, with a distinct name such as AGENT_DATABASE_URL versus DATABASE_URL. Sharing one variable name is an accident waiting to happen.

If you use MySQL, the idea is identical, only the syntax changes: GRANT SELECT ON app_db.* TO 'agent_reader'@'%'; and remember FLUSH PRIVILEGES. Changing databases does not change the principle: what the agent can read is decided by the database, not by the prompt. Prompts can be bypassed, misunderstood, or injected. Database permissions cannot.

Step 2: writes take a different path, and a human must approve them

Sooner or later the agent will say: I found the problem, let me fix it for you. Do not temporarily open write access at that moment. The correct posture is that writes are never executed directly. They first become a structured proposal that you review, edit, and approve — only then does anything land in the database.

For a solo project, the simplest approval flow needs no new system: one proposals table plus a script you run by hand. The agent's output is not an execution result but a proposal: which table, how many rows, old values, new values, why the change is needed, and how to roll it back. The proposal goes into the table with status pending, and only you — not the agent — execute it.

-- Pending change proposals (the agent may INSERT, but not change status)
CREATE TABLE agent_change_proposals (
  id          BIGSERIAL PRIMARY KEY,
  proposed_by TEXT NOT NULL DEFAULT 'agent',
  title       TEXT NOT NULL,
  sql_text    TEXT NOT NULL,
  affected_rows INT,
  reason      TEXT,
  rollback_sql TEXT,
  status      TEXT NOT NULL DEFAULT 'pending',
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- The agent can only submit proposals, never approve or delete them
GRANT INSERT, SELECT ON agent_change_proposals TO agent_reader;
-- Approval and execution belong to your personal account only, never to the agent

When you approve, check four things by hand. First, run the SELECT version of the SQL in a read-only environment and confirm the affected row count matches what the agent claimed. Second, check the WHERE clause: any UPDATE or DELETE without a WHERE clause is sent straight back. Third, confirm the rollback plan is real and usable, not a vague promise to deal with it later. Fourth, ask whether a smaller change would do: change one row instead of a thousand, roll out to a subset of users before going to everyone. Once approved, you execute it yourself, or a script that requires your manual confirmation does — the agent prepares, it never pulls the trigger.

This extra step looks like friction, but it buys something real: a chance to stop a disaster before it happens. And the proposals table is itself a natural audit record — later, when you wonder who proposed a change and why, the answer is in the table.

Step 3: least-privilege roles and explicit deny policies

A read-only database account covers the database layer, but cloud resources need a second layer. Much of a vibe project's data also lives in object storage, queues, and third-party dashboards. If the agent holds your cloud credentials, it can delete buckets, send paid messages, or call paid APIs. So apply least privilege in the cloud too: the agent's cloud identity gets only the permissions its current task needs, and high-risk actions get an explicit deny on top.

Take a cloud identity policy as an example. Start with a narrow role that may only read a specific bucket and a specific queue, then stack a deny rule: no deleting buckets, no deleting database instances, no changing billing or identity policies, no exporting data to external addresses. When an allow list and a deny list coexist, deny wins — and that is the safety net. Even if someone later mistakenly grants the agent a broad permission, the deny rule still blocks the dangerous action.

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": [
        "s3:DeleteBucket",
        "rds:DeleteDBInstance",
        "iam:*",
        "billing:*"
      ],
      "Resource": "*"
    }
  ]
}

This is only a skeleton — rewrite it in your cloud provider's actual syntax, and test it in a staging environment first: confirm normal reads still work and dangerous actions are truly denied. One more hard rule: the agent's cloud credentials are separate from yours, rotated regularly, and revoked immediately when a project ends or you switch agents. Separate storage, separate rotation, separate revocation — with those three in place, you have something to grab when you need to stop the bleeding.

Step 4: consent gates before irreversible actions

Some actions cannot be undone once executed, and they deserve their own gate. This guide treats five categories as irreversible or high-risk, blocked by default so the agent can only propose, never act directly: deleting data, migrating schema, bulk updates, bulk deletes, and external sends (email, SMS, external API calls, publishing data). The test is simple: can it be fully restored with one click afterwards? If not, it goes through the gate.

Gate design must be concrete, not a vague please confirm. Every high-risk proposal must carry five items: scope (how many rows, which users), the backup snapshot ID taken before execution, rollback SQL or rollback steps, an execution window (preferably off-peak), and a record of your explicit approval. Missing any one of them means the proposal is incomplete and goes back. For migrations in particular, the flow is: run the migration on a staging database first, then run the rollback, and only when both pass does it qualify for production approval. A migration never tested on staging never enters production.

Bulk operations get one more hard guardrail: a row cap. Any bulk update defaults to at most 100 rows; anything larger must be split into batches and approved batch by batch. Agents routinely underestimate the blast radius of a bulk operation — a casual let's clean up all the old data can mean hundreds of thousands of rows. A row cap forces the agent to count first, show a sample, and validate on a small batch instead of firing at full scale on the first try.

Step 5: preview mode first, real execution second

Beyond the approval flow, there is a second habit that costs almost nothing: dry-run mode. Before any write truly executes, run a preview: for UPDATE and DELETE, run the matching SELECT and show the affected row count plus the first 10 sample rows; for a migration, show the before-and-after schema diff; for an external send, show the recipient list and a content sample without actually sending. The preview goes into the proposal record, next to the SQL, for you to compare during approval.

Preview mode does more than prevent mistakes — it forces the agent to state its intent clearly. An agent that can only say I want to change the data is a very different level of trustworthy from one that can say I want to change these 37 rows, here is the sample, here is the rollback. For a solo developer, this needs no framework: agree that the agent shows a preview first and you confirm before execution, and you already have a working dry-run mode. Formalize it into a script or a CI check later, when the project grows.

Step 6: audit logging — record every agent data action

With guardrails in place, one piece remains: the trail. Guardrails without logs are barely guardrails at all — after an incident you could not say who did what, when, or how big the impact was. An audit record has a fixed set of fields: time, actor (agent or human), account, action type, target table and rows, SQL or action summary, affected row count, approval record ID, and result. With all of those, post-incident review has material to work with.

The audit table itself must be tamper-resistant. The single most important rule: the agent has append-only access to it — no update, no delete. If the agent can erase its own logs, the logs are meaningless. Design it like this:

-- Agent data-action audit log: append-only, never editable or deletable by the agent
CREATE TABLE agent_audit_log (
  id          BIGSERIAL PRIMARY KEY,
  actor       TEXT NOT NULL,
  account     TEXT NOT NULL,
  action      TEXT NOT NULL,
  target_table TEXT,
  row_count   INT,
  sql_summary TEXT,
  proposal_id BIGINT,
  result      TEXT,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);
GRANT INSERT, SELECT ON agent_audit_log TO agent_reader;
-- Note: never grant the agent UPDATE or DELETE on this table
-- Back up the audit table regularly to a location the agent cannot reach

Recording logs is not enough; someone has to read them. Spend ten minutes a week scanning the audit log, focusing on three kinds of records: rejected high-risk actions, bulk operations, and write attempts outside the approval flow. Rejected records are especially worth reading — they usually reveal where the agent's understanding diverges most from yours, and they are the best clues for improving your prompts and guardrails next. Logs nobody reads are just text taking up disk space.

An afternoon's build order

If you have only one afternoon, work in this order, with a clear done criterion for each step. Hour one: permission audit plus the read-only account. Done means the agent can read through its read-only connection string while write attempts are rejected by the database itself. Hour two: the proposals table plus the approval flow. Done means you can receive a proposal complete with a rollback plan, and only you can execute it. Hour three: cloud least privilege plus deny rules. Done means dangerous cloud actions are explicitly denied while normal reads are unaffected. Hour four: the high-risk action list, row caps, preview convention, audit table, and a weekly review reminder. Done means when you deliberately ask the agent to propose dropping a table, it gets blocked, logged, and waits for your approval.

Then run a destructive drill: ask the agent to drop a test table, bulk-update a thousand rows, and export data externally. The expected result is that all three are blocked and all three appear in the audit log. The guardrails only count as standing once the drill passes; any hole the drill finds is a hole to patch before going live.

Common pitfalls

First, read-only in the prompt is not read-only. A prompt constraint only works when the agent understands it correctly; a database permission works all the time. The two are not substitutes. Second, staging and production sharing one credential set. If the agent practices in staging with production credentials, practice can become a production incident — credentials for the two environments must be physically separate. Third, backups that exist only in your head. Without a recoverable snapshot before a migration, approval is empty talk — the snapshot ID belongs in the proposal, not in a search afterwards. Fourth, an approval flow so heavy that people route around it. Once the flow is painful enough to invite a back door, the guardrail is hollow; for a solo project, keep it light: one table, one review, one manual execution. Fifth, permissions granted and never reclaimed. When a project phase changes, an agent is replaced, or a task ends, reclaim the permission promptly — a long-lived broad permission is the most common slow leak.

Checklist: paste it into your project README

  • The agent uses its own read-only account with no write and no DDL permission at the database layer.
  • Agent and human connection strings and cloud credentials are stored, rotated, and revoked separately.
  • Writes are submitted only as proposals, with SQL, affected row count, rollback plan, and reason.
  • Approval verifies the affected row count in a read-only environment first; writes without a WHERE clause are always sent back.
  • Delete, migrate, bulk update, bulk delete, and external send are blocked by default and require human approval.
  • Bulk operations default to a 100-row cap; anything larger is split into batches and approved batch by batch.
  • Migrations must pass forward and rollback runs on staging before they qualify for production approval.
  • Every execution has a backup snapshot ID recorded in its proposal.
  • Every write is previewed first: affected row count and sample rows before, real execution after.
  • The audit log records every agent data action, and the agent may only append — never edit or delete.
  • The audit log is reviewed weekly, focusing on rejected high-risk actions and unusual bulk operations.
  • Sensitive columns are hidden behind views or redaction, never exposed directly to the agent account.
  • Connection-pool and row caps are set, so a bad query cannot take production down.
  • The destructive drill passes: drop, bulk change, and external export are all blocked and logged.

Revocation: take the key back when the job ends

Guardrails are not set-and-forget; permissions need a lifecycle too. When a one-off debugging task ends, disable or downgrade the agent account right away. When you switch agent tools, revoke the old connection strings and cloud credentials and reissue fresh ones. When a project graduates from personal experiment to public service, re-review the agent's default permissions instead of carrying over the loose experimental settings. Give every agent credential a purpose and an expiry date, with an automatic reminder to review it — one extra reminder beats a forgotten credential staying valid indefinitely. Record the revocation itself in the audit log as well: who revoked which account, and when, so that too can be traced later. Issuing permissions is easy and reclaiming them is hard; building revocation into the process is what closes the loop.

Closing: let the agent run fast, and make sure it can stop

The appeal of vibe development is speed, but speed itself is not a guardrail. What lets you trust an agent with data is not how smart it is, but how small its blast radius is when it errs, how quickly you can stop it, and how complete the evidence is afterwards. A read-only account draws the baseline, an approval flow takes back the trigger, deny rules catch what slips through, an audit log preserves the evidence, and preview mode buys you time to think. Together they cost less than an afternoon, and they buy you the ability to let an agent touch data without holding your breath. Governed-access tools on the market can complement this approach, but the method here depends on no specific product — only the permission mechanisms your database and cloud provider already give you. Build the guardrails first, then talk about how fast the agent should run.

Views 0Comments 0

Comments (0)

ME
0/1000
Loading comments...
Browse projectsPublish your project

Related articles