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

RepoWhat it isWhat it is not
studiobrain-templatesOfficial data only: templates, rules, skills, layouts, packsNot a plugin home. No WIT Components. No Workers. Community data does not land here.
studiobrain-pluginsOfficial and vendor code: WIT Component guests plus, for cloud, an official Worker compiled at our deployNot the community registry. Not catalog YAML/Markdown.
studiobrain-community (today: studiobrain-community-plugins)One public registry with two indexes: plugins vs catalog dataNot 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?

KindDesktopMobileCloud
Official universalWIT Component in wasmtime CraneliftSame Component in wasmtime Pulley (SBAI-7745)Official Worker per plugin, compiled at Biloxi deploy. Panels on every surface.
Official desktop-onlyWIT Component plus network:local and environments: ["desktop"]Not installedNot installed
Community pluginWIT Component (Cranelift)Same Component (Pulley)Panel iframe and/or the author’s own backend. Never a Worker on BiloxiStudios Cloudflare.
Any data kindFiles in the projectFiles in the projectFiles 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:

ArtifactBuilt fromUsed by
WIT Component (plugin.wasm, StudioBrain plugin world)cargo component against the host WITDesktop wasmtime Cranelift; mobile wasmtime Pulley. Same Component bytes.
Official WorkerOur cloud deploy pipeline for that pluginCloud only. Bundled at deploy.
PanelStatic HTML/JSAll 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):

PluginWhat it proves
entity-notesSidebar panel + plugin-data persistence + a real host-entities hook
entity-snapshotsSave-hook archive into plugin storage + history panel
content-statsRead-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

  1. Code PRs land in studiobrain-plugins (Component + panels; cloud Worker wired in our deploy).
  2. Data PRs land in studiobrain-templates (templates, rules, skills, layouts, packs only).
  3. Marketplace lists official rows from those two homes.
  4. Install is per-project. Tenant policy may allowlist which official plugins a tenant may enable.

Community

  1. Authors publish a Component (and panels) from their own repo.
  2. They open a PR against studiobrain-community (currently studiobrain-community-plugins) on plugins/index.json or catalog/index.json — never into studiobrain-templates. Submit-plugin and submit-template/pack/provider/flow are different CONTRIBUTING sections.
  3. Marketplace reads those indexes. CatalogSync does not already merge them into the official catalog.
  4. 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 keySource
Official plugin index + signed WASMstudiobrain-plugins CI
Official catalog-index + filesstudiobrain-templates CI (SBAI-7611)
community/plugins/_index.jsonstudiobrain-community plugins/index.json
community/catalog/_index.jsonstudiobrain-community catalog/index.json

What the 2026-08-14 design got wrong

ClaimStatus
Plugins live in studiobrain-templatesWrong. Templates is official data only.
Reject studiobrain-pluginsWrong. That repo is official/vendor code.
Mobile = wasmiWrong. Mobile = wasmtime Pulley, same Component (SBAI-7745).
Dynamic Worker Loader is the cloud pathWrong. Official = Worker per plugin at our deploy. Community = no Worker on our CF.
One WASM Component runs on CloudflareWrong. Fetched Component bytes cannot be compiled in Workers.
WASI-on-Workers ≈ StudioBrain WITWrong. Experimental WASI is not our world.
CatalogSync already merges communityWrong. 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