Plugin and Catalog Architecture
Owner lock (2026-08-22, SBAI-7665) is the source of truth. It replaces the 2026-08-14 note that put plugins in studiobrain-templates, rejected studiobrain-plugins, named wasmi as the mobile runtime, and treated Dynamic Worker Loader as the cloud path.
Marketplace is the only store UI. Install is per-project. Tenant policy is an allowlist.
Repo homes
| Repo | What it is | What it is not |
|---|---|---|
| studiobrain-templates | Official data only: templates, rules, skills, layouts, packs | Not a plugin home. No WIT Components. No Workers. Community data does not land here. |
| studiobrain-plugins | Official and vendor code: WIT Component guests plus, for cloud, an official Worker compiled at our deploy | Not the community registry. Not catalog YAML/Markdown. |
| studiobrain-community (today: studiobrain-community-plugins) | One public registry with two indexes: plugins vs catalog data | Not official code. Not a BiloxiStudios Cloudflare Worker farm. |
CatalogSync pulls official catalog data from studiobrain-templates. It does not already merge the community indexes. Community browse/install is a Marketplace concern, not a silent catalog merge.
Where does it run?
| Kind | Desktop | Mobile | Cloud |
|---|---|---|---|
| Official universal | WIT Component in wasmtime Cranelift | Same Component in wasmtime Pulley (SBAI-7745) | Official Worker per plugin, compiled at Biloxi deploy. Panels on every surface. |
| Official desktop-only | WIT Component plus network:local and environments: ["desktop"] | Not installed | Not installed |
| Community plugin | WIT Component (Cranelift) | Same Component (Pulley) | Panel iframe and/or the author’s own backend. Never a Worker on BiloxiStudios Cloudflare. |
| Any data kind | Files in the project | Files in the project | Files in the project. No Worker. |
Panels (static HTML/JS in a sandboxed iframe) are the one UI artifact that runs on every surface.
Why cloud is not “fetch WASM and compile”
Cloudflare Workers and Durable Objects cannot instantiate fetched WASM at runtime. WebAssembly.compile, WebAssembly.compileStreaming, and WebAssembly.instantiate(buffer) are banned in the isolate, same class as eval(). WASM that we run on BiloxiStudios Cloudflare must enter as a statically bundled Worker module at deploy time.
That is why official cloud plugins are one Worker per plugin, compiled at our deploy. Hosts must not WebAssembly.compile bytes fetched from R2 (or anywhere else) inside a Worker or Durable Object.
WASI-on-Workers is experimental and is not the StudioBrain WIT world. A WASI core-wasm guest that happens to load in workerd is not a StudioBrain plugin.
Retracted (2026-08-14). Dynamic Worker Loader (env.LOADER.get(...) fetching bundle.js or wasm bytes from R2) is not the StudioBrain cloud plugin path. Community code is not loaded into Biloxi isolates at all.
Guest format
One source language for first-party proofs: Rust. Two compile targets, not one binary for every surface:
| Artifact | Built from | Used by |
|---|---|---|
WIT Component (plugin.wasm, StudioBrain plugin world) | cargo component against the host WIT | Desktop wasmtime Cranelift; mobile wasmtime Pulley. Same Component bytes. |
| Official Worker | Our cloud deploy pipeline for that plugin | Cloud only. Bundled at deploy. |
| Panel | Static HTML/JS | All surfaces, sandboxed iframe |
Community authors ship a Component (and panels). They do not get a Worker on our account. If a community plugin needs a server on cloud, the author hosts it.
Mobile does not use wasmi. SBAI-7745 is wasmtime Pulley for the same Component desktop already runs.
Official proofs and follow-on samples
Official proof plugins now (Rust, in studiobrain-plugins):
| Plugin | What it proves |
|---|---|
| entity-notes | Sidebar panel + plugin-data persistence + a real host-entities hook |
| entity-snapshots | Save-hook archive into plugin storage + history panel |
| content-stats | Read-only project stats from the Component guest |
Follow-on samples (Track H tickets, not current 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 prompt construction. Do not list it in studiobrain-plugins or the community plugins index.
Legacy Python/HTML trees under studiobrain-templates/plugins/ are not a distribution home. They are leftover data and should be marked legacy or removed once the ports above are the source of truth.
Publish, browse, install
Marketplace is the store
Users browse and install from Settings → Plugins → Marketplace (and the matching catalog tabs for data). There is no second store UI.
Official
- Code PRs land in
studiobrain-plugins(Component + panels; cloud Worker wired in our deploy). - Data PRs land in
studiobrain-templates(templates, rules, skills, layouts, packs only). - Marketplace lists official rows from those two homes.
- Install is per-project. Tenant policy may allowlist which official plugins a tenant may enable.
Community
- Authors publish a Component (and panels) from their own repo.
- They open a PR against studiobrain-community (currently
studiobrain-community-plugins) onplugins/index.jsonorcatalog/index.json— never intostudiobrain-templates. Submit-plugin and submit-template/pack/provider/flow are different CONTRIBUTING sections. - Marketplace reads those indexes.
CatalogSyncdoes not already merge them into the official catalog. - Desktop/mobile load the Component in wasmtime. Cloud shows panels and may call an author-hosted backend. Nothing community-authored is compiled or loaded as a Worker on BiloxiStudios Cloudflare.
Install state
- Per-project. Enabling a plugin on Project A does not enable it on Project B.
- Tenant allowlist. A tenant admin (or policy) can restrict which plugin IDs may be installed. Marketplace cards that are not allowlisted do not install.
- Desktop/mobile write the Component + panels into the project’s plugin dir and load via wasmtime.
- Cloud records install + config on the project and serves panels. Official plugins additionally invoke that plugin’s deployed Worker.
Host contract (unchanged shape)
Plugins have no ambient authority. Entity I/O, plugin-scoped storage, settings, mediated HTTP, and AI go through host functions declared in the manifest. See Permissions and Host Functions.
network:local requires environments: ["desktop"]. Official desktop-only plugins use that pair. Community CI rejects network:local unless the listing is desktop-only. Cloud never grants it.
R2 live indexes
Clients read R2, not GitHub, at request time. Publish is on merge and hourly.
| R2 key | Source |
|---|---|
| Official plugin index + signed WASM | studiobrain-plugins CI |
| Official catalog-index + files | studiobrain-templates CI (SBAI-7611) |
community/plugins/_index.json | studiobrain-community plugins/index.json |
community/catalog/_index.json | studiobrain-community catalog/index.json |
What the 2026-08-14 design got wrong
| Claim | Status |
|---|---|
Plugins live in studiobrain-templates | Wrong. Templates is official data only. |
Reject studiobrain-plugins | Wrong. That repo is official/vendor code. |
| Mobile = wasmi | Wrong. Mobile = wasmtime Pulley, same Component (SBAI-7745). |
| Dynamic Worker Loader is the cloud path | Wrong. Official = Worker per plugin at our deploy. Community = no Worker on our CF. |
| One WASM Component runs on Cloudflare | Wrong. Fetched Component bytes cannot be compiled in Workers. |
| WASI-on-Workers ≈ StudioBrain WIT | Wrong. Experimental WASI is not our world. |
CatalogSync already merges community | Wrong. It does not. |
Next steps for authors
- Getting Started — scaffold a Component and a panel
- Creating Plugins — manifest, capabilities, official vs community
- Marketplace — browse and per-project install
- Community submissions — two indexes on the community registry
- Examples — official proofs and follow-on tickets
- Repo boundaries:
plans/SBAI-2015-repo-boundaries-canonical.md— templates vs plugins vs community