Plugins
Plugins let you extend VirtualDB without modifying it. A plugin is any standalone executable that connects to VirtualDB over a Unix socket and communicates using JSON-RPC 2.0. It can intercept queries, transform rows, observe events, and emit its own events — all from outside the VirtualDB process.
How plugins work
When vdb-mysql starts, it scans the directory at VDB_PLUGIN_DIR for plugin subdirectories, launches each plugin as a child process, and waits for it to connect and declare what it wants to do. The plugin registers its pipeline handlers and event subscriptions in a single declare call, then stays connected and running for the lifetime of the server.
VirtualDB calls the plugin whenever a pipeline point it registered is reached. The plugin receives the current payload, can modify it, and returns the updated payload. Writes are applied, queries are rewritten — the plugin's response drives what happens next.
Events work differently: they're fire-and-forget notifications delivered after something has already happened. A plugin subscribes to events to observe state changes without blocking the request path.
Directory layout
/etc/vdb/plugins/
my-plugin/
manifest.json
my-plugin ← the plugin executableSet VDB_PLUGIN_DIR=/etc/vdb/plugins and VirtualDB will find and launch my-plugin automatically.
Manifest
Each plugin subdirectory must contain a manifest.json, manifest.yaml, or manifest.yml file.
{
"name": "my-plugin",
"version": "1.0.0",
"command": ["./my-plugin"],
"env": {
"MY_PLUGIN_API_KEY": "..."
}
}| Field | Required | Description |
|---|---|---|
command | Yes | Command and arguments to launch the plugin. The working directory is set to the plugin's subdirectory. |
name | No | Identifier used in logs. Defaults to the subdirectory name. |
version | No | Informational. Printed at startup. |
env | No | Additional environment variables passed to the plugin process. |
Startup handshake
Once launched, the plugin receives the Unix socket path in the VDB_SOCKET environment variable. It must connect to that socket and send a declare notification within 10 seconds. Plugins that miss the deadline are logged and skipped — the server continues without them.
The declare notification tells VirtualDB which pipeline points the plugin handles and which events it wants to receive:
{
"jsonrpc": "2.0",
"method": "declare",
"params": {
"plugin_id": "my-plugin",
"pipeline_handlers": [
{ "point": "vdb.records.source.transform", "priority": 15 }
],
"event_subscriptions": [
"vdb.query.completed"
],
"event_declarations": [
"my-plugin.row.flagged"
],
"pipeline_declarations": []
}
}pipeline_handlers— the pipeline points the plugin handles, each with a numeric priority. Lower numbers run first.event_subscriptions— events delivered to the plugin as notifications (no response expected).event_declarations— events the plugin will emit. Must be declared upfront.pipeline_declarations— custom pipelines the plugin owns, which other plugins can attach to.
Handling pipeline points
For each registered pipeline point, VirtualDB sends a synchronous handle_pipeline_point request. The plugin's response replaces the current payload and the pipeline continues.
{
"jsonrpc": "2.0",
"method": "handle_pipeline_point",
"id": 1,
"params": {
"point": "vdb.records.source.transform",
"payload": { "..." }
}
}The plugin responds with the (optionally modified) payload:
{
"jsonrpc": "2.0",
"id": 1,
"result": { "payload": { "..." } }
}If the plugin returns an error, the pipeline stops and the error is sent to the client as a SQL error.
Receiving events
Events arrive as handle_event notifications — no response is expected.
{
"jsonrpc": "2.0",
"method": "handle_event",
"params": {
"event": "vdb.query.completed",
"payload": { "..." }
}
}Emitting events
A plugin can emit any event it declared in its declare notification using emit_event:
{
"jsonrpc": "2.0",
"method": "emit_event",
"id": 1,
"params": {
"event": "my-plugin.row.flagged",
"payload": { "..." }
}
}VirtualDB acknowledges with an empty result and forwards the event to all subscribers.
Shutdown
When vdb-mysql shuts down, it sends a shutdown request to each plugin. The plugin must send a JSON-RPC response to acknowledge it, then clean up and exit. VirtualDB waits up to 10 seconds for the acknowledgement — if no response arrives, the process is killed. After the response, VirtualDB waits a further 10 seconds for the process to exit before sending SIGKILL.
{
"jsonrpc": "2.0",
"method": "shutdown",
"id": 42,
"params": {}
}Plugin priorities and the delta overlay
When registering a handler at vdb.records.source.transform, the priority you choose matters:
- Priority < 10 — your plugin sees raw rows from the source, before the delta overlay is applied.
- Priority > 10 — your plugin sees post-overlay rows, which already reflect any writes the application has made.
The delta overlay itself is the built-in handler at priority 10.
Error handling
- If a plugin exits unexpectedly, VirtualDB logs it and continues running without that plugin. There is no automatic restart.
- Event handler errors are logged but do not affect other subscribers or the request path.
Further reading
- Pipelines & Events Reference — all pipeline points and events
- How It Works — the delta model and query lifecycle