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.

narwhal demo

Features

  • Six database engines through one TUI: Postgres, MySQL, SQLite, DuckDB, ClickHouse, SQL Server.
  • MCP server built in — narwhal mcp exposes list_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-diff between two connections with dialect-specific DDL emit, :diff for 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|sparkline and :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_host in your connection and the local port forward is opened transparently.
  • Headless modenarwhal 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-gnu
  • aarch64-apple-darwin (Apple Silicon)

Quick start

narwhal                        # launches the TUI

Then inside the TUI:

  1. :add — connection wizard (or :url postgres://user:pass@host/db).
  2. :open <name> — connect; the sidebar fills with schemas and tables.
  3. F6 — run the buffer. Results stream into the lower pane.
  4. 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.