A database client is a tool that's worth a lot to the person sitting in front of it, and that person's workflow is almost never the workflow the people who wrote the client had in mind. The whole point of a plugin system is to close that gap without baking every team's preference into the core app.
Today QoreDB ships its plugin system. It does two things. It lets you contribute static data — SQL snippets, colour themes, connection templates, mappings that pick a built-in renderer for a column — without writing a line of code. And it lets you ship sandboxed WebAssembly modules that hook into the query lifecycle: a function that runs before a query and can allow, warn or block it; a function that runs after a query and can observe the outcome; user-invocable commands that fire a handler in your module.
The two halves work together. The same plugin can carry a snippet pack and a runtime that intercepts dangerous DDL. The same manifest declares both.
Two flavours of plugin
A plugin is a folder containing a plugin.json manifest. The host parses the manifest at install time and surfaces the plugin's contributions to the rest of the app.
Declarative plugins carry only data. No runtime block, no code. They contribute snippets, connection templates, themes, result-viewer mappings. The host validates the shapes, namespaces every contributed id by plugin id (so two plugins can both ship a snippet called hello), and that's it.
Executable plugins ship a sandboxed WebAssembly module on top of that. The manifest's runtime block names the WASM file, the ABI version (currently 1), the hooks the module subscribes to, and the capabilities the host should expose to it. Every executable plugin can also contribute declarative data — most do.
The host's hook surface is three exports:
pre_executeruns before a query and returnsallow,warnorblock. Ablockstops the query before it reaches the database; awarnlets it run and surfaces a toast.post_executeruns after a query — successful or failed — and observes the outcome.commandruns when the user clicks a contributed command in the UI.
That's a small surface on purpose. The plugin runtime is sandboxed under wasmi, with fuel-bounded execution (about 50 million instructions per invocation), a 16 MiB linear-memory cap, and wall-clock timeouts (500 ms for pre-execute, 5 s for post-execute). A trapping or runaway plugin can't take the app down with it: three consecutive failures unload the plugin for the rest of the session and pop a warning.
Capabilities are opt-in, declared and granted
A WASM module on its own can compute but cannot reach anything outside the sandbox. Everything that touches the host — logging, notifications, key-value storage, query results, HTTP, the filesystem, secrets — goes through a host function gated by a capability.
There are seven capabilities. Every one of them defaults to off. The plugin declares the capabilities it needs in its manifest; the user reviews them in a consent dialog at install time; the host filters every host-function call against the granted set at the first instruction of the call. A revoked capability becomes ERR_DENIED on the next call — no reload required.
The HTTP capability is worth a closer look. A plugin that requests http must declare an allowedHosts list — a wildcard is rejected at install time. By default the host refuses requests that resolve to private, loopback, link-local, or cloud-metadata addresses, post-DNS. So an attacker who tricks DNS into pointing api.example.com at 169.254.169.254 cannot use the plugin as an SSRF jump host into the user's internal network. A plugin can opt into reaching private networks — and that opt-in is flagged loudly in the consent UI.
Secrets follow the same pattern. The manifest lists the names of secrets the plugin will read; the user provisions the values through the plugin's detail dialog, where they land in the OS keyring. A tampered consent file can never widen what the plugin asked for.
This isn't a novel model. VS Code, browsers, modern desktop OSes all do something close. It's the discipline that matters: declared in metadata, granted by the user, enforced at the bridge.
Integrity
Executable plugins should ship a runtime.integrity digest — a sha256-<64 hex> hash of the WebAssembly bytes. The host computes the actual hash of the loaded module before instantiation and refuses to run anything that doesn't match.
Plugins without an integrity digest are still allowed, but the QoreDB plugins panel flags them as Unsigned. The qoredb-plugin build CLI computes the hash for you and writes it back into the manifest, so signing your own plugins is the easy path.
A marketplace, and a registry behind it
A plugin system without a place to discover plugins isn't much of a system. As of today, QoreDB has a public marketplace at qoredb.com/marketplace, and inside the app under Settings → Marketplace.
The marketplace is built on top of a public GitHub repo, qoredb-plugins-registry, which holds the canonical bytes for every approved plugin version: a plugin.json, a flat plugin.zip (manifest + WASM module at the root), and a sha256 of the archive. A index.json at the top of the repo summarises every entry. The marketplace website reads the index; the QoreDB app downloads the archive directly from the registry's raw URLs and verifies the sha256 end-to-end before parsing the manifest. The trust anchor is the catalog, not the transport.
Anyone can submit a plugin through the submission form. It cross-checks the form against the manifest (plugin id, version, runtime presence vs declared kind) before accepting; submissions that would fail to install in QoreDB never enter the review queue. A maintainer reviews each submission by hand — manifest validation, capability appropriateness, allowedHosts tightness, integrity match, an actual local install against a test database — and emails the submitter with the result. Expect a few days of turnaround.
Three plugins to start
The marketplace launches with three small plugins authored by the QoreDB team. They double as worked examples for the topics covered in the plugin docs.
Danger Guard is a preExecute hook that blocks destructive DDL — DROP, TRUNCATE — and warns on UPDATE/DELETE without a WHERE clause. It asks for log only. Roughly 90 KB of WebAssembly.
Query Stats is a postExecute hook that counts queries per driver and per operation type. It contributes two commands, show-stats and reset-stats, that surface and reset the counters. It asks for log and storage.
Slow Query Toast is a postExecute hook that pops a toast when a query takes longer than a configurable threshold. It contributes two commands, show-threshold and set-threshold, to inspect and adjust the threshold. It asks for notify and storage.
All three are signed: their runtime.integrity matches the bytes in the registry, and QoreDB refuses to load anything else.
What's next
This is v1 of the plugin system. A few things are deliberately not in it yet, and they're tracked in the QoreDB roadmap:
- Auto-updates. The host doesn't auto-update plugins. When a new version lands in the registry, QoreDB surfaces it in the Marketplace tab as an opt-in upgrade.
- Paid plugins. The marketplace is free for now. The QoreDB app's licensing system applies to its own Premium modules, not to third-party plugins.
- Revocations. A plugin removed from the registry stays installed on existing machines. We can announce a deprecation; the user remains in control of what runs on their machine.
If you want to write a plugin, the plugins-dev/ folder in the QoreDB repo has the SDK, the CLI, a worked example (sql-linter), and an ABI reference. The plugin docs on the website cover the same material in tutorial form.
If you'd rather just use what's there, install QoreDB and head to Settings → Marketplace. Three plugins won't be enough for everyone, but they're a credible start — and the part of the system that's hardest to get right (the sandbox, the consent model, the integrity check) is the part you don't need to rebuild.
Mantente al día de las novedades
Suscríbete para recibir los lanzamientos, los nuevos controladores y nuestros tutoriales técnicos.

