Creating a Plugin
This guide walks through the complete process of building a StudioBrain plugin, from project structure to testing. For a quicker introduction, see Getting Started.
Plugin Structure
Every plugin has the same basic structure:
my-plugin/
plugin.json # Plugin manifest (required)
plugin.wasm # WASM module (for WASM plugins)
panels/ # Frontend panel HTML files
main-panel.html
widgets/ # Field widget HTML files (optional)
my-widget.html
assets/ # Icons, screenshots (optional)
icon.png
README.md # Plugin documentation (optional)For Python plugins (Desktop/Core only), the structure differs:
my-plugin/
plugin.json # Plugin manifest (required)
backend/
routes.py # FastAPI route handlers
events.py # Event handler registrations
panels/
main-panel.htmlplugin.json Schema
The manifest file declares everything about your plugin. Here is the complete schema with all fields:
{
"id": "my-plugin",
"name": "My Plugin",
"version": "1.0.0",
"description": "A short description of what the plugin does",
"author": "Your Name or Organization",
"license": "MIT",
"homepage": "https://github.com/you/my-plugin",
"repository": "https://github.com/you/my-plugin",
"icon": "Puzzle",
"platforms": ["cloud", "desktop", "core"],
"min_studiobrain_version": "2026.4",
"wasm": {
"module": "plugin.wasm"
},
"capabilities": {
"host_functions": [
"entity_read",
"entity_write",
"asset_read",
"asset_write",
"http_request",
"ai_generate",
"ui_notify",
"get_config",
"set_config"
],
"http_domains": [
"api.example.com",
"*.example.org"
],
"frontend": {
"panels": [
{
"id": "main-panel",
"title": "My Panel",
"location": "entity-sidebar",
"url": "/panels/main-panel.html",
"entity_types": ["character", "location"],
"default_collapsed": false
}
],
"tabs": [
{
"id": "analytics",
"title": "Analytics",
"url": "/panels/analytics.html",
"entity_types": ["*"]
}
],
"pages": [
{
"id": "dashboard",
"title": "Plugin Dashboard",
"url": "/panels/dashboard.html",
"nav_section": "tools"
}
],
"field_widgets": [
{
"id": "my-widget",
"label": "My Widget",
"category": "text",
"value_type": "string",
"accepts_options": false
}
]
},
"settings": {
"global": [
{
"key": "api_key",
"label": "API Key",
"type": "password",
"required": true,
"description": "Your external service API key"
}
],
"user": [
{
"key": "show_notifications",
"label": "Show Notifications",
"type": "boolean",
"default": true
}
]
}
}
}Required Fields
| Field | Type | Description |
|---|---|---|
id | string | Unique plugin identifier. Lowercase, hyphens only. Must be globally unique for marketplace submission. |
name | string | Human-readable display name. |
version | string | Semantic version (e.g., 1.0.0). |
description | string | One-line description shown in the marketplace. |
Optional Fields
| Field | Type | Description |
|---|---|---|
author | string | Plugin author name or organization. |
license | string | SPDX license identifier (e.g., MIT, Apache-2.0). |
homepage | string | URL to the plugin’s documentation or website. |
repository | string | URL to the plugin’s source code repository. |
icon | string | Lucide icon name (e.g., Puzzle, Zap, Globe). |
platforms | string[] | Supported platforms: cloud, desktop, core. Defaults to all. |
min_studiobrain_version | string | Minimum StudioBrain version required. |
WASM Configuration
| Field | Type | Description |
|---|---|---|
wasm.module | string | Path to the .wasm binary, relative to the plugin directory. |
Capabilities
The capabilities object declares what the plugin needs. See Permissions Reference for details on each capability.
host_functions
Array of host function names the plugin will call. Undeclared functions return an error at runtime.
http_domains
Array of domain patterns the plugin will contact via http_request. Supports wildcards (*.example.com). On Cloud, only these domains are reachable. On Desktop/Core, the list is informational.
frontend
Declares UI components the plugin provides:
- panels — Sidebar panels, footer panels
- tabs — Entity page tabs
- pages — Standalone pages
- field_widgets — Custom form inputs
settings
Declares configuration options:
- global — Admin-level settings (API keys, service URLs)
- user — Per-user preferences (notification toggles, display modes)
Setting types: text, password, number, boolean, select, textarea.
Declaring Capabilities
Capabilities are the core of the plugin security model. Declare only what your plugin actually needs.
Request the minimum set of capabilities. Users are more likely to install a plugin that asks for entity_read only than one that asks for entity_write + http_request + file_write.
Read-only plugin (auto-granted, no consent dialog)
{
"capabilities": {
"host_functions": ["entity_read", "asset_read"]
}
}Plugin that modifies entities (consent required)
{
"capabilities": {
"host_functions": ["entity_read", "entity_write", "ui_notify"]
}
}Plugin that calls external APIs (consent required, domain-restricted on Cloud)
{
"capabilities": {
"host_functions": ["entity_read", "http_request"],
"http_domains": ["api.openai.com", "api.anthropic.com"]
}
}Plugin that uses AI generation (auto-granted, BrainBits billing applies)
{
"capabilities": {
"host_functions": ["entity_read", "ai_generate", "entity_write"]
}
}Declaring Platform Compatibility
The platforms field controls where your plugin appears in the marketplace:
{
"platforms": ["cloud", "desktop", "core"]
}| Value | Edition | Runtime requirement |
|---|---|---|
cloud | StudioBrain Cloud (SaaS) | Must be WASM. file_read/file_write are blocked. |
desktop | Tauri desktop app | WASM or Python/Lua. All capabilities available with consent. |
core | Self-hosted server | WASM or Python/Lua. All capabilities available with consent. |
If you omit platforms, the plugin defaults to all three. If your plugin uses file_read or file_write, exclude cloud:
{
"platforms": ["desktop", "core"],
"capabilities": {
"host_functions": ["entity_read", "file_read"]
}
}Available Host Functions
WASM plugins interact with StudioBrain through host functions. These are APIs injected by the host runtime into the WASM sandbox. See Host Functions API Reference for complete documentation.
Summary of available functions:
| Function | Purpose | Capability |
|---|---|---|
entity_read | Read a single entity | entity_read |
entity_list | List entities with filters | entity_read |
entity_create | Create a new entity | entity_write |
entity_update | Update an existing entity | entity_write |
entity_delete | Delete an entity | entity_write |
asset_read | Read an entity’s asset file | asset_read |
asset_write | Upload or replace an asset | asset_write |
http_request | Make an outbound HTTP request | http_request |
ai_generate | Call the AI generation service | ai_generate |
ui_notify | Show a toast notification | (always available) |
get_config | Read a plugin setting | (always available) |
set_config | Write a plugin setting | (always available) |
storage_get | Read plugin-scoped data | (always available) |
storage_set | Write plugin-scoped data | (always available) |
storage_delete | Delete plugin-scoped data | (always available) |
storage_list | List keys in plugin storage | (always available) |
log | Write to the plugin log | (always available) |
Frontend Capabilities
Sidebar Panels
The most common extension point. Panels render in a sandboxed iframe in the entity editor sidebar.
{
"frontend": {
"panels": [
{
"id": "my-panel",
"title": "My Panel",
"location": "entity-sidebar",
"url": "/panels/my-panel.html",
"entity_types": ["character", "location"],
"default_collapsed": false
}
]
}
}entity_types— Array of entity types this panel appears on. Use["*"]for all types.default_collapsed— Whether the panel starts collapsed.
Entity Tabs
Full-width content tabs on entity pages.
{
"frontend": {
"tabs": [
{
"id": "analytics",
"title": "Analytics",
"url": "/panels/analytics.html",
"entity_types": ["*"]
}
]
}
}Standalone Pages
Pages accessible from navigation, independent of any entity.
{
"frontend": {
"pages": [
{
"id": "dashboard",
"title": "Plugin Dashboard",
"url": "/panels/dashboard.html",
"nav_section": "tools"
}
]
}
}nav_section— Where the page link appears:tools,settings, orsidebar.
Field Widgets
Custom form inputs that render inline in entity editors. See Building Field Widgets for the complete tutorial.
Building a WASM Plugin
Rust
The recommended language for WASM plugins. Use the StudioBrain Plugin SDK:
# Scaffold the project
npx @biloxistudios/create-plugin my-plugin --template rust
cd my-plugin
# Edit src/lib.rs with your plugin logic
# Build
cargo build --target wasm32-wasi --release
# The output is at target/wasm32-wasi/release/my_plugin.wasm
# Copy or symlink it to plugin.wasm in the project root
cp target/wasm32-wasi/release/my_plugin.wasm plugin.wasmMinimal Rust plugin:
use studiobrain_plugin_sdk::*;
#[export]
fn on_entity_created(entity: Entity) -> Result<(), PluginError> {
let name = entity.field("name").unwrap_or_default();
host::log("info", &format!("Entity created: {}", name));
host::ui_notify("New Entity", &format!("{} was created", name), "info");
Ok(())
}AssemblyScript (TypeScript-like)
For developers more comfortable with TypeScript syntax:
npx @biloxistudios/create-plugin my-plugin --template assemblyscript
cd my-plugin
# Edit assembly/index.ts
# Build
npm run build
# Output: build/plugin.wasmMinimal AssemblyScript plugin:
import { Entity, host } from "@biloxistudios/plugin-sdk-as";
export function on_entity_created(entity: Entity): void {
const name = entity.field("name");
host.log("info", `Entity created: ${name}`);
host.ui_notify("New Entity", `${name} was created`, "info");
}Python (via componentize-py)
Compile Python to WASM for cross-platform distribution:
npx @biloxistudios/create-plugin my-plugin --template python
cd my-plugin
# Edit src/my_plugin.py
# Build
componentize-py -d plugin.wit -w plugin componentize my_plugin -o plugin.wasmPython-to-WASM plugins are distinct from native Python plugins. The compiled WASM binary runs in the sandbox on all platforms, including Cloud. Native Python plugins (in backend/routes.py) run only on Desktop and Core.
Building a Python Plugin (Desktop/Core Only)
Native Python plugins use FastAPI route handlers and event bus registrations. They run as part of the backend process.
my-plugin/
plugin.json
backend/
routes.py
events.py
panels/
main-panel.htmlbackend/routes.py
from fastapi import APIRouter, Depends
from sqlalchemy.orm import Session
from database import get_db
router = APIRouter()
@router.get("/status")
async def get_status(db: Session = Depends(get_db)):
return {"status": "ok", "plugin": "my-plugin"}
@router.post("/process")
async def process_entity(request: dict, db: Session = Depends(get_db)):
entity_type = request.get("entity_type", "character")
# Plugin logic here
return {"processed": True}backend/events.py
from services.event_bus import EventBus, EntityEvent
def register(event_bus: EventBus):
@event_bus.on("entity_created")
async def on_entity_created(event: EntityEvent):
print(f"Entity created: {event.entity_type}/{event.entity_id}")Testing Locally
Install locally
Copy the plugin directory to _Plugins/ in your StudioBrain project:
# Copy plugin to project
cp -r my-plugin/ ~/StudioBrain/MyProject/_Plugins/my-plugin/
# Or symlink during development
ln -s $(pwd)/my-plugin ~/StudioBrain/MyProject/_Plugins/my-pluginTest in development mode
# Web (development server)
cd /path/to/studiobrain-core
npm run dev
# Navigate to Settings > Plugins > Install Local
# Desktop (Tauri dev mode)
cd /path/to/studiobrain-app/packages/desktop
cargo tauri dev
# Plugin loads via wasmtimeVerify capabilities
Open the browser console (F12) and check for capability errors. If the plugin tries to call a host function it did not declare, you will see:
[plugin:my-plugin] Error: Host function 'entity_write' not declared in capabilitiesAdd the missing function to capabilities.host_functions in plugin.json.
Test the consent dialog
Clear the plugin’s capability grants in Settings > Plugins > [Plugin] > Permissions > Reset, then reload the page. The consent dialog should appear with the correct capabilities listed.
Next Steps
- Getting Started — Quick scaffolding and first build
- Plugin Examples — Complete plugin walkthroughs
- Host Functions API — Detailed host function documentation
- Community Submissions — Publish your plugin to the marketplace
- Permissions Reference — Full capability matrix