DEV Community

Cover image for QueryVault: a documented home for your SQL
Rajesh Kumaravel
Rajesh Kumaravel

Posted on

QueryVault: a documented home for your SQL

🌐 Visit Query Vault

The scariest key on any developer's keyboard is Enter, right after a query they didn't fully read...

Every team that runs a serious database has a second, unmanaged database: the queries people actually use. They live in text files, chat scrollback, and one colleague's memory β€” and they get re-derived under pressure, against production, with auto-commit on.

QueryVault is my answer, now open source under MIT: a desktop app (macOS/Windows) that makes those queries first-class β€” documented, searchable, shareable β€” and executes them behind deliberate guard rails.

the workbench

The two problems in the trench coat

The tribal-query mess is really two problems occurring at the same moment:

  1. Knowledge β€” the query isn't documented anywhere structured. Wikis hold text, but nothing connects the doc to execution, so it drifts silently.
  2. Safety β€” the tools people execute with are DBA-grade IDEs whose defaults trust the operator. That's wrong for the support engineer running someone else's UPDATE at 2 a.m.

You look the query up because you're about to run it somewhere that matters β€” so QueryVault handles both in one surface. A saved query carries a title, markdown notes, tags, and named bind parameters (:customer_id, detected outside strings and comments, prompted at run time, remembered per query). Search is SQLite FTS5 across titles, SQL, notes, and tags. And execution is where the opinions live.

The safety model

Four layers, each enforced as low in the stack as it can be:

  • Typed intent, scaled to risk. Anything that isn't a SELECT requires typing RUN. On a profile tagged prod, the dialog turns red and demands the profile name.
  • Nothing commits itself. Writes run with autoCommit: false on a long-lived per-profile connection. The status bar turns amber while changes are pending; quitting rolls back.
  • Review before commit. Every uncommitted statement is inspectable β€” rows affected, bind values, and for in-grid cell edits the exact old β†’ new. Commit opens a totals confirmation with Roll back instead beside it.
  • Read-only profiles refuse writes in the main process β€” not a disabled button in the UI.

reviewing pending changes

The statement classifier is deliberately conservative: DML, DDL, and PL/SQL are guarded β€” and so is anything starting with WITH, even though CTEs are usually reads. Proving a CTE read-only across dialects is exactly the cleverness that eventually produces a false negative on production. Four extra keystrokes vs. an incident: easy trade.

One Oracle-specific wrinkle, stated honestly: DDL commits implicitly on Oracle β€” no client can prevent it β€” so the pending-transaction model tracks DML only, and DDL is caught at the confirmation step.

Architecture: a security decision that organizes the code

Architecture

Three boundaries do the work:

  1. The renderer is untrusted. sandbox: true, contextIsolation: true, nodeIntegration: false. Its entire capability surface is one function β€” window.api.invoke(channel, …) β€” typed end-to-end by a single shared IPC contract (src/shared/ipc.ts). New capability = new channel in that map + a handler in the main process. There is no second path.
  2. All database work lives in the main process behind a DatabaseAdapter interface with two implementations: OracleAdapter (node-oracledb Thin mode β€” pure TCP, no Oracle Instant Client to install) and DemoAdapter (the bundled SQLite demo dataset: 250k orders, 1k customers, 5k tickets, so every screen works with zero setup β€” and contributors never need Oracle).
  3. Safety enforcement sits below the UI, so no renderer bug can bypass it.

Arch

Things deliberately not built

  • No cloud/sync. Sharing is explicit JSON export/import (titles, SQL, notes, tags β€” never passwords or bind values). A sync backend means accounts, a server attack surface, and a governance conversation; local-first keeps the threat model auditable.
  • No plugin runtime. Extensibility is the compile-time DatabaseAdapter interface. A plugin system inside an app holding DB credentials is a privilege-escalation surface; a PR implementing an interface is a code review.
  • No ORM. The app's own store is hand-written SQL on better-sqlite3 (synchronous, in the main process β€” which removes a class of async-consistency issues in transaction bookkeeping).
  • No auto-updater. That's a network call and a code-execution path. Updates are new installers on GitHub Releases.

The verifiable parts of the security posture

  • Network: no outbound requests except to your configured Oracle DBs. Enforced by CSP default-src 'self' in src/renderer/index.html (blocks fetch/XHR/WebSocket/remote images β€” a tracking pixel pasted into a note can't load) and by bundling Monaco locally instead of a CDN.
  • Credentials: Electron safeStorage (macOS Keychain / Windows DPAPI). Encrypted blob on disk; plaintext never reaches the renderer.
  • Supply chain: two runtime dependencies β€” better-sqlite3 and oracledb. Everything else is build-time and compiled in.
  • Disclosure: SECURITY.md routes reports through GitHub private advisories, and explicitly classifies any third-party network contact as a vulnerability.

And the disclosed gap: builds are unsigned for now β€” right-click β†’ Open on macOS, dismiss SmartScreen on Windows, or build from source.

Performance posture (no invented numbers)

No published benchmarks β€” the story is structural: virtualized grid, rows streamed in 5,000-row batches, a hard 50,000-row display cap so renderer memory is bounded, CSV export that streams all rows to disk regardless of the cap, FTS5 for search. The ~23-scenario Playwright suite exercises all of it against the 250k-row demo dataset β€” and the README's screenshots are generated by that suite, so the docs can't drift from the real UI.

walkthrough

Limitations & roadmap

Oracle-only today; unsigned builds; macOS is Apple-silicon only; no TCPS/wallet yet; cell editing is intentionally narrow (single-table SELECTs, edit refused unless exactly one row matches); no result filtering or snapshot diffing yet.

Roadmap: signing/notarization, TCPS/wallet, Postgres and SQL Server adapters, snapshot diffing, grid filtering, Linux packaging.


If this matches a problem you have: it's free, MIT, and works out of the box on demo data. A star on ⭐ (https://github.com/rajeshkumaravel/queryvault) helps others find it; issues and discussions are open, and PRs are warmly reviewed β€” a new DatabaseAdapter would be a genuinely great first contribution. Tell me where it breaks.


And that’s it!

I hope you found this article useful, and as always, thanks for reading!

Happy Coding!
RK

Top comments (0)