Building a desktop database client in 2025 means choosing a runtime. Electron dominates the segment: VS Code, Slack, and 1Password prove it. But Electron bundles Chromium and Node.js into every binary, which comes at a cost in memory and size from the very first launch. QoreDB took a different path, that of Tauri 2.
The premise is simple: delegate rendering to the operating system's native WebView rather than bundling one. What follows explains what this choice concretely implies for QoreDB's architecture.
The Native WebView: A Light Binary, an OS Dependency
Tauri does not bundle Chromium. It uses what the OS provides: WKWebView on macOS, WebView2 on Windows, WebKitGTK on Linux. The effect on binary size is immediate: a QoreDB release binary is a few megabytes, where an Electron equivalent easily weighs 100 to 200 MB.
This lightness comes with a trade-off: the rendering engine is not controlled by the application. On Linux, for example, WebKitGTK 2.42+ with certain GPU drivers (notably AMD/Radeon) caused unbounded video memory consumption that could freeze the machine. The fix in main.rs is direct: disable the DMA-BUF renderer through the WEBKIT_DISABLE_DMABUF_RENDERER environment variable before any WebKit initialization. This is the kind of platform adaptation that Electron, with its frozen Chromium, doesn't require.
Tauri IPC: The Contract Between React and Rust
The React frontend and the Rust backend do not share memory. Their only communication channel is Tauri's IPC, exposed on the JavaScript side through invoke from @tauri-apps/api/core. On the Rust side, each entry point is a function annotated with #[tauri::command]. There is no automatic discovery: each command must be listed explicitly in the builder through tauri::generate_handler![].
For QoreDB, this amounts to several dozen commands, organized by domain: connections, queries, vault, export, interceptor, snapshots, licenses, federation. This explicit list is useful: it forms a clearly visible surface contract between the two layers. An unregistered command fails silently on the JS side - an incentive to keep the registry up to date.
Serialization is handled by Serde. Rust types marked #[derive(Serialize, Deserialize)] cross the bridge as JSON automatically. Errors returned as Result<T, E> are received on the TypeScript side as Promise rejections, with the serialized message. The bindings in src/lib/tauri.ts wrap each call with its TypeScript types: end-to-end static checking.
Managing State Shared Between Commands
Tauri exposes a managed state system that lets shared objects be injected into command handlers. In QoreDB, the global state is encapsulated in AppState - a struct that groups together the DriverRegistry, the SessionManager, the VaultLock, the SafetyPolicy, the InterceptorPipeline, and a dozen or so other components. The whole thing is wrapped in Arc<Mutex<AppState>> and registered at startup through .manage().
The lock granularity deserves attention: a global Mutex over the entire state creates potential serialization if several queries arrive in parallel. In practice, the DB drivers handle their own concurrency through connection pools (SQLx for PostgreSQL, MySQL, SQLite; bb8 for SQL Server). The global lock is held only long enough to access the shared components, not during the actual execution of the query.
Plugins and the Update Mechanism
Tauri 2 moved several historically built-in features into optional plugins. QoreDB uses five of them: tauri-plugin-dialog for native dialog boxes, tauri-plugin-fs for filesystem access, tauri-plugin-opener for opening URLs in the system browser, tauri-plugin-updater for automatic updates, and tauri-plugin-process for process management. This modularity reduces the attack surface: only explicitly declared capabilities are available.
The update mechanism deserves special mention. tauri-plugin-updater queries a release endpoint on GitHub, then verifies a minisign signature on the artifact before applying it. No update installs without a cryptographic integrity check. The public key is embedded in the application's configuration, which makes an update forged by a third party impossible without compromising the private key.
Security Through the CSP and the Process Model
Tauri configures a Content Security Policy on the WebView. For QoreDB, no external script can run (script-src 'self'), network connections are limited to known endpoints (local IPC, PostHog for anonymous telemetry), and external images are blocked by default. This model stems from the fact that the React frontend runs in a WebView: without constraints, an XSS injection could trigger IPC calls to sensitive Rust commands.
The other structural point is that the Rust backend does not expose an HTTP server. Communication goes solely through the ipc:// channel internal to Tauri. No open port, no attack surface from other local processes or from the network.
Choosing Tauri 2 for QoreDB means choosing a minimal runtime, consistent with the local-first approach: no data in transit, no cloud service required, a signed and verified binary. The strongly typed IPC, the managed state shared between Rust commands, and the model of discrete plugins provide a solid foundation for a desktop tool. The dependency on the system WebView implies some platform adaptations, but it's an acceptable trade-off to keep the binary light and the architecture simple.
Stay updated on new releases
Subscribe to get product releases, new drivers notifications, and technical tutorials.

