Plugin Development: Getting Started

StudioBrain plugins extend the app with sandboxed backend logic and HTML panels. Official and community code is a WIT Component (Rust → cargo component) that desktop and mobile run in wasmtime. Official cloud plugins are a Worker per plugin, compiled at Biloxi deploy. Community plugins never run as Workers on BiloxiStudios Cloudflare.

Read the lock first: Plugin Architecture.

Where does it run?

KindDesktopMobileCloud
Official universalComponent (wasmtime Cranelift)Same Component (wasmtime Pulley, SBAI-7745)Official Worker at our deploy. Panels everywhere.
Official desktop-onlyComponent + network:local + environments: ["desktop"]
Community pluginComponentSame ComponentPanel iframe and/or the author’s backend
Any data kindFiles in the projectFiles in the projectFiles in the project. No Worker.

Why a Component (and not “one WASM for the web”)

  • Desktop loads the Component with wasmtime Cranelift.
  • Mobile loads the same Component with wasmtime Pulley (SBAI-7745). Not wasmi.
  • Cloud cannot WebAssembly.compile fetched R2 bytes. Official plugins that need a guest on cloud ship as a Worker we compile at deploy. Experimental WASI-on-Workers is not the StudioBrain WIT world.
  • Panels (static HTML/JS) run on every surface in a sandboxed iframe.

What you can build

  • Frontend panels — entity sidebar, tab, or standalone page
  • Host hooks — validate, react to create/update/delete
  • Plugin-scoped storage — via the Plugin Data API
  • Settings — global (admin) and per-user
  • Official cloud routes — only on an official Worker we deploy
  • Community cloud server — the author’s own backend, not ours

Official proofs

Start from the Rust proofs in studiobrain-plugins:

PluginWhy copy it
entity-notesPanel + plugin-data + a host-entities hook
entity-snapshotsSave-hook snapshots + history panel
content-statsRead-only stats from the Component

Follow-on samples (Track H tickets, not proofs): hello-world, webhook-automations, pdf-exporter, comparison, kanban-board, google-sheets-sync, assembly-composer, blender-bridge.

prompt-engine is not a plugin. Core owns it.

Quick start (community or local)

1. Copy a proof, do not invent a new home

Official/vendor code lives in studiobrain-plugins. Community listings go to studiobrain-community (today studiobrain-community-plugins) on the plugins index. Official data (templates, rules, skills, layouts) stays in studiobrain-templates — that repo is not a plugin home.

my-plugin/
  plugin.json           # Manifest (required)
  src/lib.rs            # WIT Component guest (Rust)
  Cargo.toml            # cargo-component, StudioBrain plugin world
  frontend/
    panel.html          # Sandboxed iframe panel
  README.md

2. Manifest

{
  "id": "my-plugin",
  "name": "My Plugin",
  "version": "1.0.0",
  "description": "A custom StudioBrain plugin",
  "author": "Your Name",
  "license": "Apache-2.0",
  "trust_tier": "community",
  "type": "full",
  "capabilities": {
    "backend": {
      "entry": "plugin.wasm"
    },
    "frontend": {
      "panels": [
        {
          "id": "main-panel",
          "title": "My Panel",
          "location": "entity-sidebar"
        }
      ]
    }
  },
  "permissions": ["read_entities"]
}

Declare only the host capabilities you need. network:local is official desktop-only and is rejected for community plugins.

3. Implement the Component

// src/lib.rs — cargo-component guest against the StudioBrain plugin WIT world
// See first-party/entity-notes in studiobrain-plugins for a complete hook.
 
fn on_entity_validate(/* entity from host */) -> Result<(), String> {
    // Call host-entities / plugin storage through WIT imports.
    Ok(())
}

Build:

cargo install cargo-component wasm-tools
cargo component build --release
# copy the wasm32-wasip1 release Component to plugin.wasm

Do not target a raw wasm32-wasi core module and expect the host to load it. The host wants a Component for the StudioBrain WIT world.

⚠️

Python componentize-py and Javy one-offs are not the official proof path. Proofs are Rust Components. Community plugins that compile to the same WIT world can use other languages; they still do not become Workers on our Cloudflare account.

4. Install (per-project)

Marketplace is the only store UI: Settings → Plugins → Marketplace.

  • Pick a project. Install is per-project, not tenant-wide.
  • Tenant policy is an allowlist. If the plugin ID is not allowed, Marketplace will not install it.
  • Desktop / mobile: Marketplace fetches the Component + panels, verifies the listing, writes them into the project plugin dir, wasmtime loads the Component.
  • Cloud (community): panels load in an iframe. Backend logic, if any, is the author’s server. We do not compile or load your WASM as a Worker.
  • Cloud (official): we already deployed that plugin’s Worker; enable it for the project.

For local iteration, copy the plugin directory into the project’s plugin folder and enable it for that project. Git-clone install and zip upload are development conveniences, not a second store.

5. Enable

After install, enable the plugin on that project (Settings → Plugins, or the project plugin API). Capability consent still applies for community plugins.

Host functions

The Component talks to StudioBrain only through WIT imports the host implements. Typical groups: entities, plugin storage, settings, log/notify, mediated HTTP, AI generate. See Host Functions and Permissions.

Undeclared functions fail at runtime. http_request and write capabilities require consent. network:local never appears on cloud.

Frontend panels

Panels are HTML/CSS/JS in sandboxed iframes. They use the postMessage protocol (entity-context, entity-updated, theme-change, entity-modified, toast, resize). See Plugin Iframe Protocol.

Use semantic surface CSS variables. Hardcoded colors break user themes — Plugin Development.

Next steps