← Back to blog
Use CasesSQL10 min read

September 19, 2026

Add approval-gated SQL writes without exposing DATABASE_URL

Read-only gates protect production and create ticket traffic for routine INSERT / bounded UPDATE work. Deny-all-writes and allow-all-writes are both wrong. Prefer: reads execute, writes park for approval, schema changes deny.

Configure approval-gated writes

Keep sql.read allowed, park sql.write, deny sql.ddl. Expose limetry_sql_query and limetry_sql_list_pending. The agent still never sees DATABASE_URL.

import { SqlActionGate } from "@limetry/sql"

const gate = new SqlActionGate({
  connectionString: process.env.DATABASE_URL!,
  apiKey: process.env.LIMETRY_API_KEY!,
  baseUrl: process.env.LIMETRY_BASE_URL,
  policyId: process.env.LIMETRY_SQL_POLICY_ID!,
  agentId: "sql_mcp",
  resourceLabel: "postgres://analytics",
  dryRun: false,
})

const result = await gate.evaluateAndMaybeExecute({
  sql: "INSERT INTO experiment_results (experiment_id, variant, conversions) VALUES ('exp_42', 'B', 17)",
  execute: true,
})

if (result.evaluation.decision === "approval_required") {
  process.stdout.write(JSON.stringify(gate.listPending(), null, 2))
}
{
  "allowed_action_types": ["sql.read", "sql.write"],
  "denied_action_types": ["sql.ddl"],
  "require_approval_action_types": ["sql.write"],
  "audit_mode": "minimal"
}

A production DELETE still classifies as sql.write and parks — it does not auto-execute. DROP TABLE is sql.ddl and still deny. Approval-gated writes do not create approval-gated schema changes. Operators approve via the approval API.

Review pending INSERT before retry

First INSERT returns approval_required:

{
  "sql_class": "write",
  "decision": "approval_required",
  "approval_id": "apr_01j7m2k8",
  "executed": false,
  "reasons": [
    "action_type sql.write requires human approval"
  ]
}

Open limetry_sql_list_pending, review the truncated preview, approve, and retry. On retry, Limetry returns allow, the gate executes, and the decision receipt ties execution to the approved intent. Reject broad UPDATE / DELETE shapes; require a narrowed WHERE and a new park.

Measure the first enforced week

Example targets:

  • 243 read queries allowed and executed.
  • 47 write queries returned approval_required.
  • 44 writes approved and retried successfully.
  • 3 writes rejected for broad predicates.
  • 0 DDL statements executed.

Ticket traffic moves from "run this SQL" to "approve this evaluated intent." Audit shows every write with the approver's identity. The agent gains write access without seeing DATABASE_URL.

Incident that made read-only the default: Deny sql.write before the database sees a mass DELETE. Origin: Gate SQL writes so agents never hold DATABASE_URL.

Run Limetry on your own stack

Self-host the open source evaluation server, wire evaluate into your agents, and keep privacy-safe audit under your control.