Overview
Host service:services/pluginmgr/pluginmgr.goPlugin SDK:
pkg/plugin (ServeCLI helper + protobuf aliases)Proto contract:
contracts/plugin/v1/plugin.proto → generated rpc/contracts/plugin/v1 (pluginpb)
Plugins run as isolated subprocesses with no persistent state. This keeps the system simple and provides strong fault isolation.
CLI Commands
Each plugin implements a command-line interface with these commands:| Command | Stdin | Stdout | Timeout | Required |
|---|---|---|---|---|
info | — | {name, version, description, type, ...} | 2s | ✓ |
exec | {connection, query, options?} | {result, error} | 30s | ✓ |
authforms | — | Auth form definitions | 30s | ✓ |
connection-tree | {connection} | {nodes: [...]} | 30s | optional |
test-connection | {connection} | {ok: bool, message: string} | 15s | optional |
The info Command
Returns plugin metadata used for discovery and UI rendering:
Hosts ignore unknown fields for forward compatibility. Older plugins emitting a numeric
type are also accepted.The exec Command
Executes queries and returns structured results. The response contains exactly one result type:
| Field | Type | Use |
|---|---|---|
sql | SqlResult{columns, rows} | Query results with column names |
document | DocumentResult{documents} | JSON document store results |
kv | KvResult{entries} | Key-value results (also used as raw-text wrapper) |
Plugins that return raw strings are automatically wrapped in
kv format by the host.Example exec request:
Example exec response:
The authforms Command
Returns structured form definitions for connection authentication:
Plugins that don’t implement
authforms fall back to a single DSN/credential text input.ConnectionService.CreateConnection.
The connection-tree Command
Returns hierarchical database structure for browsing:
PluginManager.ExecTreeAction(name, conn, actionQuery, options), which delegates to ExecPlugin.
The test-connection Command
Validates connection parameters without persistence:
Test connection uses a 15s timeout (shorter than the standard 30s) to provide faster feedback during connection setup.
Capabilities
Explain-Query Capability
Plugins can advertise"explain-query" in their capabilities array. When present:
- The host renders an Explain button in the result workspace
- Clicking it reruns the current query with
options: {"explain-query": "yes"} - The plugin interprets the flag (e.g., prepending
EXPLAINto SQL) - Results are rendered in a separate Explain tab
The plugin is responsible for implementing the explain logic. The host only passes the option flag.
Plugin Discovery
QueryBox discovers plugins from two locations:Primary: User Directory
User-writable config directory for custom plugins:- Linux:
$XDG_CONFIG_HOME/querybox/plugins - Windows:
%APPDATA%\querybox\plugins - macOS:
~/Library/Application Support/querybox/plugins
bin/plugins to this directory, overwriting existing files. This keeps bundled drivers up-to-date while allowing custom plugins.
Fallback: Bundle Directory
Traditionalbin/plugins next to the executable:
- Inside
.appbundles - Installers
wails3 devworking directory
Discovery Process
PluginManager.Rescan() triggers an immediate synchronous re-probe if a manual refresh is needed without a full restart. This is exposed as a button in the Plugins window.Reference Plugins
QueryBox ships with these reference implementations:| Plugin | Commands | Capabilities | Notes |
|---|---|---|---|
mysql | exec, authforms, connection-tree, test-connection | explain-query | TLS support |
postgresql | exec, authforms, connection-tree, test-connection | explain-query | |
sqlite | exec, authforms, connection-tree, test-connection | explain-query | Two auth forms: local file (modernc.org/sqlite) + Turso Cloud (go-libsql) |
redis | exec, authforms | — | Two auth forms: basic (host/port/password/db) + URL string |
arangodb | exec, authforms | — | Multi-model (documents, graphs); basic auth form |
Writing a Plugin
Quick Start
- Create
plugins/<name>/main.go(packagemain) - Import
pkg/pluginand callplugin.ServeCLI()inmain() - Implement handler functions for each command
- Build:
task build:plugins→ binary lands inbin/plugins/<name>(.exeon Windows) - Drop binary into
bin/plugins/— the host discovers it automatically
Minimal Example
See
plugins/template/main.go in the repository for a complete example with all optional fields.Design Benefits
Language Agnostic
Any language can implement a plugin:- Go (reference implementations)
- Python, Node.js, Rust, etc.
- Just read JSON from stdin, write JSON to stdout
Crash Isolation
Plugin failures are isolated:- Host never crashes due to plugin bugs
- Failed queries return error responses
- Next execution spawns a fresh process
Simple Development Workflow
- Edit plugin code
- Rebuild binary
- Restart QueryBox (or click Rescan)
- Test immediately
No IPC Complexity
Stdin/stdout is the universal interface:- No sockets or named pipes
- No serialization library dependencies
- Standard JSON everywhere