Creating a Plugin
This guide covers structure, plugin.json, and the two authoring paths. For the lock (repos, runtimes, Marketplace), see Architecture. For a first build, see Getting Started.
Where does it run?
| Kind | Desktop | Mobile | Cloud |
|---|---|---|---|
| Official universal | WIT Component (wasmtime Cranelift) | Same Component (wasmtime Pulley) | Official Worker, compiled at our deploy. Panels on all surfaces. |
| Official desktop-only | Component + network:local + environments: ["desktop"] | — | — |
| Community plugin | Component | Same Component | Panel iframe and/or author backend. Never a Worker on BiloxiStudios Cloudflare. |
| Any data kind | Files in the project | Files in the project | Files in the project. No Worker. |
Two homes for code, one home for official data
| You are shipping | Land it in |
|---|---|
| Official or vendor code (Component + official Worker) | studiobrain-plugins |
| Community plugin listing | studiobrain-community (today studiobrain-community-plugins), plugins/index.json |
| Community templates / rules / skills / layouts / packs / providers / abilities / flows / canvas | Same community repo, catalog/index.json — not studiobrain-templates |
| Official data | studiobrain-templates only |
studiobrain-templates is not a plugin home. Do not open plugin PRs there.
Plugin structure
my-plugin/
plugin.json # Manifest (required)
plugin.wasm # WIT Component (desktop / mobile)
src/ # Rust guest (cargo-component)
frontend/ # or panels/ — iframe HTML
panel.html
widgets/ # Field widget HTML (optional)
assets/ # Icons, screenshots (optional)
README.mdOfficial universal plugins also have a Worker crate or package that our cloud deploy compiles. That Worker is not something community authors push into BiloxiStudios Cloudflare.
plugin.json
{
"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": "Apache-2.0",
"homepage": "https://github.com/you/my-plugin",
"repository": "https://github.com/you/my-plugin",
"icon": "Puzzle",
"trust_tier": "community",
"type": "full",
"min_studiobrain_version": "2026.8",
"capabilities": {
"backend": {
"entry": "plugin.wasm"
},
"frontend": {
"panels": [
{
"id": "main-panel",
"title": "My Panel",
"location": "entity-sidebar",
"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
}
],
"user": [
{
"key": "show_notifications",
"label": "Show Notifications",
"type": "boolean",
"default": true
}
]
}
},
"permissions": ["read_entities"],
"http_domains": ["api.example.com"]
}Required fields
| Field | Type | Description |
|---|---|---|
id | string | Unique identifier. Lowercase, hyphens only. |
name | string | Display name. |
version | string | Semver (1.0.0). |
description | string | One-line Marketplace blurb. |
trust_tier | string | community, partner, trusted_vendor, or first_party. |
Optional fields
| Field | Type | Description |
|---|---|---|
author | string | Name or organization. |
license | string | SPDX id (Apache-2.0, MIT). |
homepage / repository | string | Docs and source URLs. |
icon | string | Lucide icon name. |
type | string | full (Component + panels) or panel-only. |
min_studiobrain_version | string | Minimum host version. |
Backend entry
capabilities.backend.entry is the WIT Component path (plugin.wasm). Desktop and mobile load that Component. It is not compiled by Cloudflare from R2.
Official cloud execution is a separate Worker we bundle at deploy. Community listings must not assume a Worker on our account.
Permissions and HTTP
Declare host functions / permissions you actually call. See Permissions.
http_domains lists hosts for mediated http_request. On cloud official Workers, only those domains are reachable. Community cloud backends are the author’s problem and are not our Worker allowlist.
Request the minimum set. Users install a read_entities-only plugin faster than one that asks for writes plus arbitrary HTTP.
Frontend
- panels — sidebar / footer
- tabs — entity page tabs
- pages — standalone navigation
- field_widgets — custom form inputs (Field Widgets)
Settings
- global — admin (API keys, service URLs)
- user — preferences
Types: text, password, number, boolean, select, textarea.
Official vs community
Official universal
Rust Component in studiobrain-plugins plus an official Worker in our cloud deploy. Panels everywhere. Proofs today: entity-notes, entity-snapshots, content-stats.
Official desktop-only
Same Component, plus network:local and environments: ["desktop"] (LAN devices, local daemons). Marketplace hides these on cloud and mobile. Community listings may declare network:local only when environments is exactly ["desktop"].
Community
Component for desktop/mobile. Cloud = panel iframe and/or your backend. Submit to the community plugins index. See Submitting.
Native Python / Lua guests are not Marketplace community plugins and are not a cloud runtime. New work is a WIT Component. Legacy backend/routes.py trees are archive, not a template.
Host functions
WASM guests call host imports. Summary — full list in Host Functions:
| Function | Purpose | Typical permission |
|---|---|---|
entity_read / entity_list | Read entities | read entities |
entity_create / entity_update / entity_delete | Mutate entities | write entities |
asset_read / asset_write | Entity assets | read/write assets |
http_request | Mediated outbound HTTP | http + declared domains |
ai_generate | StudioBrain generation (BrainBits on cloud) | ai generate |
storage_* | Plugin-scoped data | plugin data |
get_config / set_config | Settings | always (scoped) |
log / ui_notify | Diagnostics and toasts | always |
network:local | Desktop LAN / localhost | official desktop-only |
Building the Component
Rust is the official proof language:
# From a first-party proof in studiobrain-plugins, e.g. entity-notes
cargo install cargo-component wasm-tools
cargo component build --releaseCopy the wasm32-wasip1 release Component to plugin.wasm. The host WIT world lives with the plugin SDK; do not invent a parallel world.
WASI-on-Workers is experimental and is not this world. Do not transpile the Component to a Worker yourself and expect us to load it.
Testing locally
# Per-project: copy or symlink into the project plugin dir
cp -r my-plugin/ ~/StudioBrain/MyProject/_Plugins/my-plugin/Enable it for that project. Tenant allowlist still applies on cloud.
- Desktop: wasmtime Cranelift loads the Component.
- Mobile: same Component, wasmtime Pulley.
- Cloud community: open an entity and confirm the panel iframe. Do not expect your
.wasmto become a Worker.
If the guest calls an undeclared permission, the host returns an error. Reset grants under Settings → Plugins → [Plugin] → Permissions to re-test consent.