Config
tablebelt.yaml
tablebelt init writes this file. Flag, then environment, then file, then default. Every key maps to TABLEBELT_ plus the path in upper case with underscores, for example TABLEBELT_LISTEN_ADDR and TABLEBELT_HOLD_BLOCK_TIMEOUT. Unknown keys are an error. Relative paths resolve against the config file's directory. A container can run from environment variables alone.
Search order for the file: $TABLEBELT_CONFIG, ./tablebelt.yaml, $XDG_CONFIG_HOME/tablebelt/tablebelt.yaml, /etc/tablebelt/tablebelt.yaml.
version: 1
database:
name: prod
listen:
addr: 127.0.0.1:5439
auth: scram-sha-256
users:
- name: agent
password_file: .tablebelt/agent.password
allowed_options: [search_path, statement_timeout, lock_timeout, application_name]
allow_plaintext_remote: false
tls:
mode: prefer
cert_file: .tablebelt/tls.crt
key_file: .tablebelt/tls.key
upstream:
dsn_env: TABLEBELT_UPSTREAM_DSN
admin_dsn_env: TABLEBELT_ADMIN_DSN
max_conns: 50
connect_timeout: 10s
hold:
block_timeout: 60s
pending_ttl: 24h
approval_ttl: 30m
max_pending: 100
snapshots:
schema: tablebelt
dsn_env: ""
max_bytes_per_hold: 1GiB
max_total_bytes: 5GiB
retention: 14d
rules:
builtin: default
protected_schemas: [tablebelt]
custom: []
limits:
max_statement_bytes: 1MiB
admin:
addr: 127.0.0.1:5440
token_file: .tablebelt/admin.token
metrics:
addr: ""
cloud:
enabled: false
url: https://app.tablebelt.com
api_key_file: .tablebelt/cloud.key
proxy_id_file: .tablebelt/proxy.id
redact: full
local_approvals: true
log:
level: info
format: json
log_statements: held
Listen
Default address is 127.0.0.1:5439. Binding a non-loopback address requires client TLS require or listen.allow_plaintext_remote: true. Client auth is scram-sha-256, or trust only on loopback. Passwords come from password_env or password_file. The upstream DSN is upstream.dsn_env (default TABLEBELT_UPSTREAM_DSN) or dsn_file. Init refuses to write the upstream DSN into the YAML.
listen.allowed_options defaults to search_path, statement_timeout, lock_timeout, and application_name. Other startup -c options are rejected. That blocks session_replication_role=replica.
Holds and snapshots
hold.block_timeout defaults to 60 seconds (0 to 30 minutes). After that the client gets TB001 and the hold stays pending. An identical statement after approval runs once, within approval_ttl (default 30 minutes). pending_ttl defaults to 24 hours. max_pending defaults to 100.
Snapshots live in schema tablebelt on the admin DSN, or the upstream DSN if you do not set one. Default cap is 1 GiB per hold and 5 GiB total, kept for 14 days. See Restore.
Cloud
cloud.enabled: false is the Free default and makes no outbound calls. Pro and Team set url to https://app.tablebelt.com, store the API key in a 0600 file, and choose redact. local_approvals: false makes tablebelt approve refuse, so decisions happen in the dashboard, Slack, or email. Details are on Cloud.
Logs
JSON logs by default. Statement text is logged for held statements only (log_statements: held). Passwords, DSNs, and parameter values are not logged. metrics.addr empty means no /metrics listener. Set it to a loopback address, for example 127.0.0.1:9439, when you want Prometheus text.
The proxy reads config at start. Reloading the file on SIGHUP is not in this version. Restart the process after an edit.