DBX as an agent database layer: we measured the query path and the write gates
Published
Against a throwaway Postgres 17.11: 25–50 ms per command. Writes are blocked by default, DDL (schema changes) needs two flags, a connection flagged production refuses both, and you have to fill the connection store by hand.
Our first DBX article stopped at the footprint because the query path could not be tested: no connections add command, an empty connection store, every query refused with CONNECTION_NOT_FOUND. This time we pointed the CLI at a throwaway Postgres 17.11 on port 5433 and ran the whole path.
1.Result
| Reachable directly from the CLI | Needs DBX Desktop as a bridge |
|---|---|
| postgres | redis |
| sqlite | mongodb |
| mysql | duckdb |
| redshift | clickhouse |
| questdb | snowflake |
| doris, starrocks, manticoresearch, rqlite, kwdb | 58 more, including oracle, sqlserver and elasticsearch |
| 10 engines total | 62 engines total |
Engines the CLI reaches by itself
10 engines total
Postgres, SQLite, MySQL, Redshift, QuestDB and five more, with no desktop app running
Engines that need DBX Desktop
62 engines total
Redis, MongoDB, DuckDB, ClickHouse, Snowflake, Oracle and SQL Server sit behind the bridge
Overhead per CLI command
25 ms
The cheapest of six measured commands: an aggregate over 500 local rows, returned as JSON
| Command | Wall clock | Result |
|---|---|---|
| dbx connections list | 50 ms | the test connection, without secrets |
| dbx schema list | 50 ms | one table, orders (BASE TABLE) |
| dbx schema describe orders | 50 ms | columns, types, nullability, keys |
| dbx query "select 1" | 30 ms | one row, as JSON |
| dbx query count(*)+sum over 500 rows | 25 ms | the aggregate, as JSON |
| dbx context | 48 ms | full schema with primary keys, defaults and comments |
| dbx dbml | — | a DBML file written to disk |
| Attempt | Result |
|---|---|
| delete from orders | blocked, SQL_BLOCKED: write statement is blocked |
| insert into orders ... | blocked, SQL_BLOCKED: write statement is blocked |
| drop table orders | blocked, SQL_BLOCKED: DDL statement is blocked |
| drop table orders --allow-dangerous-sql | rejected, INVALID_OPTION: --allow-dangerous-sql requires --allow-writes |
| insert into orders ... --allow-writes | executed, affected rows reported |
| Attempt | Result |
|---|---|
| delete from orders --allow-writes | blocked, SQL_BLOCKED: writes and DDL are blocked for production databases |
| insert into orders ... --allow-writes | blocked, the same code, orders still at 500 rows |
| drop table orders --allow-writes --allow-dangerous-sql | blocked, the flag pair that unlocks DDL elsewhere changes nothing here |
Decision
- Use it when
- your engines are Postgres or SQLite and you want a read-only-by-default query path: 25–50 ms overhead per command.
- Read-only by default: a write needs --allow-writes
- 25 to 50 ms per command, process start and connection setup included
- Skip it when
- your stack is Redis, MongoDB, DuckDB or ClickHouse: those engines need the desktop app as a bridge.
- 62 of the 72 engines reachable through DBX sit behind that bridge
- No bridged engine was exercised here: the desktop app was never launched
- Write gate
- Real and layered. A write needs --allow-writes, DDL needs two flags, and a connection flagged production refuses both even when you pass them.
- delete and insert without the flag: SQL_BLOCKED
- drop table --allow-dangerous-sql without --allow-writes: INVALID_OPTION
- Rough edge
- You cannot create a connection from the CLI: we wrote a row into its SQLite store by hand to run this test at all.
- No connections add command exists, and the store ships empty
- The CLI silently skips a stored row whose JSON it cannot parse
Details worth knowing
- Two commands are built for agents, not for clicking, and both run on the same connection, with no second integration. The first, dbx context, returns the schema as structured JSON: type, nullability, key, default, comment, precision. The second, dbx dbml, writes a diffable DBML file.
- These are overhead numbers, not performance numbers: a 25 ms aggregate over 500 local rows says nothing about a 200 GB warehouse.
- The CLI resolves connections by name, not id, and silently skips a stored connection whose JSON it cannot parse.
Not verified
- Desktop app never launched, bridge engines never exercised. The production refusal is now measured. A second row in the same connection store, flagged production, refused delete, insert and drop, while the identical delete ran on the unflagged row of the same database.
- One small table on the same machine as the database: query performance, large schemas and network latency stay open.
No X/10 scores in this column, by rule. Raw rows: docs/analysis/2026-10-05-dbx-cli-query-path.jsonl, one JSON object per command, including the blocked write attempts.