A modern, restrained desktop Postgres client. Neutral surfaces, hairline borders, and a single indigo accent reserved for actions and active state. Built with Tauri (Rust) + React + TypeScript.
Status: early v1. The core loop works end to end — connect, browse the schema, run a query, and see results. See Roadmap for what is intentionally not built yet.
Implemented in this milestone:
- Connection management — connect by connection string or individual host / port / database / user / password fields, with a "Test connection" action. Connections are saved to a local JSON file and listed on launch.
- Schema browser — schemas, tables/views, and columns (with types and primary-key markers) read from the Postgres catalog and rendered as a collapsible sidebar tree. Right-click (or double-click) a table for "Select top 100".
- SQL editor — CodeMirror 6 with Postgres syntax highlighting, multiple tabs, and Cmd/Ctrl+Enter to run. Errors from Postgres are shown inline near the editor, never in a modal.
- Results grid — tabular results with a default
LIMIT 100applied to unboundedSELECTs, numeric columns right-aligned, and an "Export CSV" action. - Query history — every executed query is logged locally with a timestamp; a side panel browses and re-runs past queries.
Not built yet (planned): inline cell editing with generated UPDATEs, and the
no-SQL table browser. See Roadmap.
| Layer | Choice |
|---|---|
| Shell | Tauri 2 (Rust) |
| Frontend | React 18 + TypeScript + Vite |
| Editor | CodeMirror 6 |
| State | Zustand |
| DB driver | tokio-postgres + native-tls |
The guiding principle is that all database-specific logic lives behind one driver interface in the Rust backend, so a second engine could be added later without touching the UI.
src-tauri/src/db/mod.rsdefines the engine-agnostic types (ConnectionParams,SchemaNode,QueryResult, ...) and theDatabaseDriver/DbSessiontraits.src-tauri/src/db/postgres.rsis the only module that depends ontokio-postgres. Result values are read withsimple_query, which returns every value in its canonical text form — so the grid needs no per-type conversion code.- The frontend never constructs SQL except the user's own editor text. The
default
LIMITfor unbounded selects and the "Select top 100" statement are both generated in the backend (src-tauri/src/commands.rs).
src-tauri/src/
db/
mod.rs driver interface + shared types
postgres.rs Postgres implementation
error.rs structured DbError (carries SQLSTATE, hint, position)
connections.rs saved-connection persistence (JSON)
history.rs query history log (JSONL)
state.rs Tauri-managed app state (one active session)
commands.rs the command surface exposed to the frontend
lib.rs / main.rs app wiring + entry point
src/
api/backend.ts typed wrappers over the Tauri commands (the only bridge)
state/store.ts Zustand store + backend orchestration
components/
connection/ the connection screen
workspace/ top bar, schema tree, editor tabs, editor, results, history
common/ shared frame / spinner / button styles
styles/tokens.css design tokens transcribed from the design spec
- Node.js 18+
- Rust (stable) and the platform toolchain Tauri requires (on macOS: the Xcode command line tools)
npm install # install frontend dependencies
npm run app # launch the desktop app (Tauri dev)Other useful scripts:
npm run dev # frontend only, in the browser (backend commands unavailable)
npm run build # type-check + build the frontend bundle
npm run app:build # produce a distributable desktop buildRust unit tests:
cd src-tauri && cargo testThere is also a live end-to-end smoke test, ignored by default. Point it at a throwaway database:
cd src-tauri
CUBBYDB_TEST_DSN="postgresql://user@localhost:5432/your_db" \
cargo test --lib -- --ignored --nocaptureCubbyDB stores everything under the OS app-data directory
(~/Library/Application Support/com.cubbydb.app on macOS):
connections.json— saved connectionshistory.jsonl— query history
Security note: in this version, saved connection passwords are written to
connections.json in plaintext (the file is created with 0600 permissions on
Unix). Moving secrets to the OS keychain is planned. Don't save credentials you
wouldn't keep in a local dotfile.
Deliberately out of scope for v1: autocomplete/IntelliSense, engines other than Postgres, multiple simultaneous connections, accounts/sync/cloud, and release automation. Planned next steps within v1:
- Inline cell editing → backend-generated
UPDATEscoped by primary key (read-only cells, with a note, when a table has no detectable key). - The no-SQL table browser (design screen 04).
- Moving saved-connection secrets into the OS keychain.
MIT — see LICENSE.