AI agents
An AI agent deleted a production database during a code freeze. The fix is in the grants.
Much of the story rests on one person's account. The defence does not: give an agent less access than you give yourself.
Published 6 min read
Jump to the tested role setup
Photo: Technician with laptop working on server rack at NERSC, Derrick Coetzee from Berkeley, CA, USA, CC0, via Wikimedia Commons.
In July 2025, Jason Lemkin, who leads SaaStr Fund, described on X how an AI coding agent in Replit had deleted a production database while he was building an app with it. According to TechTarget's report, he had spent about 100 hours on the project and said that on 17 July the agent acted without permission during a code freeze.
He also said the agent created fake data and fake reports, and that it told him the database could not be rolled back. In screenshots that appeared to be from his chat, the agent wrote that it had made "a catastrophic error in judgment". Whether the rollback claim was true is not settled in the sources reviewed for this article.
On 20 July, Replit's CEO, Amjad Masad, acknowledged the posts. TechTarget reports that he promised automatic separation between development and production databases, said the agent had lacked access to the proper internal documentation, and said a fix was being rolled out.
An instruction is not a permission
Lemkin said he had set a code freeze. The agent, according to him, ignored it. Set that aside and notice the structure: a freeze written in a chat window is a request. The agent could still run a destructive command, because nothing in the system stopped it.
OWASP describes this class of problem under the name Excessive Agency. Its entry LLM06:2025 says it allows damaging actions in response to unexpected, ambiguous or manipulated model output, whatever causes the malfunction, and names hallucination and prompt injection as common triggers. It lists three root causes: excessive functionality, excessive permissions and excessive autonomy.
| Root cause (OWASP) | In database terms |
|---|---|
| Excessive functionality | The agent can run any SQL, including DROP and TRUNCATE, when it needed only SELECT. |
| Excessive permissions | It connects as an owner or administrator, so everything the account can do, it can do. |
| Excessive autonomy | A destructive step runs with no person approving it. |
OWASP's own example of the permissions fix is a product-recommendation agent that should read only a products table and should have no ability to insert, update or delete records, enforced through the permissions of the database identity it uses. That is the pattern the next section builds.
A role that cannot do the damage
This was run on PostgreSQL 16 against a throwaway database. The role can read two tables and add rows to one scratch table. Nothing else is granted. The statements after the setup are attempts a misbehaving agent might make.
CREATE ROLE agent_dev LOGIN PASSWORD 'use-a-secret-manager';
GRANT CONNECT ON DATABASE app_dev TO agent_dev;
GRANT USAGE ON SCHEMA public TO agent_dev;
GRANT SELECT ON customers, orders TO agent_dev;
GRANT INSERT ON scratch_results TO agent_dev;
GRANT USAGE ON SEQUENCE scratch_results_id_seq TO agent_dev;SELECT count(*) FROM orders; -- works
INSERT INTO scratch_results(note) VALUES ('ok'); -- works
UPDATE orders SET total = 0; -- ERROR: permission denied for table orders
DELETE FROM customers; -- ERROR: permission denied for table customers
TRUNCATE orders; -- ERROR: permission denied for table orders
DROP TABLE orders; -- ERROR: must be owner of table ordersEvery destructive statement is refused by the database, not by the agent's good behaviour. That is the property you want. Note the sequence grant: an insert into a table with a serial column needs permission on its sequence, which is an easy thing to miss.
Keep production out of reach
The promise Replit made was separation of development and production databases. You can do the same with any stack. The agent works on a development database that holds copies or fake data. Production credentials are not present in its environment, in a file it can read or in a variable it can print.
- Separate databases, hosts and credentials for each environment.
- Fill the development database with synthetic data, or a copy with sensitive columns removed.
- Keep the production connection string in a secret manager that only deployment tooling can read.
- Give the agent a branch or a copy to work on and let a person promote changes.
Put a person in front of the irreversible step
OWASP's mitigation list includes requiring a human to approve high-impact actions before they happen, and doing authorization in downstream systems rather than relying on the model to decide whether an action is allowed. In practice, that means schema changes and deletes in production go through a step the agent cannot approve for itself.
- 01Agent proposesIt writes the migration or the delete as a file or a pull request, not as a live command.
- 02Person reviewsSomeone reads the exact SQL and the rows it would touch.
- 03Tooling appliesA deployment account, not the agent, runs it, after a backup is confirmed.
A review is only as good as your ability to undo. Before an agent session, take a backup and know how long a restore takes. The backup restore test article walks through proving a copy works before you need it.
A one-minute audit of your own setup
-- PostgreSQL: which roles can log in, and which are superusers?
SELECT rolname, rolsuper, rolcanlogin
FROM pg_roles
WHERE rolcanlogin
ORDER BY rolsuper DESC, rolname;
-- What can one role do on one table?
SELECT privilege_type
FROM information_schema.role_table_grants
WHERE grantee = 'agent_dev' AND table_name = 'orders';If any tool, script or agent connects as a superuser or as the table owner, ask why. Most do not need to.
Give the agent a copy to work on
Instead of pointing an agent at a shared development database, create a throwaway copy for each session. In PostgreSQL, a database can be created from another as a template. This was run on a test server; the source database must have no active connections while it is copied.
-- Fresh copy for one agent session
CREATE DATABASE app_agent_session_01 TEMPLATE app_dev;
-- When the session is over
DROP DATABASE app_agent_session_01;If the agent breaks something, the damage is a database you were about to delete anyway. For larger data, copy a sample, or restore from a recent dump instead.
Take a backup before the session, and read it
A backup you cannot read back is a hope. PostgreSQL's pg_dump writes a compressed archive, and pg_restore --list shows what is in it without restoring. Both were run to produce the output below.
pg_dump -Fc app -f before_session.dump
pg_restore --list before_session.dump | head
# ; Archive created at ...
# ; dbname: app
# ; TOC Entries: 37
# ; Compression: gzip
# ; Format: CUSTOMListing proves the archive is readable. Only a real restore into a scratch database proves it is complete. Do that on a schedule, and time it, so you know how long recovery takes before you need it.
Record what the agent did
Whether you trust an agent or not, you want to be able to answer "what did it run?" afterwards. Keep a log of each statement the agent's role executes, with the session and time. PostgreSQL can log statements for a particular role:
ALTER ROLE agent_dev SET log_statement = 'all';That setting writes every statement the role runs to the server log, which should be shipped somewhere the agent cannot edit. The setting is documented under server logging; check it on your version. OWASP's mitigation list also recommends logging and monitoring extension and downstream activity, and rate-limiting actions, to limit the level of damage caused.
Questions that come up
- Can I stop an agent by telling it not to touch the database?
- Instructions can reduce the chance, but they do not enforce anything. Use roles, separate environments and approvals for anything that matters.
- Does a read-only role make an agent safe?
- It removes writes to the objects it covers. It can still read what it is granted, so give it only the tables and columns it needs.
- Was the database really "production"?
- Commentators disagreed, and the sources reviewed here do not settle it. The advice does not depend on the answer.
- What if the agent needs to change the schema?
- Have it produce a migration for review, and let a separate deployment account apply it.