Open-core project— core components are[Apache-2.0]. Some features and pre-built binaries include code under the[dbward Commercial License]. See[License]for details.
Approval workflows and audit logs for your production database.
Stop accidents before they hit production. Add approval gates, audit trails, and AI agent guardrails to every database operation — with standalone binaries and embedded SQLite. No external control-plane DB required.
- 🔐
Approval workflows— multi-step, conditional auto-approve, TOML policy engine - 📋
Audit logs— tamper-evident hash chain, 24 event types, SQL redaction - 🤖
MCP-native— 12 tools, 6 prompts, elicitation support. AI agents operate safely. Remote HTTP transport for team setups - ⚡
Standalone binaries— CLI, server, and agent ship as self-contained Rust binaries with embedded SQLite. No external control-plane DB - 🔒
Agent isolation— DB credentials never leave the agent. CLI/AI never touch your database directly - 🛡️
SQL safety review— risk classification, DDL detection,
DROP
blocking. Auto-approve safe queries, require approval for risky ones - 🔍
Preflight— analyze SQL before submitting. Get risk level, EXPLAIN plan, review findings, and fix hints without creating a request. AI agents converge on safe SQL before asking for approval - 🧠
Auto schema context— the agent collects table structures, columns, FKs, and row counts automatically. AI tools access schema via MCP resources — no manual documentation needed - 💬
Slack approvals— approve/reject from Slack with one click.dbward slack init
generates the app manifest - 🚨 Break-glass— emergency bypass with mandatory reason and audit. Operator/admin only, not available via MCP - 🆓 Core features free— approval, audit, MCP, Slack, break-glass all included underApache-2.0. Team features (OIDC, group auth) require acommercial license
┌─────────────────────────────────────────────────────────┐
│ dbward client (CLI / MCP) │
│ No DB credentials — sends requests, receives results │
└──────────┬───────────────────────────────────────────────┘
│ REST API
▼
┌─────────────────────────────────────────────────────────┐
│ dbward server │
│ Approval engine │ Policy engine │ Audit log (hash chain) │
│ Ed25519 token signing │ OIDC/API auth │ Webhooks │
│ In-memory result relay │ NO database credentials │
└─────────────────────────────────────────────────────────┘
▲ Agent polls (outbound HTTPS)
│
┌──────────┴───────────────────────────────────────────────┐
│ dbward agent │
│ DB credentials here only │ Executes approved operations │
│ Token verification (Ed25519) │ Multiple DB support │
└──────────┬───────────────────────────────────────────────┘
│
▼
Target Database (PostgreSQL / MySQL)
Key principle: The client requests. The server decides. The agent executes. No component has more access than it needs.
Try the approval flow in 2 minutes (Docker):
git clone https://github.com/dbward-dev/dbward.git && cd dbward/examples/quickstart
docker compose up -d
docker compose run --rm alice execute "SELECT version()" -e development
Then submit → approve → execute → audit. Full walkthrough: Quickstart with Docker
Quick smoke test (local install):
curl -fsSL https://dbward.dev/install.sh | sh
dbward dev --database-url "postgres://user:pass@localhost:5432/mydb"
dbward --config ~/.dbward/dev/client.toml --database app execute "SELECT 1"
Dev mode auto-approves everything for fast iteration. See Connect Your Database for details.
Full reference:
[docs/reference/mcp.md]
{
"mcpServers": {
"dbward": {
"command": "dbward",
"args": ["mcp"]
}
}
}
MCP Tools (12):
| Tool | Description |
|---|---|
dbward_execute_query |
|
| Execute SQL (SELECT/DML) via approval workflow | |
dbward_migrate_status |
|
| Show migration status | |
dbward_migrate_up |
|
| Apply pending migrations | |
dbward_migrate_down |
|
| Rollback migrations | |
dbward_migrate_create |
|
| Create migration file (local) | |
dbward_wait_request |
|
| Wait for request completion and return result | |
dbward_list_pending |
|
| List pending approval requests | |
dbward_who_can_approve |
|
| Show who can approve a request | |
dbward_find_similar_requests |
|
| Find similar past requests | |
dbward_preflight_sql |
|
| Analyze SQL safety without creating a request | |
dbward_explain_policy_failure |
|
| Explain why approval is needed | |
dbward_inspect_schema |
|
| Inspect database schema (list tables or describe columns) |
MCP Prompts (6): review_migration
, explain_request
, draft_migration
, draft_rollback
, summarize_audit_trail
, prepare_approval_comment
Elicitation: On production operations, dbward asks the AI client for a reason before proceeding (if the client supports MCP elicitation).
Remote MCP (HTTP): For team setups, the server exposes MCP over HTTP — no local binary needed (9 tools, excludes local-only migration tools):
{
"mcpServers": {
"dbward": {
"type": "streamable-http",
"url": "https://your-server.example.com/mcp"
}
}
}
dbward uses on-demand execution: the agent does not execute on approval. Instead, the client explicitly resumes the request when ready to receive the result.
1. Client creates request → server evaluates policy → pending / auto_approved
2. (If pending) Human approves via CLI
3. Client resumes (`dbward request resume <id>`) → server marks as "dispatched"
4. Agent polls, claims, executes on DB → returns result to server
5. Server relays result in-memory to waiting client (long poll)
6. Client displays result (server persists to local FS or S3)
Results are persisted on the server by default (local filesystem or S3, configurable via [result_storage]
). The in-memory relay has a 10-minute TTL for streaming delivery. Use --no-result-store
to skip persistence for a single request.
Defined in server.toml
and hot-reloaded via SIGHUP. See Configuration Reference.
Control whether operations require approval:
[[workflows]]
database = "*"
environment = "production"
operations = ["execute_select", "migrate_up", "migrate_down"]
[[workflows.steps]]
type = "approval"
[[workflows.steps.approvers]]
role = "admin"
min = 1
[[workflows]]
database = "*"
environment = "staging"
[workflows.auto_approve]
mode = "risk_based"
risk = "low"
[[workflows.steps]]
type = "approval"
[[workflows.steps.approvers]]
role = "admin"
min = 1
Control re-execution limits (rate limiting):
[[execution_policies]]
database = "primary"
environment = "production"
max_executions = 10
execution_window_secs = 3600
retry_on_failure = false
Control who can access results and storage:
[[result_policies]]
database = "primary"
environment = "production"
delivery_mode = "stream"
access = ["requester", "admin"]
Route webhooks per database × environment:
[[notification_policies]]
database = "primary"
environment = "production"
[[notification_policies.webhooks]]
url = "https://hooks.slack.com/services/..."
format = "slack"
Full reference:
[docs/reference/cli.md]
dbward [OPTIONS] <COMMAND>
Commands:
init Interactive setup wizard
doctor Diagnose connectivity and configuration
login OIDC login (browser or --device for headless)
logout Revoke tokens and delete credentials
whoami Show current identity and role
migrate Run migrations (up/down/status/create)
execute Execute SQL (--emergency --reason for break-glass)
audit Search audit log (--verify for hash chain check)
mcp Start MCP stdio server
server Server management (start, token create/revoke, reload)
agent Start the agent
dev Start local dev server + agent
self-update Update dbward to the latest version
request Manage requests:
list List requests (--pending-for-me, --status)
show Show request detail
approve Approve a pending request
reject Reject a pending request
resume Resume and wait for result
cancel Cancel a pending request
token Manage API tokens (create/list/revoke)
user Manage users (list/suspend/activate)
slack Slack integration:
init Generate app manifest and creation URL
policy Policy tools:
resolve Resolve effective policy for a request
Global Options:
--version, -v Show version and exit
--config <PATH> Config file (standalone mode; omit for auto-detect)
--database <NAME> Target database [env: DBWARD_DATABASE]
--environment <ENV> Environment [env: DBWARD_ENV]
Full reference:
[docs/reference/api.md]
| Method | Path | Description |
|---|---|---|
| POST | /api/requests |
|
| Create request | ||
| POST | /api/requests/:id/approve |
|
| Approve | ||
| POST | /api/requests/:id/resume |
|
| Resume for on-demand execution | ||
| GET | /api/requests/:id/result/stream |
|
| Long-poll for result | ||
| GET | /api/audit/events |
|
| Audit events | ||
| GET | /api/audit/verify |
|
| Verify hash chain integrity | ||
| POST | /api/tokens |
|
| Create API token | ||
| GET | /api/databases |
|
| List configured databases | ||
| GET | /api/agents |
|
| List connected agents | ||
| POST | /mcp |
|
| Remote MCP (HTTP) |
See full API reference for all endpoints, parameters, permissions, and response formats.
Threat model and hardening guide:
[docs/security/]
Zero-trust client— developer machines never have DB credentials** Signed execution tokens**— Ed25519. Token includes SHA-256 hash of SQL + target database** Token replay prevention**— executed/failed requests don't issue new tokens** Multi-statement rejection**— prevents SQL injection via statement chaining** Writable CTE detection**—WITH x AS (DELETE ...) SELECT ...
classified as DMLRBAC— admin (system management), requester (SQL operations), operator (monitoring + break-glass), approver (review)** Network isolation**— server has no DB credentials; agent connects outbound only** API token auth**— SHA-256 hashed, prefix+hash composite lookup** OIDC auth**— JWT verification with JWKS caching, RS256/ES256, PKCE for CLI (Team)** Audit hash chain**— SHA-256 chain linking all events, tamper-evident
| Target | Status |
|---|---|
| Linux x86_64 (glibc) | ✅ Supported |
| Linux aarch64 (glibc) | ✅ Supported |
| macOS Apple Silicon | ✅ Supported |
| macOS Intel | ✅ Supported |
| Windows | ❌ Not supported |
Pre-built binaries are available on GitHub Releases. Docker images are published for linux/amd64
and linux/arm64
.
Note:Pre-built binaries and Docker images include commercial-licensed components. They are free to use within Free plan limits. See[LICENSE]for details.
| Database | Status |
|---|---|
| PostgreSQL | ✅ Supported |
| MySQL | ✅ Supported |
Auto-detected from URL scheme (postgres://
or mysql://
).
Full guide:
[docs/guides/authentication.md]
cat ./data/admin-token # admin token
cat ./data/agent-token # agent token
dbward token create --subject alice --role admin
dbward login # Browser-based (PKCE)
dbward login --device # Headless (SSH, containers)
dbward whoami # Check identity
dbward logout # Revoke + delete tokens
Approve and reject requests directly from Slack with interactive buttons:
dbward slack init --server-url https://your-server.example.com
Configure in server.toml
:
[slack]
bot_token = "${SLACK_BOT_TOKEN}"
signing_secret = "${SLACK_SIGNING_SECRET}"
channel = "C0123ABC456"
See Notifications Guide for setup details.
[[webhooks]]
url = "https://internal.example.com/dbward"
format = "generic"
secret = "whsec_xxxx" # HMAC-SHA256 in X-Dbward-Signature header
Events: request.created
, request.approved
, request.rejected
, execution.completed
, request.break_glass
.
Free: unlimited webhook destinations.
dbward execute "SELECT pg_terminate_backend(12345)" \
--emergency --reason "connection pool exhausted at 3am"
- Skips approval — agent executes immediately when dispatched
- Fires
request.break_glass
webhook (🚨 in Slack) - Reason recorded in audit log
Operator or admin role(requiresrequest.break_glass_query
for SELECT,request.break_glass_dml
for writes)Not available via MCP(AI agents cannot trigger break-glass)
Full reference:
[docs/reference/configuration.md]
Config is resolved in two layers:
Global(~/.config/dbward/config.toml
): server URL, token/OIDCProject(./dbward.toml
): databases, migrations
[server]
url = "http://localhost:3000"
token = "dbw_..."
default_database = "app"
migrations_dir = "db/migrations"
[databases.app]
agent_id = "agent-prod"
poll_interval_ms = 1000
max_concurrent_tasks = 2
[server]
url = "https://dbward.internal:3000"
agent_token = "${DBWARD_AGENT_TOKEN}"
[databases.primary.production]
url = "${DATABASE_URL_PRIMARY}"
[databases.analytics.production]
url = "${DATABASE_URL_ANALYTICS}"
state_dir = "/data"
[auth]
[[webhooks]]
url = "https://hooks.slack.com/services/..."
format = "slack"
[[workflows]]
database = "*"
environment = "production"
operations = ["execute_select", "migrate_up", "migrate_down"]
[[workflows.steps]]
type = "approval"
[[workflows.steps.approvers]]
role = "admin"
min = 1
[[execution_policies]]
database = "*"
environment = "production"
max_executions = 10
execution_window_secs = 3600
[logging]
output = "stderr" # "stderr" (default) or "file"
| Free | Team ($149/mo) | |
|---|---|---|
| Database connections | 3 | 20 |
| Active users | 20 | 50 |
| Workflow rules | Unlimited | Unlimited |
| Webhooks | Unlimited | Unlimited |
| Agents | Unlimited | Unlimited |
| Approval + Audit + MCP + Break-glass | ✅ | ✅ |
| Slack approval UI | ✅ | ✅ |
| Result policies | ✅ | ✅ |
| Notification policies | ✅ | ✅ |
| OIDC / SSO | — | ✅ |
| Group-based authorization | — | ✅ |
| Audit export (CSV/JSON) | — | ✅ |
Safety features are always free. You pay for scale and organizational complexity.
Team plan is not yet available.[Join the waitlist]to get notified.
Migrations use single-file dbmate-compatible format:
migrations/
├── 20260501120000_create_users.sql
└── 20260502090000_add_email.sql
-- migrate:up
CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT NOT NULL);
-- migrate:down
DROP TABLE users;
dbward uses an open-core licensing model.
Core(crates/
):Apache-2.0— approval workflows, audit logs, MCP, SQL review, agent execution, break-glass. Use, modify, and redistribute freely.Commercial(commercial/
):dbward Commercial License— OIDC/SSO, group authorization, Team/Enterprise plan enforcement. Requires a paid subscription for production use.
No license key = Free plan. All core features work without restriction.
See LICENSE for the full structure.