Skip to content

Plugin system for extensibility #16

@anitnilay20

Description

@anitnilay20

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 ✅

  • Define wit/thoth-plugin.wit
  • Implement PluginManager — discovery, loading, sandboxing
  • Wire into app startup; surface in Settings → Plugins

Phase 2 — File Loader Plugins ✅ (#35)

  • WasmFileLoader binding
  • Bundled CSV/TSV plugin
  • Documentation

Phase 3 — Data Source Plugins ✅ (#14)

  • WasmDataSourceLoader binding with async HTTP
  • Network policy layer (domain allowlist, SSRF guard, rate limiting)
  • URL Source plugin — full REST API explorer
  • cURL export / import, saved requests, consent flow
  • SQLite bundled data source example

Phase 4 — Plugin Registry 🔨

  • Define a thoth-plugins index (GitHub-hosted JSON to start)
  • In-app plugin browser: search, install, update, remove
  • Plugin signing / verification for registry-distributed plugins
  • Capability-based permissions prompt on first load

Phase 5 — Theme Plugins 🔨 (#70)

  • theme capability in plugin.toml
  • Static theme via theme.json colour token map (no WASM required)
  • Optional WASM theme-provider interface for dynamic/adaptive themes
  • Theme selector in Settings → Appearance lists installed plugins
  • Bundled example theme (Gruvbox Dark / Solarized Light)
  • See Theme Plugin — user-installable colour schemes and syntax highlighting #70 for full spec and acceptance criteria

Phase 6 — Database Source Plugins 🔨

  • Migrate to wasm32-wasip2 to unlock wasi:sockets/tcp
  • PostgreSQL plugin (postgres crate, pure Rust, WASI TCP)
  • MySQL / MariaDB plugin
  • SQLite plugin (pure-Rust limbo engine compiled to WASM)
  • Redis plugin (trivial RESP protocol over WASI TCP)
  • Each plugin ships as an independent .wasm that the user installs

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

Metadata

Metadata

Assignees

Labels

architectureCode structure and architecturefeatureNew feature requestspriority:highHigh priority itemssize:largeLarge effort (1+ months)

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions