Database backups an AI coding agent cannot delete
A coding agent in your terminal runs commands as you. It can read your shell's environment,
your config files and the keys beside them, and it can run psql,
terraform and aws with whatever those hold. When it drops the wrong
table, the backup is what brings the table back. So the backup has to be out of reach of
everything the agent can read, and it has to exist from just before the command ran.
This guide sets that up in four steps, then lists what it does not cover.
1. Find what the agent can reach
List the credentials on the machine the agent runs on, and ask of each one whether it can delete or overwrite a backup:
| Credential | Where it usually is | Can it delete a backup? |
|---|---|---|
| Cloud keys | ~/.aws/credentials, AWS_* in the shell | If its policy grants s3:DeleteObject or s3:DeleteObjectVersion on the backup bucket, yes, unless the bucket locks the objects (step 2). |
| The backup tool's own key | Its config file | Most tools use a key that can delete, so they can expire old backups. |
| The database URL | .env, DATABASE_URL | It can destroy the database. It cannot touch a copy stored elsewhere. |
| An API token for the backup service | The agent's MCP config | Depends on what the service lets a token do (step 3). |
2. Lock the backups in the bucket
S3 Object Lock in compliance mode stops anyone from deleting or overwriting
a locked object before its date, the bucket's owner and the account's root user included.
Governance mode does not: a key with s3:BypassGovernanceRetention deletes
through it. A plain DELETE on a locked bucket only writes a delete marker,
which hides the backup from a listing and leaves the locked version in place.
Immutable backups with S3 Object Lock
shows each case against a real bucket.
Then give the machine a key that cannot delete at all, so it cannot even write the marker.
The policy SafeGrd gives a backup host grants writing, locking, reading and listing, and no
s3:DeleteObject (the policy). On SafeGrd's
hosted storage the host holds no storage key: it asks SafeGrd's server for a URL signed for
one part of one upload, and a URL cannot delete. Either way, the lock's date can only move
later: safegrd keep extends a lock and nothing shortens one.
Cloudflare R2 and DigitalOcean Spaces have no S3 Object Lock, so backups there can be deleted by a key that can delete. Use another bucket for anything an agent can reach (details).
3. Give the agent a token that cannot delete
An agent that talks to SafeGrd uses a personal access token through the MCP server. Its eleven tools list organizations, projects, surfaces, snapshots and drills, read storage use and drill figures, and ask for a backup or a drill. None deletes a snapshot, changes where backups are stored, rotates a key, or changes members or billing, and every tool is marked non-destructive in its MCP annotations. The server refuses those changes to a token on every API route as well, so the agent cannot make them through REST either: they take a signed-in console session, which an agent does not have.
4. Take a snapshot before each destructive command
A nightly backup can be most of a day old when the agent runs DROP TABLE.
The agent guard closes that gap: a hook in Claude Code, Cursor or
Codex sends every shell command through safegrd guard, which lets a command
through at once unless it matches the destructive list (DROP,
TRUNCATE, DELETE without WHERE, dropdb,
terraform destroy, prisma migrate reset, supabase db
reset and the Rails and Django resets). For a match, it backs up the database, waits
until the snapshot is uploaded and locked, and only then runs the command. If the backup
fails or the snapshot is not locked, the command does not run.
The restore brings the one table back as the snapshot defined it, and leaves the database's other tables as they are. The hook for each agent is a few lines of JSON in the project.
What this does not cover
- The live database. Nothing here stops the agent from dropping a table. It makes sure a locked copy from just before exists, and that you can restore from it.
- An unlocked bucket. On a bucket without Object Lock, or under governance mode with a key that can bypass it, a key that can delete can delete the backups.
- A key that can delete in the agent's reach. Keep the account's admin
keys, and any key with delete permission on the backup bucket, off the machine the agent
runs on. Turning on expiry for your own bucket gives the host's key
s3:DeleteObjectVersion, which deletes backups whose lock has ended and none that is still locked (details). - A console session. Sign in to the console in your browser, not in a browser the agent drives.
Install SafeGrd on the machine, then add the hook from Agent guard.