Skip to content
Wharfbase

The wb CLI

wb (also wharfbase) does from a terminal what the web UI does: find databases, inspect them, connect, browse, run SQL, open a native client, and look after the installation. Every command can print JSON for scripts.

On the server, nothing to install: the installer’s wharfbase command passes everything it doesn’t know to the CLI inside the container, which needs no login (it acts as the owner through a socket only the server can open).

Terminal window
wharfbase ls
wharfbase query pg17 "SELECT count(*) FROM orders"

The installer’s own commands (up, down, restart, logs, status, upgrade, uninstall) keep their meaning: wharfbase status shows the containers, wharfbase wb status the CLI’s summary. Without the installer’s command: docker exec -it wharfbase wb ls.

From your laptop or CI, install it from npm (Node 24+) and sign in:

Terminal window
npm install -g wharfbase
wb login https://db.example.com # asks for email, password, and a 2FA code if you use one
wb ls

A URL without https:// means plain HTTP (fine for localhost; elsewhere wb login warns you). For a self-signed certificate add --insecure. The session lasts 30 days (WB_CLI_SESSION_TTL_DAYS) and is stored in ~/.config/wharfbase/config.json, readable only by you. wb logout ends it.

Several servers: wb login https://staging.example.com --profile staging, then --profile staging on any command (the last login becomes the default). In CI, set WB_URL and WB_TOKEN instead of logging in: on Team, WB_TOKEN can be an API token from Settings → API tokens (wbt_…, scoped and revocable).

Terminal window
wb status # version, plan, databases found/connected, last scan
wb scan # scan now and print what changed (--deep also looks for SQLite files)
wb ls # every database: engine, version, container, endpoint, reachability, findings
wb ls --engine postgres,mysql --status running
wb inspect pg17 # everything about one: storage, credentials hints, consumers, findings, evidence
wb topology # compose projects → containers → the databases they run or use
wb topology --dot | dot -Tsvg > topology.svg
wb findings --severity high

While wb scan waits, it shows where the scan is: inspecting containers 120/200, checking databases, finding SQLite files, saving. That works for a scan of an agent’s server too (--server).

<db> can be the id (or its first characters), the name shown in the list, the container name or the compose service. When a name matches several databases, wb lists them and asks for the id.

Terminal window
wb connect pg17 # asks for the user and password; offers the container's password when it has one
wb connect pg17 --user app --use-detected-password --remember
printf %s "$PW" | wb connect mysql --user root --password-stdin --read-only
wb dbs pg17 # databases on that server, with sizes
wb tables pg17 -d appdb -s public # tables, views, collections, with row estimates
wb describe pg17 customers # columns, indexes, foreign keys

A connection follows the same rules as in the UI: production databases are read-only until writes are unlocked, viewers are read-only, and the Community plan connects up to 5 databases per server (wb connect exits with code 5 at the limit). A password you didn’t save is asked for again when the server restarts (or pass --password-stdin).

Terminal window
wb query pg17 "SELECT id, email FROM customers ORDER BY id LIMIT 10"
wb query pg17 --format csv "SELECT * FROM orders" > orders.csv
cat migration.sql | wb query mysql -d appdb # a whole script from stdin; stops at the first error
wb query redis "HGETALL user:1" # console commands for Redis and MongoDB
wb query pg17 --json "SELECT 1" | jq '.statements[0].rows'

Results stream as they come. Each statement stops at 1,000 rows unless you pass --max-rows. Ctrl+C cancels the query on the database. The same read-only rules and audit log as the SQL editor apply.

Terminal window
wb shell pg17 # psql, mysql, mongosh or redis-cli, already connected

It starts the database’s own client in a short-lived container on the database’s network, and removes it when you exit. This needs ephemeral containers, which are off by default: add WB_GUARD_ALLOW_EPHEMERAL=true to .env next to docker-compose.yml and run wharfbase up. The connection must be writable for you, since the client can do anything the database user can.

Terminal window
wb users ls
wb users add [email protected] --role editor # prints a temporary password (shown once)
wb users reset-password [email protected]
wb users unlock [email protected]
wb users role [email protected] admin # "owner" transfers ownership
wb audit verify # checks the audit log's hash chain
wb audit tail -f --action sql.executed
wb license status # plan, limits, usage, expiry
wb license activate eyJhbGciOi… # or `-` to read the key from stdin
wb license deactivate # frees this instance's seat; --force when the license server is unreachable
wb doctor # checks Docker, the guard, networks, disk, the master key
wb doctor --bundle # a redacted zip for support (see "What Wharfbase reads")

On the server only:

Terminal window
wharfbase setup-token # before setup: the first-run token
wharfbase keys rotate # a new master key; every stored password is re-sealed

With the standard install the key is a file you own (secrets/master.key), so keys rotate prints the new key once: put it in that file and run wharfbase up. The restart re-seals everything; until then the old key keeps working. With a key Wharfbase generated in its data volume, the rotation happens right away.

wb store migrate copies the store to PostgreSQL, for a hub that will run on it (and, on Team, with a standby next to it). Run it inside the container, with the URL in the environment so the password stays out of the process list:

Terminal window
docker exec -it -e WB_STORE_URL="postgres://wharfbase:…@db:5432/wharfbase" wharfbase wb store migrate

Then set WB_STORE_URL for the container and restart it. High availability has the whole procedure.

  • --json prints one JSON document per command (audit tail -f prints one per line). Each command’s output has a schema: apps/cli/test/schemas.
  • -q prints only ids (wb ls -q, wb connect … -q) and silences progress.
  • Errors go to stderr; with --json as {"error":{"code","message","hint"}}.
  • Exit codes: 0 ok, 1 error, 2 usage, 3 auth, 4 not found, 5 plan limit, 6 unreachable, 130 cancelled.
Terminal window
for id in $(wb ls -q --engine postgres --status running); do
wb inspect "$id" --json | jq -r '"\(.displayName) \(.version)"'
done
  • Not signed in (exit 3): wb login <url>, or check --profile, WB_URL and WB_TOKEN.
  • Cannot reach … (exit 6): the URL, the port (8480 by default) and any reverse proxy in between. Behind a proxy, wb shell also needs HTTP upgrades passed through (the same setting as websockets).
  • Permission denied on the admin socket: inside the container, run it as the server’s user: docker exec -u wharfbase wharfbase wb … (the wharfbase command does).
  • wb shell: “the socket-guard does not allow”: see A native client. “The image … is not on the server”: pull it there once (docker pull postgres:17-alpine); Wharfbase never pulls images.