Policy configuration
Complete reference for the Backstop policy file — every field, its behavior, and recommended values for development and production.
The policy file is a JSON document that controls all of Backstop's enforcement behavior. You pass it to the gateway on startup. There is no hot-reload — restart the gateway to apply changes.
Policy file location
# Pass via environment variable
BACKSTOP_POLICY=/etc/backstop/policy.json
# Or as a CLI flag
backstop-gateway --policy /etc/backstop/policy.jsonFull reference
{
"require_approval_for_risks": ["HIGH", "IMPACT_CRITICAL", "CRITICAL"],
"block_operations": ["DROP DATABASE", "DROP SCHEMA"],
"require_recovery_for_critical": true,
"block_unknown_or_parse_failure": true,
"block_unrecoverable_operations": true,
"max_snapshot_age_seconds": 300,
"require_sidecar_heartbeat": true,
"max_sidecar_heartbeat_seconds": 120,
"impact_analysis_enabled": true,
"max_write_rows_without_critical": 1000,
"max_write_percent_without_critical": 50,
"protected_tables": ["users", "payments"],
"protected_columns": {
"users": ["password", "api_key", "mfa_secret"],
"payments": ["card_number", "cvv"]
},
"max_blocked_attempts_per_window": 3,
"quarantine_duration_seconds": 1800,
"dangerous_retry_window_seconds": 600
}Field reference
| Parameter | Type | Description |
|---|---|---|
require_approval_for_risksOPTIONALdefault: ["HIGH","IMPACT_CRITICAL","CRITICAL"] | string[] | Risk levels that require operator approval before execution. SAFE reads execute immediately. |
block_operationsOPTIONALdefault: ["DROP DATABASE","DROP SCHEMA"] | string[] | Specific SQL operations to block unconditionally, regardless of approval. These operations cannot be snapshotted and are too destructive to allow. |
require_recovery_for_criticalOPTIONALdefault: true | boolean | If true, CRITICAL operations require a verified snapshot before approval is accepted. Set to false only in development environments. |
block_unknown_or_parse_failureOPTIONALdefault: true | boolean | If true, SQL that cannot be parsed is treated as CRITICAL. Strongly recommended for production. |
block_unrecoverable_operationsOPTIONALdefault: true | boolean | If true, operations classified as unrecoverable (e.g., DROP DATABASE) are blocked even with explicit approval. |
max_snapshot_age_secondsOPTIONALdefault: 300 | number | Maximum age of a snapshot (in seconds) for it to be considered valid for the recovery gate. |
require_sidecar_heartbeatOPTIONALdefault: true | boolean | If true, the sidecar must have reported a heartbeat within max_sidecar_heartbeat_seconds or CRITICAL operations are blocked. |
max_sidecar_heartbeat_secondsOPTIONALdefault: 120 | number | Maximum age of the sidecar heartbeat in seconds. |
impact_analysis_enabledOPTIONALdefault: true | boolean | If true, Backstop asks PostgreSQL for an EXPLAIN plan estimate for UPDATE/DELETE operations and may escalate HIGH to IMPACT_CRITICAL. |
max_write_rows_without_criticalOPTIONALdefault: 1000 | number | Threshold for escalating to IMPACT_CRITICAL based on estimated affected row count. |
max_write_percent_without_criticalOPTIONALdefault: 50 | number | Threshold for escalating to IMPACT_CRITICAL based on estimated percentage of table affected (0–100). |
protected_tablesOPTIONALdefault: [] | string[] | Tables that receive additional scrutiny. Any write to a protected table is escalated to at least HIGH. |
protected_columnsOPTIONALdefault: {} | object | Map of table name to column names. Writes to protected columns trigger escalation and alert. |
max_blocked_attempts_per_windowOPTIONALdefault: 3 | number | Number of blocked/denied queries an agent can submit within dangerous_retry_window_seconds before being quarantined. |
quarantine_duration_secondsOPTIONALdefault: 1800 | number | How long a quarantined agent is blocked from submitting any queries. |
dangerous_retry_window_secondsOPTIONALdefault: 600 | number | The time window (in seconds) over which blocked attempt counts are tracked for quarantine purposes. |
Recommended profiles
Development (permissive)
{
"require_approval_for_risks": ["CRITICAL"],
"block_operations": ["DROP DATABASE"],
"require_recovery_for_critical": false,
"block_unknown_or_parse_failure": false,
"impact_analysis_enabled": false,
"max_blocked_attempts_per_window": 10
}Production (strict)
{
"require_approval_for_risks": ["HIGH", "IMPACT_CRITICAL", "CRITICAL"],
"block_operations": ["DROP DATABASE", "DROP SCHEMA"],
"require_recovery_for_critical": true,
"block_unknown_or_parse_failure": true,
"block_unrecoverable_operations": true,
"max_snapshot_age_seconds": 300,
"require_sidecar_heartbeat": true,
"max_sidecar_heartbeat_seconds": 120,
"impact_analysis_enabled": true,
"max_write_rows_without_critical": 1000,
"max_write_percent_without_critical": 50,
"protected_tables": ["users", "payments"],
"protected_columns": { "users": ["password", "api_key"] },
"max_blocked_attempts_per_window": 3,
"quarantine_duration_seconds": 3600,
"dangerous_retry_window_seconds": 600
}