Skip to content

Server Setup

The dbward server manages approval state, audit logs, and coordinates agents. It does not connect to your database.

Full configuration reference with all options: Configuration

# Required: directory for server state (SQLite DB, signing keys, agent-token)
state_dir = "/data"
trusted_proxies = ["10.0.0.0/8"] # Trust X-Forwarded-For from these CIDRs

Note: The server has a 64MB request body size limit.

[auth]
# break-glass: users with request.break_glass_* permissions (operator or admin role)

For OIDC setup, see Authentication.

[retention]
request_ttl_days = 90 # Auto-delete completed requests (default: 90)
audit_ttl_days = 365 # Audit log retention (default: 365)
result_ttl_days = 30 # Stored results retention (default: 30)
approval_ttl_secs = 86400 # Approval expiry — re-approval needed after (default: 24h)
[audit]
redaction = "literals" # "literals" (mask SQL values) | "none" (default: literals)
# IP recorded automatically via trusted_proxies # Record client IP in audit events (default: true)
# Local (default) — omit root_dir to use {state_dir}/results
[result_storage]
backend = "local"
root_dir = "/data/results"
# S3
[result_storage]
backend = "s3"
bucket = "my-dbward-results"
region = "us-east-1"
# endpoint = "http://minio:9000" # For S3-compatible storage
FieldDescriptionDefault
bucketS3 bucket namedbward
regionAWS regionus-east-1
endpointCustom endpoint (MinIO, LocalStack)
access_key_idAWS access key (falls back to env/instance role)
secret_access_keyAWS secret key
path_styleUse path-style URLs (required for MinIO)false
prefixKey prefix for all objects (e.g. prod/)

Configure an S3 Lifecycle Rule as a safety net for expired results. dbward tags each stored result with dbward-expires containing the RFC 3339 expiry timestamp. The background job deletes expired results, but a lifecycle rule provides defense-in-depth:

{
"Rules": [
{
"ID": "dbward-result-expiry",
"Filter": { "Prefix": "results/" },
"Status": "Enabled",
"Expiration": { "Days": 91 }
}
]
}

Set Expiration.Days to retention_days + 1 (default retention is 30 days for Free, configurable via result policies for Team).

[[webhooks]]
url = "https://hooks.slack.com/services/T.../B.../xxx"
events = ["request.created", "request.approved", "request.rejected", "execution.completed"]
format = "slack"
# secret = "whsec_..." # HMAC-SHA256 signature (optional)

See Workflows guide for full configuration.

# Production: require admin approval
[[workflows]]
database = "*"
environment = "production"
require_reason = true
[[workflows.steps]]
type = "approval"
[[workflows.steps.approvers]]
role = "admin"
min = 1
# Development: auto-approve (mode = "always")
[[workflows]]
database = "*"
environment = "development"

Auto-approve is configured within each workflow:

# Development: auto-approve everything
[[workflows]]
database = "*"
environment = "development"
[workflows.auto_approve]
mode = "always"
# Staging: auto-approve low-risk only
[[workflows]]
database = "*"
environment = "staging"
[workflows.auto_approve]
mode = "risk_based"
risk = "low"
allow_read_only = true
allow_safe_ddl = true
[[workflows.steps]]
type = "approval"
[[workflows.steps.approvers]]
role = "dba"
min = 1
# Production: no auto-approve (always require human)
[[workflows]]
database = "*"
environment = "production"
[[workflows.steps]]
type = "approval"
[[workflows.steps.approvers]]
role = "dba"
min = 1
[[execution_policies]]
database = "*"
environment = "production"
max_executions = 1 # One-shot execution (default: 1)
execution_window_secs = 3600 # Must execute within 1 hour of approval (default: 86400)
retry_on_failure = false # Allow re-execution on failure only (default: false)

Result policies are managed via the REST API, not TOML. See Result Policies.

Notification policies are managed via the REST API, not TOML. See Notification Policies.

After writing your configuration, validate it before starting the server:

Terminal window
dbward-server validate --config /path/to/server.toml
dbward-server validate --config /path/to/server.toml --preflight # also check Slack/OIDC connectivity

This checks workflow validity, role resolution, and webhook references — catching misconfigurations before they cause runtime failures. Use --preflight to additionally verify Slack API connectivity and OIDC issuer reachability.

/etc/systemd/system/dbward-server.service
[Unit]
Description=dbward server
After=network.target
[Service]
Type=simple
User=dbward
ExecStart=/usr/local/bin/dbward-server start \
--config /etc/dbward/dbward-server.toml \
--listen 0.0.0.0:3000
Restart=always
RestartSec=5
[Install]
WantedBy=multi-user.target
Terminal window
docker run -d \
--name dbward-server \
-p 3000:3000 \
-v dbward-data:/data \
-v ./dbward-server.toml:/etc/dbward/dbward-server.toml:ro \
ghcr.io/dbward-dev/dbward-server:latest \
start \
--config /etc/dbward/dbward-server.toml \
--listen 0.0.0.0:3000

Create tokens for users and agents:

Terminal window
# Initial tokens created automatically on first server start.
# Read them from files:
cat /data/admin-token # admin token (can approve, manage users)
cat /data/requester-token # requester token (can submit queries)
cat /data/agent-token # agent token
# Additional tokens via CLI (requires admin token):
dbward token create --subject alice --scope-roles admin
dbward token create --subject bob --scope-roles requester
dbward token create --subject prod-agent --subject-type agent --no-scope-ceiling

For API-based token management, see REST API Reference.

Two endpoints are available without authentication:

EndpointPurposeUse for
GET /healthProcess liveness — always returns 200 if the server is runningContainer liveness probes, external uptime monitors
GET /readyService readiness — returns 503 when degraded or drainingLoad balancer target health, rollout gating
Terminal window
curl http://localhost:3000/health
# → {"status":"ok","version":"0.1.5","min_agent_version":"0.1.5"}
curl http://localhost:3000/ready
# → {"status":"ok","checks":{"sqlite":"ok","result_store":"ok"}}
# → 503 {"status":"degraded",...} when SQLite or result store is unavailable

Choosing between them: Use /health for restart decisions and “is it up at all?” checks. Use /ready for load balancer health and traffic routing — it intentionally returns 503 during graceful shutdown and maintenance drains.

For external uptime monitoring (e.g., Route53 Health Check, UptimeRobot), point at /health. This avoids false alerts during planned rolling deploys where /ready temporarily returns 503.

All responses include an X-Dbward-Version header.

The server tracks agent liveness via poll heartbeats. Query the fleet status with:

Terminal window
curl http://localhost:3000/api/agents -H "Authorization: Bearer $TOKEN"
# Requires metrics.view permission

Each agent has a status field:

StatusMeaning
healthyPolling and has spare capacity
saturatedPolling but at max concurrency (in_flight >= max_concurrent)
offlineNo poll received for 60+ seconds
drainingGraceful shutdown in progress

Note: An agent in degraded mode (e.g., lost DB connection) still polls with limit=0 and appears healthy here. Check application logs or the agent’s readiness probe for degraded state.

This is the best available fleet-level view. Local probe files (/tmp/dbward-agent-alive, /tmp/dbward-agent-ready) are for the container runtime only.

Terminal window
# Requires metrics.view permission
curl http://localhost:3000/metrics -H "Authorization: Bearer $TOKEN"
# → Prometheus text format

If you use Prometheus/Grafana, scrape /metrics for request queue depth (dbward_requests_current) and general activity. Note that /metrics does not reflect agent offline state — the dbward_agents_active gauge counts configured agents regardless of last_seen. For offline detection, poll GET /api/agents which applies the 60-second heartbeat check.

The server stores all state in a single SQLite file. Back it up with:

Terminal window
# Simple copy (while server is running — SQLite WAL mode is safe)
cp /data/dbward.db /backup/dbward-$(date +%Y%m%d).db
# Or use Litestream for continuous replication to S3
# See: https://litestream.io

All TOML string values support ${ENV_VAR} expansion:

[[webhooks]]
url = "${SLACK_WEBHOOK_URL}"
secret = "${WEBHOOK_SECRET}"