A page for every feature: what it is, how to use it, and how it works underneath. The specifications are at the end.
Get started
Install the app, add a connection, and find your way around the window.
Install DBClient
DBClient ships as a signed and notarized disk image, and as a Homebrew cask. Both install the same app, and both are kept current by the app's own updater.
How to
Download DBClient.dmg, open it and drag the app to Applications. There is no "unidentified developer" step: the build is notarized by Apple.
Or install it with Homebrew: brew tap az-code-lab/taps && brew install --cask dbclient. Later releases arrive with brew upgrade, or through the app.
To check what you downloaded, run spctl -a -vv /Applications/DBClient.app. The answer names the Developer ID and says accepted.
To remove it, delete the app, or run brew uninstall --zap dbclient to remove its files as well.
The app, every library inside it and the disk image are signed with a Developer ID certificate under the hardened runtime, then notarized and stapled.
Saved passwords are Keychain items under the service dev.azcode.dbclient.connections; delete them in Keychain Access if you want them gone. Everything else the app keeps is listed under Where things are stored.
Your first connection
A connection is an engine, an address and a way to sign in. You can paste all of it as one connection string, or fill in the fields.
The picker a new connection starts on: the engines first, the hosted services under them.
How to
Click Add Connection under the sidebar, or press ⌘K and choose Add Connection…
Pick the engine or the hosted service. A service tile sets the port and the SSL mode that service expects.
Paste a connection string into Connection String and the fields fill themselves in, or type the Host, Port, Database, User and Password. For SQLite and DuckDB, click Choose… to pick a file or New… to create one.
Leave Save Password off to be asked each time you connect, or turn it on to keep the password in your Keychain.
Click Test Connection, then Add. A new connection is tried before it is filed; if the server does not answer, the sheet stays open and offers Add Anyway.
Click the connection in the sidebar to connect. Click a database to see its overview, a table to browse its rows, or press ⌘T for a query tab.
Oracle and IBM Db2 offer to download their maker's client library the first time. See Oracle and IBM Db2 clients.
Around the window
One window holds every connection. The sidebar is the map, the tab strip is what you have open, and each tab is an editor above its results.
One window: the sidebar, the tab strip and the toolbar, an editor above its results, and the status bar with the pager.
Sidebar. Folders of connections; under a connection its databases and schemas; under those a folder per kind of object, plus Queries, Diagram and the accounts folder. ⌥⌘S hides and shows it. See The sidebar.
Tab strip. One strip across all connections. ⌘T opens a query tab, the terminal button a console, the grid button every open tab at once.
Toolbar. The database and schema pickers, Run, Explain, Watch, Auto-Commit, Inspector, AI Assist and Commands.
Status bar. The connection's state, what the last run returned, the pager, and any pending changes with Discard and Commit…
Technical notes
A tab belongs to one connection and one database. Changing the database of one tab never changes another's.
Twelve engines, the hosted services built on them, and the ways to reach a server safely.
Engines and server versions
Every engine gets the sidebar, the grid, the assistant and a door to its own console client. The kinds of object that exist on an engine are the kinds it shows. The version ranges below come from each driver's own documentation, or from the catalog queries the app needs; a range is where things are known to work, not a promise about every release.
PostgreSQL
Default port
5432
SSL
Disabled, Required, Verify Certificate
Servers
PostgreSQL 11 and newer: the schema browser reads pg_proc.prokind, which arrived in 11.
Console
psql — brew install libpq
Notes
Every designer, including aggregates, conversions, operators, operator classes, extensions, casts and tablespaces. The fullest Users & Roles pane.
MySQL
Default port
3306
SSL
Disabled, Required, Verify Certificate
Servers
MySQL 5.7 and newer, as the driver documents.
Console
mysql — brew install mysql-client
Notes
Designers for tables, triggers, events and tablespaces. Each structure change applies at once, so a failure partway leaves the earlier changes in place; the designer says so.
MariaDB
Default port
3306
SSL
Disabled, Required, Verify Certificate
Servers
Supported through the MySQL driver, which publishes no minimum for MariaDB.
Console
mariadb — brew install mariadb
Notes
Everything MySQL gets, plus sequences.
SQLite
Connects to
A file on your Mac. No port, no SSL, no accounts.
Files
SQLite 3 database files, read with the SQLite library that comes with macOS.
Console
sqlite3 — brew install sqlite
Notes
A type, default, key or foreign-key change rebuilds the table in place, rows copied over in one transaction. See The table designer.
SQL Server
Default port
1433
SSL
Disabled, Required
Servers
SQL Server 2017 and newer: the app speaks TDS 7.4 and its catalog queries use STRING_AGG.
Console
sqlcmd — brew install sqlcmd
Notes
Synonyms, partition functions and partition schemes; a DENY is kept apart from a missing grant in Users & Roles. Defaults are named constraints, so changing one needs SQL.
Oracle
Default port
1521, by Service Name or SID
SSL
Disabled, or Verify Certificate with a Wallet Folder
Servers
Oracle Database 19c and newer, as Oracle documents for its 23 client.
Console
SQL*Plus — brew install InstantClientTap/instantclient/instantclient-arm64-sqlplus — or SQLcl — brew install --cask sqlcl and brew install openjdk
Disabled, or Verify Certificate with an optional Server Certificate
Servers
Db2 for Linux, UNIX and Windows; checked against Db2 Community 12.1. Db2 for z/OS and IBM i are not supported.
Console
db2cli, which comes with the downloaded client. brew install rlwrap gives it history and line editing.
Notes
Needs IBM's CLI driver, downloaded on first use. A table is editable in the grid when its primary key is enforced.
MongoDB
Default port
27017
SSL
Disabled, Verify Certificate
Servers
MongoDB 3.6 and newer, as the driver documents.
Console
mongosh — brew install mongosh
Notes
Queries are JSON command documents; see MongoDB and Redis commands. TLS cannot be combined with an SSH or proxy tunnel yet.
Redis
Default port
6379
SSL
Disabled, Required, Verify Certificate
Servers
Redis 3 and newer; the ACL users pane needs 6.0. Valkey connects with the same driver.
Console
redis-cli or valkey-cli — brew install redis
Notes
One command per line. A SCAN reply becomes a key browser you can edit.
DuckDB and data files
Connects to
A DuckDB file, or a Data File: a CSV, TSV, Parquet or JSON file, or a folder of them.
Files
DuckDB 1.1.3 is built in. It opens files written by DuckDB 0.9, 0.10, 1.0 and 1.1; a file from a newer release may not open.
Console
None. A DuckDB file allows one process at a time, and the app is it.
Notes
A data file is opened as a view through an in-memory DuckDB and read again on every query, so an edit to the file shows up on the next run.
ClickHouse
Default port
8123, the HTTP interface; ClickHouse Cloud uses 8443
SSL
Disabled, Required, Verify Certificate
Sign-in
Password, Cloud SSO or Cloud SSO (Read-Only). With SSO, click Sign In with Browser; no database password is needed.
Console
clickhouse-client — brew install --cask clickhouse. A browser sign-in has no console.
Notes
ClickHouse has no transactions, so grid changes apply statement by statement. Keys are fixed when a table is created, and there are no foreign keys.
Databricks
Connects to
A SQL warehouse over HTTPS on port 443, certificate always verified.
Fields
Host, Warehouse ID, Catalog, Schema and an Access Token. One Unity Catalog per connection.
Console
dbsqlcli — pip install databricks-sql-cli
Notes
Each request runs on its own, so interactive transactions and temporary objects are not available. No SSH or proxy tunnels: the connection is direct.
Hosted services
A service tile in the New Connection picker is an engine with the right defaults already set. Pick the tile, paste the host, and the port and SSL mode are what the service expects. Switching to another tile puts back whatever you had not touched.
Service
Driver
Port
SSL
Supabase
PostgreSQL
5432
Verify Certificate
Neon
PostgreSQL
5432
Verify Certificate
CockroachDB
PostgreSQL
26257
Verify Certificate
TimescaleDB
PostgreSQL
5432
Disabled
YugabyteDB
PostgreSQL
5433
Disabled
Valkey
Redis
6379
Disabled
ClickHouse Cloud
ClickHouse
8443
Verify Certificate
Technical notes
A service uses its engine's driver unchanged, so which features work depends on how closely the service follows that engine's protocol and catalogs.
Every default can be changed after the tile fills it in.
The connection editor
The editor is one sheet with up to four pages: General, Options, SSH Tunnel and Proxy. File-based engines show the first two only.
The General page of a saved PostgreSQL connection. The Connection String under the fields always spells what they hold.
How to
General holds the server: Host, Port, Database, User, Password with Save Password, the SSL mode and the Connection String. A file engine shows Database File with Choose… and New… instead.
Options holds the Name shown in the sidebar, the folder it is filed in, a Color and the Production switch.
Test Connection dials the server with what is on the sheet, without saving anything.
To change an existing connection, right-click it in the sidebar and choose Edit… Changing the engine keeps your server details and credentials.
Technical notes
An engine offers only the SSL modes it can carry out. A pasted or saved mode it cannot is moved to the strongest one it can.
Oracle adds Connect Using, Wallet Folder, Certificate DN (optional) and Call Timeout (seconds), 60 by default. IBM Db2 adds Server Certificate and Query Timeout (seconds), 60 by default.
Connection strings
The Connection String field reads a URL and fills in the fields above it, and it writes the URL for whatever the fields hold. Use it to paste what your host gave you, or to copy a connection to a colleague.
How to
Paste a string such as postgresql://user@host:5432/database, sqlite:///path/to/database.sqlite or duckdb:///path/to/file.duckdb.
If the string carries a password, Save Password turns itself on. Turn it off again to keep nothing.
Click the copy button to copy the string. It leaves the password out unless you have revealed it, and revealing a password asks for Touch ID.
Technical notes
Schemes read: postgres, postgresql, mysql, mariadb, sqlite, sqlite3, file, mssql, sqlserver, oracle, db2, mongodb, redis, rediss, duckdb, csv, tsv, clickhouse and databricks. A bare path starting with / or ~ is a file database.
Query keys read: sslmode (disable, require, verify-full), and per engine warehouse_id, schema, server_certificate, query_timeout, connect_using, wallet, certificate_dn, call_timeout and auth.
Text that is not a connection string changes nothing: the field says so and leaves the fields alone.
Passwords, Keychain and Touch ID
A password is either in your Mac's Keychain or nowhere. There is no password file, and no setting that writes one.
What connecting asks when Save Password is off.
How to
Keep it: turn on Save Password. The password, and any SSH or proxy password, become Keychain items.
Keep nothing: leave Save Password off, which is how a new connection starts. Connecting then asks with a sheet titled Connect to “name”. The answer is held until the app quits, so a reconnect does not ask again; tick Save Password on that sheet to stop being asked.
Guard it: in Settings ▸ Security, Require authentication to use saved passwords asks for Touch ID or your login password before a saved password is used. It is on by default.
Technical notes
All secrets sit under one Keychain service, dev.azcode.dbclient.connections: database, SSH and proxy passwords, and AI provider keys.
Showing a saved password on screen always asks for Touch ID, whatever the setting.
A reconnect after a dropped connection reuses the secret already unlocked, so it never interrupts you with a prompt.
Turning Save Password off and saving removes the Keychain item.
SSL modes
Three modes, named for what they check.
Mode
What it does
Disabled
No encryption. For localhost, or inside an SSH tunnel, which is already encrypted.
Required (no certificate check)
Encrypts the connection, without checking who is at the other end.
Verify Certificate
Encrypts, and checks the server's certificate and that it was issued for the host you typed.
Technical notes
PostgreSQL, MySQL, MariaDB, Redis and ClickHouse offer all three. SQL Server offers Disabled and Required. Oracle, IBM Db2 and MongoDB offer Disabled and Verify Certificate. Databricks is always Verify Certificate.
Verify Certificate is refused through an SSH or proxy tunnel: the driver dials the tunnel's local end, so there is no server name to check. Use Required there; an SSH tunnel has already authenticated the server.
Oracle reads its certificates from a wallet folder, and Db2 can be given a server certificate file. The other engines have no field for a private CA certificate.
SSH tunnels and proxies
Reach a database that only a bastion host can see, or one behind a corporate proxy. The tunnel runs inside the app; there is no ssh process to start first.
The SSH Tunnel page, set up for a bastion host with a private key.
How to
On the SSH Tunnel page, turn on Connect through an SSH tunnel and fill in SSH Host, SSH Port and SSH User.
Choose Password or Private Key, and for a key click Choose Key… The panel opens in ~/.ssh.
Pick a Host Key policy: Accept New Hosts records a host's key the first time and trusts it after; Require Known Host connects only to hosts already in known_hosts; Accept Any (insecure) checks nothing.
For a proxy, open the Proxy page, turn on Connect through a proxy server, choose SOCKS5 or HTTP and fill in the host, the port and, if it wants them, a user and password.
Technical notes
Private keys: unencrypted ed25519 keys in OpenSSH format. RSA and ECDSA keys and passphrase-protected keys are not supported yet; ssh-keygen -t ed25519 makes one that is.
Host keys are checked against ~/.ssh/known_hosts, which the app reads and never writes, plus its own list of hosts it accepted. A key that conflicts with a known one is always refused, with both fingerprints and the ssh-keygen -R line that clears it. A key marked @revoked is refused too.
With both pages on, the SSH connection itself goes through the proxy. An HTTP proxy is reached with CONNECT; default ports are 1080 for SOCKS5 and 3128 for HTTP. A tunnel or proxy that does not answer is given up on after 10 seconds.
Every network engine can use a tunnel except Databricks. MongoDB through a tunnel reaches the one server you named, not the rest of a replica set, and cannot use TLS there.
Production mode and connection colours
Two switches on the editor's Options page make a dangerous server hard to mistake for a safe one.
The Options page: red, and the Production switch, for a server that matters.
How to
Turn on Production. Every session on that connection then starts with auto-commit off, so a change waits for your commit. You can still switch auto-commit on for a session; the next connect starts it off again.
Pick a Color: Blue, Teal, Green, Amber, Red, Purple or Graphite. The whole workspace is tinted while that connection is in front.
Technical notes
Production mode relies on transactions. Databricks, ClickHouse, MongoDB and Redis have none, so they apply every change at once, and the editor says so under the switch.
Opening files
A database file is a connection. Drop one on the window or on the Dock icon, double-click it in Finder, or use File ▸ Open… (⌘O).
A SQLite or DuckDB file becomes a saved connection named after the file. Opening the same file again selects that connection rather than making a second one.
A .sql script opens as a query tab on the connection you have selected.
A CSV, TSV, Parquet or JSON file is opened from the New Connection picker's Data File tile, with Choose…
Technical notes
The app reads the first bytes before it trusts the name: a file that starts with SQLite's header is SQLite, and one with DuckDB's is DuckDB, so a DuckDB file called backup.db opens as DuckDB.
Extensions known by name: sqlite, sqlite3, db, db3, s3db, sl3, duckdb and ddb.
A script larger than 16 MB is not opened in the editor.
Oracle and IBM Db2 clients
Oracle and IBM Db2 can only be reached through their makers' client libraries, and the app ships neither. The first Oracle or Db2 connection offers to download the client from Oracle or IBM.
How to
In the connection editor, click Oracle Client Setup… or Db2 Client Setup…
Click Download. Oracle's Instant Client is about 140 MB, IBM's CLI driver about 20 MB.
The same sheet later offers Update, Remove…, Show in Finder, and Check Oracle for Updates or Check IBM for Updates.
Technical notes
Downloads come from download.oracle.com and public.dhe.ibm.com, under those companies' own terms. The app asks them about newer clients only when you press the button.
The download, the licence text inside it and then every extracted file are checked against SHA-256 digests built into the app. Nothing is kept unless all of them match.
Clients live in ~/Library/Application Support/DBClient/Oracle and …/Db2, one folder per version. brew uninstall --zap dbclient removes them.
This release is pinned to Instant Client 23.26 and the Db2 CLI driver 12.1.4, both for Apple silicon.
Dropped connections
Laptops sleep and VPNs drop. The app notices, reconnects, and tells you exactly what it knows about the statement that was in flight.
A statement that only reads is run again on the fresh connection, and you see its result.
Anything else is not run again. The message says the connection was lost while it ran, that the server may or may not have applied it, and asks you to check the data first.
If a transaction was open, the server rolled it back, and the message says that too.
Technical notes
A network connection is pinged every 15 seconds, with 10 seconds to answer. The same loop keeps trying to reconnect while the server stays away. File engines have no keepalive.
"Only reads" is judged by the statement's first word — select, show, explain, describe, pragma, values and the like — and excludes statements that call functions with side effects. Oracle, Db2 and Databricks statements are never re-run.
A reconnect puts back the tab's database, reloads the sidebar, and sets auto-commit to what the connection starts with.
Export and import connections
Move your set-up to another Mac, or hand a colleague the connections for a project, as one readable file.
Export Connections: tick what travels. Passwords stay behind unless you ask for them.
How to
Choose File ▸ Export Connections…, or right-click a connection and choose Export… to start with just that one.
Tick the connections and the saved queries to include. Turn on Include saved passwords only if you need them; it asks for Touch ID, an Apple Watch or your login password.
On the other Mac, choose File ▸ Import Connections…, or open the file. Each connection gets a choice: Add, Replace, Keep Both or Skip.
Technical notes
The file is plain JSON, marked "format": "dev.azcode.dbclient.connections", and carries each connection's settings, its folder and its place in the list.
Included passwords are plain text in that file. The sheet says so, and the file is written readable only by you.
On import, a connection that came from this same set-up is offered as Replace. One that only looks the same — the same engine, name and host and port, or the same file — is offered as Keep Both, and the copy is named "name (imported)". Imported passwords go straight into the Keychain.
A file from a newer version of the app is refused with a message to update, rather than half-read.
The sidebar
One tree for everything: folders, connections, databases, and a folder for each kind of object. It stays quick with hundreds of tables open.
How to
Folders: click New Folder… under the sidebar, then drag connections in, or right-click a connection and choose Move to Folder.
Several at once:⌘-click or ⇧-click connections to gather them, then drag them into a folder together, or right-click for Move to Folder, Remove from Folder and Export…
Filter: type in the Filter box to narrow the tree. The funnel beside it narrows by engine.
Objects: click a kind folder such as Tables for a listing tab of everything in it, with its own filter (⌘F). Right-click a table for Open, Show DDL, Show in Diagram, Copy Name, Rename…, Edit Table…, Import CSV…, Copy to Database…, Truncate… and Drop…
Technical notes
The filter searches what is already loaded. It never connects to a server or fetches a schema to answer.
A connection's dot is green once it is connected and its tree has loaded. A connection that was refused shows a red dot, with the reason on a line under it.
Settings ▸ Browsing ▸ Show built-in database objects decides whether system schemas and objects are listed.
Query
Write SQL, run it, read what came back, and keep what you want to run again.
The SQL editor
The editor knows where one statement ends and the next begins, so you can keep a whole script in a tab and run the one line you are looking at.
Completion as you type, then ⌘↩ runs the statement under the cursor.
How to
⌘↩ runs the statement under the cursor, or the selection when there is one. The status bar says ⌘↩ runs selection while text is selected.
⇧⌘↩ runs everything in the tab, statement by statement, and stops at the first failure.
⌘. stops a running query. ⌘R runs the last one again.
Completion opens by itself after two typed characters; esc opens it at once. ↑↓ move, ↩ or ⇥ accepts.
⇧⌘F formats the SQL, or only the selection. ⌘/ comments or uncomments the lines you are on. ⌘D deletes a line and ⇧⌘D duplicates it.
⌘F opens the find bar; ⌘G and ⇧⌘G walk the matches; ⌥⌘F finds and replaces.
Technical notes
Statements are split on ; with quotes, comments, nested block comments and PostgreSQL dollar-quoting respected. A chunk that is only a comment is not a statement. A CREATE FUNCTION, PROCEDURE, TRIGGER or EVENT takes the rest of the script as its body.
Completion offers the columns of the tables in your statement, tables and views, schemas, the schema's own functions and procedures, SQL keywords and built-in functions. After FROM or JOIN it offers tables only; after alias. that table's columns. Names come from the tab's own database; another schema is a schema. away. It never opens inside a string or a comment.
The formatter moves clauses onto their own lines, puts list items one per line, and writes keywords in capitals. It changes whitespace and keyword case and nothing else: strings, comments, quoted names and type names pass through as written. One ⌘Z undoes it.
Formatting and Explain are SQL features; MongoDB and Redis tabs do not have them.
Results, pages and the row cap
Each statement gets its own result: a grid when rows came back, the server's own completion line when none did. Results appear as each statement finishes.
A script run with ⇧⌘↩. The Summary lists each statement with how it ended; the one that returned rows has a result of its own beside it.
How to
After a script, a strip above the grid shows Summary and a chip for each statement that returned rows. The summary lists every statement with how it ended and how long it took; click one to read its SQL, and the server's error if it failed.
Page through a large result with the pager in the status bar: first, previous, a page number you can type, next, last, and the rows-per-page menu. ↑ and ↓ in the page field turn pages.
To sort, right-click a column header: Sort Ascending, Sort Descending, Clear Sort. Sorting a second column adds it to the first, and the headers then show the order as ↑1, ↓2.
⌘L shows the result log under the grid: what ran, when, and what the server said.
Technical notes
Paging happens on the server: the app re-runs your one statement for the window of rows you are looking at, and counts the total in the background. Rows per page can be 100, 200, 500, 1,000, 2,000 or 5,000; 1,000 to start with. The menu in the pager changes that tab only; the default is in Settings ▸ Browsing.
One result never holds more than a fixed number of rows: 5,000 on PostgreSQL, MySQL, MariaDB, SQLite, DuckDB, ClickHouse and Redis; 10,000 on SQL Server, Oracle, IBM Db2 and Databricks; 1,000 documents on MongoDB. A result cut there shows a warning and First 5,000 rows. A single SELECT is paged instead, so every row of it can be reached. The app's own catalog reads are not capped.
Sorting re-runs the query with a new ORDER BY when the tab holds exactly one statement, so the order covers every page. With several statements in the tab it sorts the rows already loaded.
Explain and Watch
Read the plan before you run something expensive, and keep an eye on a number that is changing.
How to
Click Explain, or press ⇧⌘E, for the plan of the statement under the cursor.
Click Watch to run the tab's last statement again on a timer. Click it again, or press ⌘., to stop.
Set the timer in Settings ▸ Browsing ▸ Watch interval: 1, 2, 5, 10 or 30 seconds, 1 minute or 5 minutes; 5 seconds to start with.
Technical notes
Explain sends the engine's own form: EXPLAIN on PostgreSQL, MySQL, MariaDB, DuckDB and ClickHouse; EXPLAIN QUERY PLAN on SQLite; EXPLAIN FORMATTED on Databricks; EXPLAIN PLAN FOR on Oracle and Db2; SET SHOWPLAN_ALL around the statement on SQL Server. A statement that already starts with EXPLAIN runs as written.
MongoDB and Redis have no Explain.
Auto-commit and transactions
With auto-commit on, each statement commits as it runs. With it off, changes wait in a transaction until you say so, and the app keeps reminding you that they are waiting.
How to
Click Auto-Commit in the toolbar, or press ⌥⌘T, to switch it off.
Run your changes. A dialog asks Commit changes to “connection”? with Commit, Roll Back and Not Yet.
After Not Yet, the status bar shows uncommitted transaction with Roll Back and Commit until you choose.
Technical notes
Available where the engine has transactions: PostgreSQL, MySQL, MariaDB, SQLite, SQL Server, Oracle, IBM Db2 and DuckDB. Not on Databricks, ClickHouse, MongoDB or Redis.
A connection marked Production starts every session with auto-commit off.
While a transaction is open, actions that would run on the same session — an import, a grid commit — are refused with a message asking you to commit or roll back first.
If the connection drops with a transaction open, the server rolls it back and the app tells you. See Dropped connections.
The command palette
⌘K opens one search box over your tables, their definitions, the app's actions and your other connections. It also lives in the toolbar as Commands and in the View menu as Command Palette…
⌘K, then a few letters of a table's name.
How to
Press ⌘K and start typing a table's name.
Pick Open schema.table to browse it, or DDL: table for its CREATE statement. Typing ddl or diagram narrows to those rows.
↑↓ move, ↩ runs the row, esc closes.
Technical notes
Rows: every table and view, twice (open, and DDL); a Diagram row per schema; the actions New Query Tab, New Table…, Open Console, Run Query, Run All Statements, Explain Statement, Show Result Log, Show Result as Chart, Refresh Schema and Add Connection…; and every other saved connection, to switch to it.
Typing narrows by the text you typed appearing anywhere in a row's title or subtitle, in any case. Rows whose title starts with it come first.
Console tabs
A console tab is a real terminal running the engine's own command-line client, already signed in with the connection's host, port, user and password, and through its SSH tunnel or proxy if it has one. The prompt, history and pager are the client's own.
A console tab running sqlite3 on a SQLite file.
How to
Click the terminal button in the tab strip, or right-click a connection and choose Open Console.
If the client is not installed, a sheet names it and shows the Homebrew line that installs it, with a copy button. Run the line in Terminal, then click Check Again.
When the client exits, the tab offers Restart.
Technical notes
Clients: psql; mysql or mariadb; sqlite3; sqlcmd; clickhouse-client; mongosh; redis-cli or valkey-cli; SQL*Plus or SQLcl; db2cli; dbsqlcli. The install line for each is under Engines.
The password is never put on the command line, where other processes could read it. It goes through the client's own environment variable — PGPASSWORD, MYSQL_PWD, SQLCMDPASSWORD, REDISCLI_AUTH — or is typed at the client's own password prompt with echo off.
The app looks for a client on your PATH, in Homebrew and in the usual install folders, and looks again whenever it comes to the front.
No console for DuckDB and data files, where one process may hold the file, nor for a ClickHouse Cloud browser sign-in. clickhouse-client speaks ClickHouse's native protocol, which a tunnel set up for the app's HTTP port does not carry, so a tunnelled ClickHouse connection has no console either.
The terminal is Ghostty's, built into the app.
MongoDB and Redis commands
MongoDB and Redis do not speak SQL, so their query tabs take the engine's own commands and show the reply as a grid.
MongoDB: a tab holds one or more JSON command documents — anything db.runCommand accepts. For example { "find": "users", "filter": {}, "limit": 10 } or { "aggregate": "orders", "pipeline": [], "cursor": {} }. Documents come back one row each, one column per key.
Redis: one command per line, written as in redis-cli, with quotes for values that contain spaces. A line starting with # is a comment. SELECT 2 moves the tab to database 2.
Technical notes
A plain find on one collection, with _id in the result and no projection, can be edited in the grid. So can the key browser a Redis SCAN returns: key, type, TTL and value.
Several Redis lines run in order and the last reply fills the grid.
Tabs and the overview
One strip holds the tabs of every connection, grouped in the sidebar's order. Clicking a tab of another connection takes you to that connection.
Show all tabs: the arrow keys walk the cards, Return opens one.
How to
⌘T opens a query tab, ⌘W closes the one in front, ⇧⌘T brings back the last one you closed.
⌃⇥ and ⌃⇧⇥ step through tabs; ⌘1 to ⌘8 jump to a tab by position and ⌘9 to the last.
Click the grid button, Show all tabs, for a card per open tab with its first lines. Arrow keys move, ↩ opens, esc closes.
Right-click a tab for Save Query…, Duplicate, Close, Close Others and Close All.
Technical notes
A tab opened by clicking something in the sidebar is provisional, its title in italics: the next thing you open takes its place, so browsing does not pile up tabs. Typing, running, editing a row, or double-clicking the tab makes it stay.
Closing tabs that hold unsaved work asks once, for all of them.
Saved queries
A saved query lives in the sidebar beside the database it was written for.
How to
Press ⌘S in a query tab. The Save Query sheet suggests a name from the first line.
Find it under that database's Queries folder. Click it to open it in a tab.
⌘S in a tab that came from a saved query saves back to it, without asking.
Technical notes
A query is filed under its connection and database, and each database's folder shows only its own.
When a grid has pending changes, ⌘S reviews and commits those instead.
Saved queries are kept in ~/Library/Application Support/DBClient/saved-queries.json, and can travel in an exported connections file.
Edit
Change rows in the grid, read the SQL that will run, and move rows in and out as files.
Editing rows in the grid
A result is editable when the app can tell, for every cell, which row of which table it came from. Nothing you type reaches the server until you commit.
One changed cell, one row marked for deletion and one new row: 3 pending, in the status bar.
How to
Double-click a cell, or right-click it and choose Edit Cell. ↩ keeps the change, esc drops it.
Use the buttons beside the editor: a picker for dates, enums and foreign keys, Restore original value, and Set NULL. In a number or a boolean, ↑ and ↓ step the value.
Click + in the status bar to add a row. Right-click a row number for Add Row and Delete Row.
Changed cells are washed orange, new rows green, rows marked for deletion red and struck through. The status bar counts them: 3 pending.
Technical notes
What is editable: one SELECT whose columns are plain table columns — no grouping, DISTINCT or UNION — from tables, not views, with every table's whole primary key in the result. Joins qualify: each cell is written to the table that owns it, with that table's key. A table without a primary key is read-only, and so is a Db2 table whose key is not enforced.
A read-only result shows a lock in the status bar; its tooltip says why.
Editors by column: text, whole and decimal numbers, booleans, dates, dates with times, enums, and foreign keys. A foreign key's picker lists the first 500 distinct values of the column it points at, with a filter; any other value can still be typed.
Long text and JSON — a JSON column, or a value with a line break or 80 characters — opens in its own editor sheet with Format and Condense, so the grid's rows stay one line tall.
Selection: click a row number for a row, a header for a column, a cell for a cell; ⌘ toggles and ⇧ extends. Right-click for Copy Cell, Copy Row, Copy Column, Copy as Markdown, Copy as JSON and Copy as INSERT. Rows are copied tab-separated, ready to paste into a spreadsheet.
Pending changes become SQL you can read before it runs, and they run together or not at all.
Review Changes: the exact statements, between BEGIN and COMMIT.
How to
Click Commit… in the status bar, or press ⌘S. Discard throws the pending changes away instead.
The Review Changes sheet shows every UPDATE, DELETE and INSERT, between BEGIN and COMMIT.
Click Commit. The grid reloads from the server.
Technical notes
All statements run in a single transaction. If one fails, the app rolls back, shows the server's error, keeps your pending changes and leaves the sheet open.
The wrapper is the engine's own: BEGIN…COMMIT, BEGIN TRANSACTION on SQL Server, an atomic block on Db2 and Databricks. ClickHouse has no transactions, so there the sheet says the changes apply statement by statement.
Every UPDATE and DELETE names its row by primary key.
While changes are pending, running another query in that tab, refreshing and importing are held back, so the rows under your edits do not move.
The inspector
The inspector is a panel on the right for the row, or the object, you have selected. Open it with the Inspector button or ⌥⌘I.
The inspector on Fields: the selected row as a form.
Fields shows the selected row as a form, one field per column. On an editable result you can change values here, with the same pickers, restore and Set NULL as in the grid; the changes join the grid's pending ones.
JSON shows the selected rows as JSON, with Copy JSON.
DDL shows the CREATE statement of the object selected in the sidebar, with Copy SQL.
Technical notes
On a read-only result, Fields is a document you can select and copy from, not a form.
The inspector remembers which of the three you last used.
Export a result
Take the rows in front of you away as CSV, JSON or SQL inserts.
Export Result, with a preview of what will be written.
How to
Click the export button in the result bar, or select rows and right-click for Export Rows…
In Export Result, choose CSV, JSON or SQL Inserts, and All rows or the Selected ones. The sheet previews what will be written.
Click Copy for the clipboard or Save… for a file.
Technical notes
CSV has a header row, commas, RFC 4180 quoting, and empty fields for NULL.
SQL Inserts name the table the result came from, when it came from one.
The export holds the rows that are loaded. To export a whole large table, raise the rows per page or page through it; see the row cap.
Import a CSV
Load a CSV file into an existing table, with each of its columns mapped to the column it belongs in.
Import CSV: each column of the file, with a sample value, beside the table column it fills.
How to
Right-click a table and choose Import CSV…, then pick the file.
In the Import CSV sheet, check First row is header and Empty fields become NULL.
Look over the mapping: each CSV column, with a sample value, beside the table column it will fill. Choose another column, or — skip —.
Click Import. When it finishes, the table opens.
Technical notes
Header names are matched to column names without regard to case; with no header, columns map by position.
Rows go in as multi-row INSERTs of 250. Where the engine has transactions they all run inside one, so a bad row leaves the table as it was. Databricks commits each batch on its own, and the sheet says so.
Importing waits while the tab has pending grid changes or an open transaction.
Show DDL
Read the CREATE statement of anything in the sidebar, rebuilt from the server's catalog.
Show DDL for a table: the CREATE statement, and its index after it.
How to
Right-click an object and choose Show DDL, or press ⌘K and type ddl and the table's name.
The sheet offers Copy SQL, Save…, and for a table Copy JSON Schema.
The inspector's DDL page shows the same statement for whatever is selected in the sidebar.
Technical notes
Available for tables and views, and for functions, procedures, triggers, events, sequences, types, synonyms, partition functions and schemes, aggregates, conversions, operators, operator classes, extensions, casts and tablespaces.
Design
Create and change tables, and every other kind of object your engine has, with the SQL in view before it runs.
The table designer
The designer loads a table's columns, indexes and foreign keys as drafts. As you change them, it works out the ALTER statements that turn what the server has into what you drew, and shows them to you.
The table designer: the columns, the selected column's settings under them, and the SQL preview at the bottom.
How to
Right-click a table and choose Edit Table…, or choose New Table… from a Tables folder. The designer opens as a tab, so your drafts survive a look at another tab.
Under Columns, edit Name and Type, tick PK and NN, and drag rows to reorder. Select a column for its Collation, Default, Auto-increment and Generated settings. Add Column is under the rows.
Switch to Indexes or Foreign Keys for Add Index and Add Foreign Key, with On Delete and On Update rules.
Read the SQL Preview. Problems show in red, notes in grey. Click Apply Changes; the designer stays open on the fresh structure.
Technical notes
PostgreSQL and DuckDB retype a column with ALTER COLUMN … TYPE … USING column::type, so existing values are cast.
MySQL and MariaDB can only change a column by restating all of it. The designer restates it in full, collation included, and refuses a column whose extra attributes it could not restate safely, pointing you to SQL.
SQLite cannot alter most things in place. The designer follows the SQLite manual's recipe — new table, copy the rows, drop the old one, rename, restore indexes and triggers — in one transaction.
The script runs in a transaction on PostgreSQL, SQLite, DuckDB and SQL Server. MySQL, MariaDB and Oracle apply each change at once, so a failure partway leaves the earlier ones in place; a note beside the buttons says so.
What an engine cannot do is stated, not hidden: ClickHouse keys are fixed at CREATE and its columns have no collation; SQL Server defaults are named constraints; DuckDB cannot add or drop foreign keys; Databricks tables have no conventional indexes. Apply Changes stays off while a red problem stands.
Drop… asks you to type the table's name.
Kinds of object
The sidebar shows a folder for each kind of object the engine has, and only those. Eighteen kinds of schema and server object, plus users and roles, make twenty.
Every kind has a listing tab, Show DDL and a Drop… that asks first. Designers open as tabs and preview their SQL.
Kinds that belong to the server rather than to a schema — partition functions and schemes, extensions, casts, tablespaces, accounts — hang from the connection, not from a schema.
The database overview
Click a database in the sidebar for a one-page summary: what it holds, what the server is, and a door to everything you can create.
The overview of a database: a row per kind of object, with its count and a + to make one.
The heading shows the connection, a live dot, and the server's version.
Objects has a card per kind with its count and a + to make a new one, plus Queries and Diagram.
Server has the server-level kinds, the accounts card with New User…, Grant Access… and New Role…, a Console card, and a card to create a database or schema.
Technical notes
A kind the engine supports shows even when there are none of it yet, with a count of 0, so the door to the first one is always there. The sidebar does the opposite and hides empty folders.
Opening the overview reads what the sidebar has already loaded. It sends nothing to the server.
There is one overview tab per database; clicking the database again brings it forward.
Users and roles
On engines with accounts, the sidebar lists them under Users & Roles — Users on MongoDB, ACL Users on Redis, Users, Groups & Roles on Db2, Principals & Permissions on Databricks.
Users & Roles on a PostgreSQL server.
How to
Open the accounts folder and double-click an account, or click New User…, New Role… or Grant Access…
Set the Name, Password, Abilities, Member of and Grants the engine offers.
Read the SQL preview, then click Create or Save. Edit as SQL hands you the script in a query tab instead.
Technical notes
PostgreSQL has the most to set: can log in, superuser, create databases, create roles, inherit, replication, bypass row security, a connection limit and an expiry date. Grants at database, schema and table level.
MySQL and MariaDB accounts are name@host, with a connection limit and grants at global, database and table level. ClickHouse grants at the same three levels.
SQL Server has three states for a permission: granted, denied, and not set. A DENY wins even where a role would allow, and the editor keeps it apart from a missing grant.
Oracle grants at system and table level. MongoDB grants are its built-in roles, such as read, readWrite and dbAdmin. Db2 and Databricks accounts come from outside the database, so they can be granted to but not created.
Redis shows each ACL user's rules read-only; change them with ACL SETUSER in a query tab or the console. The pane needs Redis 6.0.
SQLite, DuckDB and data files have no accounts, so no folder.
Understand
See how the tables fit together, turn a result into a picture, and ask questions in plain words.
The schema diagram
Every SQL database gets a diagram drawn from its real foreign keys: tables that refer to nothing at the top, the tables that depend on them below, unconnected groups set apart.
A schema of nine tables, drawn from its foreign keys.
How to
Click Diagram under a database in the sidebar, or the Diagram card on its overview, or right-click a table and choose Show in Diagram.
Drag the background or scroll to move around. Hold ⌘ and scroll to zoom about the pointer, from 50% to 200%.
Drag a table to put it where you want it; the rest stay put. Reset Layout undoes your placements.
Type in the filter (⌘F) to show only tables whose names match.
Double-click a table to open it. Double-click a line to edit that foreign key.
Technical notes
A line ends in a crow's foot for one-to-many and a bar for one-to-one, which is when the key's columns are also a primary key or a unique index.
A table that refers to itself gets a loop. Several keys between the same two tables are drawn as separate lines.
SQLite's keys that point at a table without naming a column are resolved to that table's primary key.
Views are left out. References into another schema are counted under the diagram rather than drawn.
The layout is worked out from the schema, so the same schema always draws the same way. Your placements last as long as the tab does.
MongoDB and Redis have no foreign keys, so no diagram.
Charts
Any result with a number in it can be a chart. The chart is another view of the same result, so the grid is one click away and a re-run redraws it.
A bar chart over a GROUP BY result.
How to
Click the chart button in the result bar, or press ⌘K and choose Show Result as Chart.
Pick the Chart type: bar, line, area, pie or scatter.
Pick X, one or more Y columns, a Series column to split by, and an Aggregate: None, Sum, Average, Count, Min or Max.
Click the copy button to put the chart on the clipboard as an image.
Technical notes
The first chart is a guess from your columns: dates on X give a line, categories a bar, two plain numbers a scatter. Columns that look like ids are not offered as Y.
Limits keep a chart readable: 8 series, 50 categories, 4,000 points, and 6 pie slices with the rest folded into Other. When a limit cuts something, a note under the chart says what.
The eight colours are fixed and checked for colour-blind readers in both appearances. They are never recycled, which is why series stop at eight.
The AI assistant
Ask about your data in plain words. The assistant sees your schema, can run read-only queries to check its answer, and writes SQL for you to run. It cannot change your data.
An answer from the assistant: the read-only query it ran, a table, and SQL to put in the editor.
How to
Open Settings ▸ AI. Choose a Provider — Claude, OpenAI, Gemini, Grok, DeepSeek or Mistral — paste your API key and click Save. The app fetches that provider's models; pick one under Model.
Click AI Assist in the toolbar, or press ⌥⌘A, and ask.
On a block of SQL in the reply, click Insert into the editor or Copy. You run it yourself.
Suggest completions while typing SQL offers whole-line suggestions in the editor. Turn it off in the same pane if you prefer the editor quiet.
Technical notes
What is sent, and only when you ask something: your question and the conversation, the engine and server version, the current database, table and column names with their types, up to 2,000 characters of the editor's text, and your custom instructions. Rows are sent only as the result of a query the assistant ran to answer you, at most 50 rows with long cells clipped.
The read-only guard is code, not a request to the model. A query the assistant wants to run must start with a reading word such as SELECT, WITH, SHOW or EXPLAIN, be a single statement, and contain no word that writes, grants, calls a procedure or starts a transaction. Functions with side effects are refused as well.
Then the engine enforces it again: the query runs on a separate connection in the engine's own read-only mode, such as BEGIN READ ONLY on PostgreSQL or PRAGMA query_only on SQLite. On SQL Server, Db2, Databricks and DuckDB, where no such mode is available, the assistant runs no queries at all; it still sees the schema and writes SQL.
A statement that would change data is handed back to you as text.
Your key is kept in the Keychain and goes only to the provider you chose. With no key saved, nothing is sent anywhere.
Move data
Copy tables from one server to another, even when the two do not speak the same dialect.
Copy objects between engines
Drag a table from one connection onto another connection's database, and the app writes the CREATE in the target's dialect and copies the rows.
Dragging a table from PostgreSQL onto a SQLite file.
How to
Connect to both servers. Drag a table — from the sidebar, or several at once from a Tables listing — onto a database of the other connection. Or right-click it and choose Copy to Database…
In the sheet, choose Structure and data, Structure only or Data only, and tick Replace existing if a table of that name is already there.
Read the script and any warnings, such as a column type that had to change. Click Copy. Stop ends it early.
Technical notes
Tables move between any two of the ten SQL engines: PostgreSQL, MySQL, MariaDB, SQLite, SQL Server, Oracle, IBM Db2, DuckDB, ClickHouse and Databricks. Column types are translated, and the sheet lists every one that changed, along with defaults or indexes that could not travel.
Views, routines and other objects are written in one engine's SQL, so they copy only to a server of the same dialect — PostgreSQL to PostgreSQL, MySQL to MariaDB.
Rows are read 1,000 at a time and written in INSERTs of 250. Each table's rows are copied in one transaction where the target has them, and rolled back if the copy fails or you stop it.
Creating the structure is not transactional on most engines. If a later step fails after Replace existing dropped the old table, the sheet tells you so.
Binary values that cannot make the trip arrive as NULL, and the summary counts them.
Specifications
The numbers and the fine print: what the app needs, what it keeps, what it talks to, and every key it answers to.
System requirements
macOS
15 Sequoia or newer
Mac
Apple silicon, M1 and later. There is no Intel build: the SQL Server driver's library ships for Apple silicon only.
Distribution
A disk image signed with a Developer ID certificate under the hardened runtime, notarized and stapled; or the Homebrew cask az-code-lab/taps/dbclient.
Account
None. The app never asks you to sign in.
Download
About 23 MB. Oracle's client adds about 140 MB and IBM's about 20 MB, only if you use those engines.
Built into the app: DuckDB 1.1.3, FreeTDS 1.5 for SQL Server, OpenSSL 3, and Ghostty's terminal for consoles. SSH is Apple's SwiftNIO SSH. SQLite is the one that comes with macOS.
Limits and defaults
What
Value
Rows in one result
5,000; 10,000 on SQL Server, Oracle, IBM Db2 and Databricks; 1,000 documents on MongoDB. A single SELECT is paged, so all of it can be reached.
Rows per page
100, 200, 500, 1,000, 2,000 or 5,000. Starts at 1,000.
Watch interval
1, 2, 5, 10 or 30 seconds, 1 or 5 minutes. Starts at 5 seconds.
Keepalive
A ping every 15 seconds, with 10 seconds to answer.
Tunnel and proxy connect
10 seconds.
Statement timeouts
Oracle and IBM Db2: 60 seconds unless you change it on the connection. ClickHouse requests: 300 seconds. Databricks requests: 60 seconds.
Foreign-key picker
The first 500 distinct values; others can be typed.
CSV import and table copy
INSERTs of 250 rows; a copy reads 1,000 rows at a time.
Charts
8 series, 50 categories, 4,000 points, 6 pie slices and Other.
Assistant
Sees at most 50 rows of a query it ran, and 2,000 characters of your editor.
Script files
A .sql file of up to 16 MB opens in the editor.
Window zoom
80% to 200%, in eight steps.
Where things are stored
Everything stays on your Mac. There is no cloud account behind the app, so there is nothing to sync and nothing to leak from a server of ours.
What
Where
Connections and folders
~/Library/Application Support/DBClient/connections.json — names, hosts, ports, users and options. Never a password.
The app has no telemetry, no crash reporter and no analytics. It opens a connection in these cases, and this is the whole list. The privacy page says the same in full sentences.
When
Where
Always
Your own database servers, through the tunnels and proxies you set up.
For updates
raw.githubusercontent.com for the published Homebrew cask, and github.com for the disk image when you install one.
Only while registered
azcode.dev, the licence server: when you register or deregister, and about once a day to check the registration. A copy that is not registered never contacts it.
Only with an Oracle or Db2 connection
download.oracle.com or public.dhe.ibm.com, when you click Download or check for a newer client.
Only with an AI key saved
The provider you chose: api.anthropic.com, api.openai.com, generativelanguage.googleapis.com, api.x.ai, api.deepseek.com or api.mistral.ai.
Only with ClickHouse Cloud sign-in
ClickHouse's own sign-in pages, in your browser.
Updates
The app reads the same Homebrew cask that brew upgrade does, so a DMG install and a Homebrew install learn about a release at the same moment.
It checks five minutes after launch and then about once a day. A check that fails says nothing.
When a newer build exists, the app tells you, and Update & Relaunch installs it. Nothing is downloaded without your say.
To check now, open Settings ▸ General and click Check for Updates.
Technical notes
Before the new app replaces the old one, the downloaded disk image's SHA-256 must match the cask's, and the app inside must pass codesign --verify --deep --strict and carry the same Developer ID team.
Languages, appearance and zoom
Twelve languages: English, Chinese (Simplified and Traditional), Japanese, Korean, German, French, Spanish, Italian, Portuguese (Brazil), Russian and Indonesian. The app follows macOS; Settings ▸ General ▸ Language picks another, from the next launch.
Appearance: light or dark, following macOS.
Zoom:⌘+ and ⌘− scale the whole window, text and controls together; ⌘0 returns to actual size.
Keyboard shortcuts
Keys
What it does
⌘↩
Run the statement under the cursor, or the selection
⇧⌘↩
Run everything in the tab
⌘.
Stop the running query, and any watch
⌘R
Run the last statement again
⇧⌘E
Explain the statement under the cursor
⇧⌘F
Format the SQL, or the selection
⌘/
Comment or uncomment lines
⌘D · ⇧⌘D
Delete the line · duplicate the line
⌘F
Find in the editor or the pane in front; in a listing or a diagram, the filter
⌘G · ⇧⌘G
Next match · previous match
⌘S
Review and commit pending grid changes; otherwise save the query
⌘L
Show or hide the result log
⌘K
Command palette
⌘T · ⌘W
New query tab · close the tab
⇧⌘T
Reopen the last closed tab
⌃⇥ · ⌃⇧⇥
Next tab · previous tab
⌘1 – ⌘9
Go to a tab by position; ⌘9 is the last
⌥⌘T
Auto-commit on or off
⌥⌘I
Show or hide the inspector
⌥⌘A
Show or hide the AI assistant
⌥⌘S
Show or hide the sidebar
⌘O
Open a database file or a script
⌘+ · ⌘− · ⌘0
Zoom in · zoom out · actual size
⌘,
Settings
Licences and local connections
DBClient is free for local connections. Connecting to a server elsewhere needs a licence. Prices are on the pricing page, and so is the form that buys one.
Local is a database on the Mac you are using: a SQLite or DuckDB file, a data file, or a server listening on localhost, such as a Homebrew PostgreSQL or a Docker container.
Remote is everything else: another host, a server reached through an SSH tunnel or a proxy, and every hosted service.
A licence key registers one Mac at a time, and moves to another whenever you like. A Team & Business order starts at five.
A licence runs for a year, upgrades and support included. When the year ends, remote connections stop until it is renewed; local ones stay free, and nothing you saved is touched.
Renewing is done on the renew page, with the licence key and the email address it was issued to; there is no sign-in. The term is added to the end of the one you have, or starts today for a licence that has lapsed, and the key stays the same.
Buying is paid through Stripe, PayPal or Square. The keys are shown as soon as the payment is through and mailed to you with a receipt and an invoice; they are kept at azcode.dev too, where your purchase email signs you in without a password.
How to
Open Settings ▸ License. Enter the email address the licence was issued to and one of its keys, and press Register. The pane then says to whom the copy is registered and until when.
To move the key to another Mac, press Deregister This Mac there — or simply register on the other Mac with the same email and key, and the key moves over.
An unregistered copy that is asked to connect to another machine says so, and offers Enter License… and Buy a License…. A new remote connection can still be filed with Add Anyway.
Technical notes
Local is decided from the saved connection alone: a file, or a host that is this Mac's loopback (localhost, 127.0.0.1, ::1) with no SSH tunnel and no proxy. The Mac's own network name or LAN address counts as remote.
Registering sends the email address, the key, this Mac's serial number, the app's version and the macOS version to the licence server, which binds the key to that serial number. A registered copy asks the server about once a day whether the registration still holds, without the key. Only a definitive "no" ends it — the key was moved, withdrawn, or its term ran out. Being offline never does.
A copy that is not registered never contacts the licence server. A connection already open when a registration ends is left alone; the next remote connect is turned down.
The registration is kept in the app's preferences, not the Keychain. There is still no account and no sign-in.
How DBClient is tested
A database client that gets a statement wrong can cost someone their data, so nearly everything in the app is pinned by a test that runs before every release.
More than three thousand unit tests cover the parts that decide what SQL is sent: the statement splitter, the editability rules, the ALTER planner for each engine, type translation between engines, the assistant's read-only guard.
End-to-end stages launch the real app and drive its windows the way a person does — clicks, drags, typing — then read the result back off the screen.
Both run against real servers in Docker: PostgreSQL, MySQL, MariaDB, SQL Server, Oracle, IBM Db2, MongoDB, Redis, ClickHouse, TimescaleDB, CockroachDB, YugabyteDB and Valkey, plus an SSH server and a proxy for the tunnels.
This website has its own checks: every link and every guide on it must resolve, and every page is loaded in a browser in both appearances.