bzl

self-hosted ephemeral community engine
Log | Files | Refs | README | LICENSE

PLUGINS.md (7000B)


      1 # Bzl Plugins (MVP)
      2 
      3 This is the **minimal plugin system** used to ship optional features without forking the core app.
      4 
      5 Plugins are **trusted code**: moderators/owners install them, and they can run both client-side and server-side logic.
      6 
      7 ## Included plugins at a glance
      8 
      9 Use this quick reference when packaging releases or deciding what to enable by default.
     10 
     11 | Plugin | What it adds | Where to install | Notes |
     12 |---|---|---|---|
     13 | `maps` | Spatial map rooms, local/global map chat, movement, collisions, TTRPG-style props/sprites | Any instance | Good for social hub worlds and events. |
     14 | `library` | Shelf/Reader workflow, text + PDF style content management, community borrowing/return flow | Any instance | Best used as persistent knowledge + story archive. |
     15 | `radio` | Community radio stations with uploaded tracks and shared listening | Any instance | Requires upload storage; tune in from panel UI. |
     16 | `dice` | Shared dice roller (`XdY+Z`) broadcast to connected users | Any instance | Lightweight RP/TTRPG utility. |
     17 | `godot` | Template plugin for hosting a bundled Godot HTML5 export from `godotapp/` | Any instance | Put your export files in `plugins_dev/godot/godotapp/` before build. |
     18 | `directory-server` | Public directory endpoint + moderation queue for listings (`/api/plugins/directory-server/list`) | **Directory host instance only** | Acts as the central directory service. |
     19 | `directory-publisher` | Sends this instance announcement payloads to a directory server | Any instance that wants listing | Configure directory URL + instance URL/name, then publish. |
     20 
     21 ## Install / manage (Moderator/Owner UI)
     22 
     23 1. Sign in as `owner` or `moderator`.
     24 2. Open the **Instance** panel in the left sidebar.
     25 3. Under **Plugins**:
     26    - Upload a plugin `.zip` (must contain a `plugin.json` manifest).
     27    - Toggle **Enabled** to activate it.
     28    - Use **Uninstall** to remove it from disk.
     29 
     30 Notes:
     31 - Enabling/disabling a plugin may require a refresh for all connected clients (depends on what the plugin does).
     32 
     33 ### Dev: build the included example plugin zips
     34 This repo includes starter plugins and zip builders:
     35 - Maps: `plugins_dev/maps/`
     36   - Build: `node scripts/build-maps-plugin.js`
     37   - Upload: `dist/plugins/maps.zip`
     38 - Library: `plugins_dev/library/`
     39   - Build: `node scripts/build-library-plugin.js`
     40   - Upload: `dist/plugins/library.zip`
     41 - Radio: `plugins_dev/radio/`
     42   - Build: `node scripts/build-radio-plugin.js`
     43   - Upload: `dist/plugins/radio.zip`
     44 - Dice: `plugins_dev/dice/`
     45   - Build: `node scripts/build-dice-plugin.js`
     46   - Upload: `dist/plugins/dice.zip`
     47 - Godot: `plugins_dev/godot/`
     48   - Build: `node scripts/build-godot-plugin.js`
     49   - Upload: `dist/plugins/godot.zip`
     50   - Bundle app: place your Godot HTML5 export in `plugins_dev/godot/godotapp/` (entry file must be `index.html`)
     51 - Directory Server (draft): `plugins_dev/directory-server/`
     52   - Build: `node scripts/build-directory-server-plugin.js`
     53   - Upload: `dist/plugins/directory-server.zip`
     54 - Directory Publisher (draft): `plugins_dev/directory-publisher/`
     55   - Build: `node scripts/build-directory-publisher-plugin.js`
     56   - Upload: `dist/plugins/directory-publisher.zip`
     57 
     58 ## Where plugins live on disk
     59 
     60 - Installed plugins are stored at: `data/plugins/<pluginId>/`
     61 - Each plugin folder must contain: `plugin.json`
     62 
     63 ## `plugin.json` manifest
     64 
     65 Required:
     66 - `id`: `^[a-z0-9][a-z0-9_.-]{0,31}$`
     67 - `name`
     68 - `version`
     69 
     70 Optional:
     71 - `description`
     72 - `entryClient`: path to a client JS file within the plugin folder
     73 - `entryServer`: path to a server JS file within the plugin folder
     74 - `permissions`: string labels shown in the UI (informational)
     75 
     76 Example:
     77 ```json
     78 {
     79   "id": "polls",
     80   "name": "Polls",
     81   "version": "0.1.0",
     82   "description": "Adds simple polls to hives.",
     83   "entryClient": "client.js",
     84   "entryServer": "server.js",
     85   "permissions": ["ui", "ws"]
     86 }
     87 ```
     88 
     89 ## Zip format
     90 
     91 The `.zip` you upload must include `plugin.json` either:
     92 - at the zip root, or
     93 - inside a single top-level folder
     94 
     95 After extraction, the server installs it into `data/plugins/<id>/`.
     96 
     97 ## Client plugin API
     98 
     99 If your plugin has `entryClient`, it is loaded from:
    100 - `/plugins/<id>/<entryClient>`
    101 
    102 ### UI panels (draft)
    103 
    104 Some plugins are primarily UI. We’re planning a UI “rack layout” where plugins (and core features) register **dockable panels** instead of directly manipulating core DOM.
    105 
    106 See: `docs/UI_RACK_LAYOUT.md` (draft).
    107 
    108 Client plugins can register via:
    109 ```js
    110 window.BzlPluginHost.register("polls", (ctx) => {
    111   ctx.toast("Polls", "Plugin loaded!");
    112 
    113   // Send a plugin WS message to the server:
    114   ctx.send("ping", { hello: true });
    115 });
    116 ```
    117 
    118 `ctx` provides:
    119 - `ctx.id`
    120 - `ctx.toast(title, body)`
    121 - `ctx.getUser()` / `ctx.getRole()`
    122 - `ctx.ui.registerPanel(panelDef)` (experimental; only active when the core rack layout is enabled)
    123 - `ctx.send(eventName, payload)` -> sends `{ type: "plugin:<id>:<eventName>", ...payload }`
    124 - `ctx.devLog(level, message, data)` -> writes to the in-app dev log (Moderation -> Log -> Server dev log)
    125 
    126 ### `ctx.ui.registerPanel(panelDef)` (experimental)
    127 
    128 This registers a dockable UI panel for your plugin under the rack layout system.
    129 
    130 Notes:
    131 - This is currently **experimental** and only works when the user has enabled the rack layout UI.
    132 - If rack layout is disabled, your plugin should still work using the existing DOM integration patterns.
    133 
    134 Example:
    135 ```js
    136 window.BzlPluginHost.register("hello", (ctx) => {
    137   ctx.ui?.registerPanel?.({
    138     id: "hello",
    139     title: "Hello",
    140     icon: "👋",
    141     defaultRack: "right", // or "main"
    142     role: "aux", // "primary" | "aux" | "transient" | "utility"
    143     presetHints: {
    144       discordLike: { place: "docked.bottom" }
    145     },
    146     render(mount, api) {
    147       mount.innerHTML = "<div class='small'>Hello from a plugin panel.</div>";
    148     }
    149   });
    150 });
    151 ```
    152 
    153 ## Server plugin API
    154 
    155 If your plugin has `entryServer`, it must export a function:
    156 ```js
    157 module.exports = function init(api) {
    158   api.registerWs("ping", (ws, msg) => {
    159     api.broadcast({ type: "plugin:polls:pong", at: api.now() });
    160   });
    161 };
    162 ```
    163 
    164 `api` provides:
    165 - `api.id`
    166 - `api.now()`
    167 - `api.log(level, message, data)`
    168 - `api.registerWs(eventName, handler)`
    169 - `api.registerHttp(method, routePath, handler)`
    170 - `api.broadcast(message)` (must have `type` prefixed with `plugin:<id>:`)
    171 
    172 Server handlers are invoked when the client sends:
    173 - `type: "plugin:<id>:<eventName>"`
    174 
    175 ### Plugin HTTP routes (draft)
    176 
    177 Server plugins can optionally register same-origin HTTP endpoints under:
    178 - `/api/plugins/<pluginId>/<routePath>`
    179 
    180 Example:
    181 ```js
    182 module.exports = function init(api) {
    183   api.registerHttp("GET", "/list", (req, res, ctx) => {
    184     ctx.sendJson(200, { ok: true, items: [] });
    185   });
    186 };
    187 ```
    188 
    189 The `handler` receives `(req, res, ctx)` where `ctx` provides:
    190 - `ctx.readJsonBody({ maxBytes })` (async)
    191 - `ctx.sendJson(status, body)`
    192 
    193 ## Security warning
    194 
    195 Plugins run as code on your server and in every client browser.
    196 - Only install plugins you trust.
    197 - Treat plugins like installing an app, not a theme.