A terminal database client for Postgres, MySQL, SQLite, DuckDB, ClickHouse, and SQL Server — with a built-in MCP server, vim editing, and a WASM plugin runtime.

Features
- Six database engines through one TUI: Postgres, MySQL, SQLite, DuckDB, ClickHouse, SQL Server.
- MCP server built in —
narwhal mcpexposeslist_connections,describe_schema,run_query,explain_query,get_diagram, and more to any Model Context Protocol client. Read-only by default, with a workspace ACL. - Vim editing — Normal / Insert / Visual modes, operator-pending
(
dw,yy,dgg), multi-cursor (Alt-N/Alt-A), schema-aware completion. - Schema tooling — ER diagrams (Focused, Impact, Mermaid / DOT
export),
:schema-diffbetween two connections with dialect-specific DDL emit,:difffor migration SQL. - Streaming results — rows arrive incrementally; the result pane ticks
live and you can cancel mid-run with
Ctrl-C. - Plugin runtimes — Lua (
~/.config/narwhal/plugins/*.lua) and a WASM component-model runtime (wasmtime+ capability sandbox). - Connection vault — HashiCorp Vault and 1Password backends in
addition to the OS keyring and
~/.pgpass. - Append-only audit log — JSONL sink for SOC 2 / ISO 27001 evidence, with file rotation and optional syslog.
- Inline visualisation —
:chart bar|line|sparklineand:pivot rows=… cols=… value=… agg=…over any result set. - Export to anything — table, CSV, JSON, TSV, Markdown, Parquet (with Snappy / Zstd compression).
- SSH tunnels — declare
ssh_hostin your connection and the local port forward is opened transparently. - Headless mode —
narwhal exec --conn prod 'SELECT …'for cron, CI, and shell pipelines.
Install
Cargo
cargo install narwhaldb
The crate name is narwhaldb (the bare narwhal slot belongs to an
unrelated 2018 Docker library); the installed binary is narwhal.
For users without a Rust toolchain:
cargo binstall narwhaldb
Package managers
brew install Nonanti/tap/narwhal # macOS / Linux
yay -S narwhal # Arch (AUR)
nix run github:Nonanti/narwhal # Nix
Pre-built binaries
Download from the latest release:
x86_64-unknown-linux-gnuaarch64-apple-darwin(Apple Silicon)
Quick start
narwhal # launches the TUI
Then inside the TUI:
:add— connection wizard (or:url postgres://user:pass@host/db).:open <name>— connect; the sidebar fills with schemas and tables.- F6 — run the buffer. Results stream into the lower pane.
- F1 — full keymap.
Configuration
Connections live in ~/.config/narwhal/connections.toml:
[[connection]]
id = "00000000-0000-0000-0000-000000000001"
name = "prod-pg"
driver = "postgres"
color = "red" # status bar tint
read_only = true # syntactic write guard
confirm_writes = true # type YES before any mutation
[connection.params]
host = "db.example.com"
port = 5432
username = "alice"
password = "${vault:secret/prod/db-password}" # or use keyring
database = "app"
ssl_mode = "verify-full"
Settings live in ~/.config/narwhal/config.toml:
theme = "dark" # dark | light | high-contrast
[editor]
tab_width = 4
line_numbers = true
[diagram]
icons = "ascii" # ascii | nerdfont
See docs/ for vault, audit log, MCP, and plugin configuration.
Database support
| Engine | Driver | TLS | Streaming | Cancel | DDL emit |
|---|---|---|---|---|---|
| PostgreSQL | tokio-postgres + rustls |
✅ verify-full | ✅ | ✅ | ✅ |
| MySQL / MariaDB | mysql_async |
✅ | ✅ | ✅ | ✅ |
| SQLite | rusqlite (bundled) |
n/a | ✅ | — | ✅ |
| DuckDB | duckdb (bundled) |
n/a | ✅ | — | ✅ |
| ClickHouse | HTTP + rustls | ✅ | ✅ | ✅ | ✅ |
| SQL Server | tiberius + rustls |
✅ | ✅ (buffered) | — | ✅ |
Drivers are feature-gated. The default cargo install narwhaldb includes
all six; build with --no-default-features --features driver-postgres to
slim the binary.
MCP server
narwhal mcp # JSON-RPC stdio server
Wire into Claude Desktop:
{
"mcpServers": {
"narwhal": { "command": "narwhal", "args": ["mcp"] }
}
}
A repo-local .narwhal/workspace.toml (committed alongside your code)
scopes what an agent can see:
allowed_connections = ["staging"]
allow_writes = false
Every database call is audit-logged with source: "mcp". See
docs/plugins/mcp-tools.md for the full
tool surface.
Headless exec
narwhal exec --conn prod 'SELECT count(*) FROM users'
narwhal exec -c prod -f csv 'SELECT * FROM orders' > orders.csv
narwhal exec -c prod -f json 'SELECT id, email FROM users' | jq '.[].email'
# Writes are sandboxed (BEGIN…ROLLBACK) by default. --write opts out:
narwhal exec -c prod --write 'UPDATE users SET banned = true WHERE id = 42'
Formats: table, csv, json, tsv, markdown, parquet.
Plugins
Lua (trusted)
Drop a .lua file in ~/.config/narwhal/plugins/:
narwhal.register_command("rc", "row count", function(table)
local r = narwhal.sql_run("SELECT count(*) FROM " .. table)
return tostring(r.rows[1][1])
end)
Six worked examples in examples/plugins/.
WASM (sandboxed)
Component-model plugins via wasmtime with a capability sandbox
(64 MiB memory, 100M fuel, 256 KiB KV store). Manifest declares
capabilities; host policy enforces them. See
docs/plugins/wasm.md.
Documentation
| Topic | File |
|---|---|
| Architecture & layering | docs/ARCHITECTURE.md |
| Coding style | docs/STYLE.md |
| MCP server tools | docs/plugins/mcp-tools.md |
| Connection vault | docs/vault.md |
| Audit log | docs/audit.md |
| Schema diff | docs/schema-diff.md |
| LSP (embedded) | docs/lsp.md |
| Driver matrix | docs/drivers/ |
| WASM plugins | docs/plugins/wasm.md |
| Plugin security | docs/plugins/security.md |
| Release process | docs/RELEASING.md |
Building from source
git clone https://github.com/Nonanti/narwhal.git
cd narwhal
cargo build --release # binary at target/release/narwhal
Requires Rust ≥ 1.85 (edition 2024) and a C++17 toolchain for the bundled DuckDB build. On Nix:
nix develop # pulls cmake, clang, libcxx, libclang
cargo build --release
Tests
cargo test --workspace
Driver integration tests gated behind #[ignore] require Docker
(Postgres, MySQL, SQL Server, ClickHouse testcontainers); run with
cargo test -- --include-ignored.
Upgrading from v1.x
narwhal 2.0 ships a v2 schema for connections.toml and config.toml.
On first launch against a v1 file you'll see a warning; run:
narwhal migrate-config # writes v2 in place, keeps .v1.bak
narwhal config validate # dry-run check
See CHANGELOG.md for the full breaking-change list.
Comments