A practical checklist before you let an AI agent near production

Twenty checks, grouped by what goes wrong. Most need no new tools, just SQL and a few decisions written down. Start with the first section; it finds most of the problems.

14 October 2026 · 6 min read

This is the list we work through before an AI agent is allowed near a production database, a payment API or customer data. It’s written for a small team: one or two engineers, an agent that already works in staging, and a launch date. Each item is a yes or no. If you can’t answer one, that’s the item to work on.

The public incidents in our incident catalogue share a pattern: the agent could reach far more than its task needed, and the only thing between it and a destructive action was an instruction in a chat. Most of this list is about closing that gap with things that don’t depend on the model behaving.

1. Know what it can reach

1. You have a written list of every credential the agent can read. Database URLs, API tokens, cloud keys, including the ones in .env files, MCP client configs and old scripts in the repo. An agent that can read the repository can read whatever is in it.

2. For each database credential, you know the role and what it can change. Not “it’s the app user”; the actual tables and verbs. The quickest way is to ask the database. Here is our scan against a demo store database, connected as the bootstrap superuser, which is what many agents get by default:

$ deedglass scan --postgres-dsn-env ACME_OWNER_URL
Blast radius: F · 6 write paths with no check · fix this first: The database role bypasses row-level security
  A means a small blast radius, not a safe agent.
   −35  superuser or BYPASSRLS  postgres · Role postgres is superuser · BYPASSRLS
    −8  medium finding          The database role can create roles
    −8  medium finding          The database role can replicate
    −8  medium finding          The role can run SECURITY DEFINER functions
    −4  unchecked write path    public.customers · INSERT, UPDATE, DELETE, TRUNCATE · not shown to be routed
    −4  unchecked write path    public.orders · INSERT, UPDATE, DELETE, TRUNCATE · not shown to be routed
    …
    21 points

And the same database, connected as a role made for one support job (section 3 shows how):

$ deedglass scan --postgres-dsn-env AGENT_URL
Blast radius: A · 0 write paths with no check
  A means a small blast radius, not a safe agent.
   100 points

Postgres · acme · role agent_support · wal_level logical · read-only session · 5 tables read-only

  Table           Can change   Est. rows   Cascades to   Triggers   Undo-ready   Proven-ready
  public.orders   UPDATE             500   none          none       ✓            ✓

The score starts at 100 and subtracts for a superuser or BYPASSRLS role, high and medium findings, and each write path not routed through a policy; A is 90 or more, F is under 50.1 The scan runs read-only and nothing leaves your machine.1 You can get the same facts by hand from pg_roles and information_schema.role_table_grants; the point is to look. Note the scan’s own caveat on the second line: a small blast radius is not the same as a safe agent.

3. You know which of those tables have triggers, cascades or functions that write elsewhere. A narrow UPDATE on a table with a trigger can change far more than one row. We wrote up a real run where 1 reported row was 301 committed rows.

2. Give it its own credentials

4. The agent has its own database role, not the app’s and not the owner’s. Separate roles mean separate grants, separate limits and a separate thing to switch off.

5. That role is not a superuser and doesn’t have BYPASSRLS, CREATEROLE or REPLICATION. On managed Postgres, check what your provider’s default user really is. On Amazon RDS for PostgreSQL, the first (master) user is a member of rds_superuser.7 On Neon, roles created in the Console, API or CLI are members of neon_superuser, which has CREATEROLE and CREATEDB.8 Create a plain role for the agent instead.

6. Every API token the agent can see is scoped to its job. One environment, one project, the fewest operations. A token made “just to manage domains” with account-wide rights is a production credential.

7. No credential reaches the agent’s context at all, if you can help it. Put tools between the agent and your systems that hold the secret and expose only operations. The agent asks; the tool decides and holds the key.

3. Let the database say no

8. Reads use a role that can only SELECT. A role that holds no write privilege can’t write, whatever the agent sends.3

9. Writes use a role with column grants for one job. Postgres can grant SELECT, INSERT, UPDATE and REFERENCES on single columns; TRUNCATE is a table-level privilege only.5 “Update status on orders” is a grant. “Work with orders” isn’t.

CREATE ROLE agent_support LOGIN PASSWORD '…' CONNECTION LIMIT 3;
ALTER ROLE agent_support SET statement_timeout = '2s';
GRANT USAGE ON SCHEMA public TO agent_support;
GRANT SELECT (id, customer_id, status, total_cents) ON public.orders TO agent_support;
GRANT UPDATE (status) ON public.orders TO agent_support;

10. Row-level security limits which rows it can touch, and the agent never connects as the table owner. Once row security is on for a table, access with no matching policy is denied, and table owners normally bypass row security unless FORCE ROW LEVEL SECURITY is set.4

11. There’s a statement timeout. statement_timeout aborts any statement that runs longer than its limit; the default, 0, disables it.2 Set it on the role.

12. You don’t rely on a tool’s “read-only mode” alone. default_transaction_read_only only sets the default for each new transaction.2 Datadog Security Labs showed that the archived reference Postgres MCP server’s read-only mode ran each query in a read-only transaction that a stacked COMMIT; ends, after which later statements run with the role’s full rights.6 The role’s privileges are the boundary; the mode is a second fence.

Our guide to Postgres write access for Claude Code and Cursor walks through items 8 to 12 with a tested SQL file.

4. Decide destructive verbs before they run

13. DROP, TRUNCATE, and UPDATE or DELETE without a WHERE never run from the agent. Not “the agent is told not to”; something outside the agent refuses them. In Deedglass that’s a rule, and a refused call looks like this:

✕ denied · postgres.execute
  Blocked by policy "block-destructive-sql": DROP is never allowed for agents.

Without Deedglass, the role’s grants do most of this: a role without TRUNCATE and without ownership can’t truncate or drop.

14. Writes outside the agent’s normal job wait for a person. Pick the few that matter (refunds over a threshold, deletes, anything touching customer contact data) and require an approval, ideally on a phone, with the exact arguments shown.

15. A “freeze” is a permission change, not a sentence in the prompt. If nobody should change production this week, revoke the role’s write grants this week.

5. Keep production apart

16. Staging credentials can’t reach production resources, and production credentials don’t live anywhere the agent works in staging. Separate identifiers, separate secrets, ideally separate accounts or projects.

6. Make sure you can get back

17. Backups live somewhere the agent’s credentials can’t delete, and you’ve restored one in the last month. Know your point-in-time window in hours, not “we have backups”.

18. You know how you’d undo one bad action without restoring the whole database. A restore throws away everyone else’s writes since the backup. For a single agent action, you want the rows it changed, before and after, which is what Postgres’s change log carries for the tables you watch.

7. Keep evidence, not just logs

19. For every write, you record the request, the rule or person that allowed it, and what the database actually committed. The agent’s own trace records what it asked and what the tool replied, which isn’t the same thing. Why logs aren’t evidence goes into what to keep and how to make it hard to edit.

8. Be able to stop it

20. You can cut the agent off in under a minute, and you’ve tried it. For Postgres that’s two statements, run by an admin:

ALTER ROLE agent_support NOLOGIN;
SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE usename = 'agent_support';

Do the same for API tokens: know where the revoke button is, and who can press it at 2 a.m.

Where to start

If you only do three things this week: give the agent its own non-superuser role (items 4 and 5), set a statement timeout on it (11), and make destructive verbs impossible for that role (13). Then run the free scan, npx deedglass scan, and see what’s left; the grade is explained here. To watch rules, a check and an undo work on a real Postgres without installing anything, try the playground.

Sources

  1. 1Deedglass docs: Scan in 60 seconds · docs.deedglass.com · checked 14 October 2026
  2. 2PostgreSQL 18 documentation: Client connection defaults · postgresql.org · checked 13 October 2026
  3. 3PostgreSQL 18 documentation: Privileges · postgresql.org · checked 13 October 2026
  4. 4PostgreSQL 18 documentation: Row security policies · postgresql.org · checked 13 October 2026
  5. 5PostgreSQL 18 documentation: GRANT · postgresql.org · checked 13 October 2026
  6. 6Datadog Security Labs: SQL injection in the Postgres MCP server · securitylabs.datadoghq.com · checked 13 October 2026
  7. 7Amazon RDS for PostgreSQL documentation: Understanding PostgreSQL roles and permissions · docs.aws.amazon.com · checked 1 October 2026
  8. 8Neon documentation: Manage roles · neon.com · checked 1 October 2026

Published 14 October 2026, last checked 14 October 2026. If something here is out of date, write to hello@deedglass.com.

Try it on your own setup

npx deedglass scan

Grades only your own setup. Nothing is uploaded. The package opens with the public release.

Pricing