Set database guardrails for Claude Code and Cursor
Stop AI coding agents from deleting your data: give Claude Code and Cursor a read-only Postgres login and approval rules in 20 minutes.

What you need
Step 1: Create a read-only database role
5 minA login that can only read can't delete rows or drop tables, whatever the agent is told to do.
- Connect to your staging database with
psqlas an admin user. - Run the SQL below. Put a long random password in place of [PASSWORD] and your database name in place of [YOUR DATABASE].
- Once the test in step 6 passes, run the same SQL on production.
Run thisCREATE ROLE agent_ro WITH LOGIN PASSWORD '[PASSWORD]'; GRANT CONNECT ON DATABASE [YOUR DATABASE] TO agent_ro; GRANT USAGE ON SCHEMA public TO agent_ro; GRANT SELECT ON ALL TABLES IN SCHEMA public TO agent_ro; ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT ON TABLES TO agent_ro;
You should see
CREATE ROLE,GRANTandALTER DEFAULT PRIVILEGESprinted back, with no errors.Your tables live in a schema other than public?
Run the last three lines again for each schema, with its name in place of
public.The last line covers new tables made by the role that ran it. Tables another role creates later need their own
GRANT SELECT.- Connect to your staging database with
Step 2: Give the agent only that login
3 minAn agent can use any password it can find, so make the read-only login the only one in reach.
- Move the admin connection string out of the project folder, for example into your password manager.
- In the terminal you start the agent from, set
DATABASE_URLto theagent_rologin, as below. - Check it with
psql "$DATABASE_URL" -c "select current_user;". - Start Claude Code from this terminal, or point Cursor's project settings at the same login.
Run thisexport DATABASE_URL="postgresql://agent_ro:[PASSWORD]@[HOST]:5432/[YOUR DATABASE]"
You should see
agent_roprinted as the current user.The check prints your admin user instead?
Something else sets
DATABASE_URLafter this line. Search the project's.envfiles and your shell profile, and remove the admin URL.Keep this password out of committed files and chats. Anyone with the string can read every table.
Step 3: Add deny and ask rules in Claude Code
3 minRules stop Claude Code from opening secret files and make it ask before any database command runs.
- In your project, open or create
.claude/settings.json. - Paste the rules below and save the file.
- In Claude Code, run
/permissionsto check that the rules loaded.
Your config{ "permissions": { "deny": [ "Read(./.env)", "Read(./.env.*)", "Bash(dropdb *)" ], "ask": [ "Bash(psql *)" ] } }You should see your deny and ask rules in the
/permissionslist, marked as coming from.claude/settings.json.Claude Code shows a Settings Error when it starts?
Settings files are strict JSON. Remove any trailing comma or
//comment, save, and start Claude Code again.A Bash rule matches the command as Claude writes it, so a form like
/usr/bin/psqlcan slip past. That's why the read-only role is the real lock.- In your project, open or create
Step 4: Set the same limits in Cursor
3 minCursor hides
.envfiles from Agent by default, but its terminal commands can still open them.- In Settings > Agents > Approvals & Execution, pick Auto-review or Allowlist. Never pick Run Everything for a project with a database.
- In your project, create
.cursor/permissions.jsonwith the text below. - In Allowlist mode, leave
psqloff the allowlist so it always asks.
Your config{ "autoRun": { "allow_instructions": [], "block_instructions": [ "Every psql command and every command that connects to a database should go through approval first.", "Every command that drops, truncates, resets or migrates a database should go through approval first." ] } }You should see an approval card in Cursor before any
psqlcommand runs.Cursor ran a database command without asking?
Check that the mode isn't Run Everything and that
psqlisn't on your allowlist. Auto-review is a classifier, not a lock, so keep the read-only role in place.Step 5: Plan before the agent acts
2 minA plan lists every database step, so you can spot a reset or a delete before it runs.
- In Claude Code, press
Shift+Tabuntil the status bar shows plan mode, or start your prompt with/plan. - In Cursor, press
Shift+Tabin the chat input until Plan Mode is picked. - Paste the prompt below, then read the plan for migrations, deletes, resets and seed scripts.
Your prompt/plan Add [YOUR FEATURE]. List every command you would run against the database, and mark each one as read or write. Don't run any of them yet.
You should see a written plan with each database command listed and marked read or write.
Claude Code ran a command while it was planning?
Plan mode blocks file edits, but read-only commands still run, and auto mode may approve others. Your ask rule on
psqlstill makes it stop and ask.- In Claude Code, press
Step 6: Test the guardrails on staging
4 minA test you run on purpose costs nothing. A gap you find in production can cost your data.
- As your admin user, create a throwaway table on staging:
CREATE TABLE guardrail_test (id int); - Ask the agent to drop it, with the prompt below.
- Approve the
psqlrequest, so the command reaches the database.
Your promptConnect with the DATABASE_URL in this shell and run: DROP TABLE guardrail_test;
You should see PostgreSQL refusing with
must be owner of table guardrail_test, and the table still there.The table was dropped?
The agent used a login with more rights. Ask it to run
select current_user;, then find and remove the admin URL it could reach.Drop the test table yourself afterwards, as the admin user.
- As your admin user, create a throwaway table on staging:
You get
A database your coding agents can read but can't change, with secret files hidden and every database command behind an approval
Try next
Did this guide work for you?
Thanks. It helps us fix the steps.


