Overview
Instead of baking features like API support, database connectivity, and additional file formats directly into the core, Thoth adopts a plugin architecture that lets users install only what they need. This keeps the core fast and minimal while enabling the ecosystem to grow independently.
This issue tracks the design and implementation of the plugin system foundation. Issues like API support (#14), database support, and additional file formats (#35) are implemented as first-party plugins built on this foundation.
Plugin Types
| Type |
Capability key |
Status |
| File Loader |
file-loader |
✅ Shipped |
| File Viewer |
file-viewer |
✅ Shipped |
| Data Source + UI |
data-source + new-ui-component |
✅ Shipped (URL Source, #14) |
| Exporter |
exporter |
✅ Designed (WIT done) |
| Theme |
theme |
🔨 Phase 5 (see #70) |
| Database Source |
data-source (WASI sockets) |
🔨 Phase 6 |
Proposed Strategy: WebAssembly (WASM) Plugins
WebAssembly via Wasmtime with interfaces defined using WIT.
| Concern |
WASM |
Dynamic Libs |
Subprocess (JSON-RPC) |
| Safety / sandboxing |
✅ Sandboxed |
❌ Full process access |
✅ Isolated |
| Cross-platform |
✅ One binary |
❌ Per-platform |
✅ |
| Language agnostic |
✅ Any WASM target |
❌ Rust only |
✅ |
| Performance |
✅ Near-native |
✅ Native |
❌ IPC overhead |
| ABI stability |
✅ Stable via WIT |
❌ Breaks across Rust versions |
✅ |
| Distribution |
✅ Single .wasm |
❌ Complex |
✅ |
Current WIT Interface (wit/thoth-plugin.wit)
Lifecycle (required by every plugin)
interface plugin-lifecycle {
on-load: func(setting: string); // JSON array of {key,value} settings
on-close: func();
on-setting-change: func(setting: string);
}
UI Component (interactive panel)
interface ui-component {
record ui-event {
widget-id: string,
kind: string, // "click" | "change" | "http-response" | "notify"
value: string,
}
record ui-output { node-json: string, height-hint: u32 }
render-ui: func() -> result<ui-output, plugin-error>;
handle-event: func(event: ui-event) -> result<ui-output, plugin-error>;
render-sidebar: func() -> result<option<ui-output>, plugin-error>;
}
HTTP Client (host-provided import)
interface http-client {
fetch: func(req: http-request) -> result<http-response, plugin-error>; // sync
submit: func(req: http-request) -> string; // async; result via "http-response" event
}
submit error payloads: {"err":{"code":"consent_pending","message":"..."}} when awaiting user domain consent, {"err":{"code":"error","message":"..."}} for failures.
Full interface: wit/thoth-plugin.wit · Full doc: docs/PLUGIN_SYSTEM.md
Implementation Plan
Phase 1 — Core Runtime ✅
Phase 2 — File Loader Plugins ✅ (#35)
Phase 3 — Data Source Plugins ✅ (#14)
Phase 4 — Plugin Registry 🔨
Phase 5 — Theme Plugins 🔨 (#70)
Phase 6 — Database Source Plugins 🔨
Security Model
- WASM sandbox: no filesystem, network, or system API access unless explicitly granted via WASI capabilities
http-client import: all outbound HTTP routed through the host's network-policy layer (allowlist, SSRF guard, rate limiter)
- Domain consent: first request to an unlisted domain shows a consent popup; plugin receives
{"err":{"code":"consent_pending"}} until approved
- Fuel budget: each WIT call is allocated
5,000,000,000 fuel units — runaway plugins cannot stall the UI indefinitely
- Plugin registry: signing + verification required for registry-distributed plugins (Phase 4)
References
Overview
Instead of baking features like API support, database connectivity, and additional file formats directly into the core, Thoth adopts a plugin architecture that lets users install only what they need. This keeps the core fast and minimal while enabling the ecosystem to grow independently.
This issue tracks the design and implementation of the plugin system foundation. Issues like API support (#14), database support, and additional file formats (#35) are implemented as first-party plugins built on this foundation.
Plugin Types
file-loaderfile-viewerdata-source+new-ui-componentexporterthemedata-source(WASI sockets)Proposed Strategy: WebAssembly (WASM) Plugins
WebAssembly via Wasmtime with interfaces defined using WIT.
.wasmCurrent WIT Interface (
wit/thoth-plugin.wit)Lifecycle (required by every plugin)
UI Component (interactive panel)
HTTP Client (host-provided import)
submiterror payloads:{"err":{"code":"consent_pending","message":"..."}}when awaiting user domain consent,{"err":{"code":"error","message":"..."}}for failures.Full interface:
wit/thoth-plugin.wit· Full doc:docs/PLUGIN_SYSTEM.mdImplementation Plan
Phase 1 — Core Runtime ✅
wit/thoth-plugin.witPluginManager— discovery, loading, sandboxingPhase 2 — File Loader Plugins ✅ (#35)
WasmFileLoaderbindingPhase 3 — Data Source Plugins ✅ (#14)
WasmDataSourceLoaderbinding with async HTTPPhase 4 — Plugin Registry 🔨
thoth-pluginsindex (GitHub-hosted JSON to start)Phase 5 — Theme Plugins 🔨 (#70)
themecapability inplugin.tomltheme.jsoncolour token map (no WASM required)theme-providerinterface for dynamic/adaptive themesPhase 6 — Database Source Plugins 🔨
wasm32-wasip2to unlockwasi:sockets/tcppostgrescrate, pure Rust, WASI TCP)limboengine compiled to WASM).wasmthat the user installsSecurity Model
http-clientimport: all outbound HTTP routed through the host's network-policy layer (allowlist, SSRF guard, rate limiter){"err":{"code":"consent_pending"}}until approved5,000,000,000fuel units — runaway plugins cannot stall the UI indefinitelyReferences
docs/PLUGIN_SYSTEM.md