macOS menu bar app and CLI
Tablefast
Opens SQLite files on remote servers in TablePlus. A local Postgres proxy in Rust that passes queries over SSH to sqlite3 on the server.
- Status
- in use for me, not public
- First session
- 16 September 2026
- can we rebuild it in rust ?
- Stack
- Rust, Tokio, pgwire, tray-icon, SSH
Why I built it
Several of my apps store their data in SQLite, right on the server. To look inside I use TablePlus, but it only opens SQLite as a local file.
There’s Remote SQLite for that, a small Mac app. One thing bothered me: it ships its own SSH client. My keys aren’t on every server, though. I can only get further through a bastion host, and that’s exactly what the app couldn’t do. So I looked at how it works and rebuilt the idea in Rust, cut down to what I need: multiple servers over SSH and TablePlus.
What it does
- Lives as a feather icon in the menu bar. One click starts a connection, a submenu opens it straight in TablePlus.
- Every server in the config file gets its own local port.
- In TablePlus you create a normal PostgreSQL connection to
127.0.0.1. User and password don’t matter. - Read-only by default. Write statements are rejected on the Mac already.
Technical decisions
Pretend to be Postgres. TablePlus queries the catalog, so pg_class, information_schema and friends. The proxy answers those from sqlite_master and PRAGMA. Real queries go unchanged to a long-running sqlite3 -batch on the server, in JSON mode. pgwire handles the protocol.
The real ssh instead of an SSH library. That way my SSH config, the agent and Tailscale just work. For the bastion host I use nested SSH instead of ProxyJump. The bastion connects onward with its own key, so my Mac key never has to be on the target server.
Everything in one binary. The menu bar runs on tray-icon, no Swift project on the side. About 2,400 lines of Rust.
From the sessions
It started on September 16, 2026 with the question of how Remote SQLite actually works. By the afternoon, four databases from different servers were open in TablePlus.
In between was a lot of small stuff that only real use turns up. Every table showed up several times, because TablePlus asks per schema and per type and the proxy ignored the filters. The schema was suddenly called 2200, because I had sent the OID before the name. A knowledge base with embeddings crashed the proxy, because sqlite3 writes BLOBs into JSON as raw bytes. And the menu bar icon was invisible at first, then showed up twice.
The most important question came at the end: is this dangerous on production databases? Reading is fine, with care. Writing isn’t. That’s why read-only is the default, and I close TablePlus when I’m done.