bzl

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

PLUGINS.md (4059B)


      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 ## Install / manage (Moderator/Owner UI)
      8 
      9 1. Sign in as `owner` or `moderator`.
     10 2. Open the **Instance** panel in the left sidebar.
     11 3. Under **Plugins**:
     12    - Upload a plugin `.zip` (must contain a `plugin.json` manifest).
     13    - Toggle **Enabled** to activate it.
     14    - Use **Uninstall** to remove it from disk.
     15 
     16 Notes:
     17 - Enabling/disabling a plugin may require a refresh for all connected clients (depends on what the plugin does).
     18 
     19 ### Dev: build the included example plugin zips
     20 This repo includes starter plugins and zip builders:
     21 - Maps: `plugins_dev/maps/`
     22   - Build: `node scripts/build-maps-plugin.js`
     23   - Upload: `dist/plugins/maps.zip`
     24 - Library: `plugins_dev/library/`
     25   - Build: `node scripts/build-library-plugin.js`
     26   - Upload: `dist/plugins/library.zip`
     27 - Directory Server (draft): `plugins_dev/directory-server/`
     28   - Build: `node scripts/build-directory-server-plugin.js`
     29   - Upload: `dist/plugins/directory-server.zip`
     30 - Directory Publisher (draft): `plugins_dev/directory-publisher/`
     31   - Build: `node scripts/build-directory-publisher-plugin.js`
     32   - Upload: `dist/plugins/directory-publisher.zip`
     33 
     34 ## Where plugins live on disk
     35 
     36 - Installed plugins are stored at: `data/plugins/<pluginId>/`
     37 - Each plugin folder must contain: `plugin.json`
     38 
     39 ## `plugin.json` manifest
     40 
     41 Required:
     42 - `id`: `^[a-z0-9][a-z0-9_.-]{0,31}$`
     43 - `name`
     44 - `version`
     45 
     46 Optional:
     47 - `description`
     48 - `entryClient`: path to a client JS file within the plugin folder
     49 - `entryServer`: path to a server JS file within the plugin folder
     50 - `permissions`: string labels shown in the UI (informational)
     51 
     52 Example:
     53 ```json
     54 {
     55   "id": "polls",
     56   "name": "Polls",
     57   "version": "0.1.0",
     58   "description": "Adds simple polls to hives.",
     59   "entryClient": "client.js",
     60   "entryServer": "server.js",
     61   "permissions": ["ui", "ws"]
     62 }
     63 ```
     64 
     65 ## Zip format
     66 
     67 The `.zip` you upload must include `plugin.json` either:
     68 - at the zip root, or
     69 - inside a single top-level folder
     70 
     71 After extraction, the server installs it into `data/plugins/<id>/`.
     72 
     73 ## Client plugin API
     74 
     75 If your plugin has `entryClient`, it is loaded from:
     76 - `/plugins/<id>/<entryClient>`
     77 
     78 Client plugins can register via:
     79 ```js
     80 window.BzlPluginHost.register("polls", (ctx) => {
     81   ctx.toast("Polls", "Plugin loaded!");
     82 
     83   // Send a plugin WS message to the server:
     84   ctx.send("ping", { hello: true });
     85 });
     86 ```
     87 
     88 `ctx` provides:
     89 - `ctx.id`
     90 - `ctx.toast(title, body)`
     91 - `ctx.getUser()` / `ctx.getRole()`
     92 - `ctx.send(eventName, payload)` -> sends `{ type: "plugin:<id>:<eventName>", ...payload }`
     93 - `ctx.devLog(level, message, data)` -> writes to the in-app dev log (Moderation -> Log -> Server dev log)
     94 
     95 ## Server plugin API
     96 
     97 If your plugin has `entryServer`, it must export a function:
     98 ```js
     99 module.exports = function init(api) {
    100   api.registerWs("ping", (ws, msg) => {
    101     api.broadcast({ type: "plugin:polls:pong", at: api.now() });
    102   });
    103 };
    104 ```
    105 
    106 `api` provides:
    107 - `api.id`
    108 - `api.now()`
    109 - `api.log(level, message, data)`
    110 - `api.registerWs(eventName, handler)`
    111 - `api.registerHttp(method, routePath, handler)`
    112 - `api.broadcast(message)` (must have `type` prefixed with `plugin:<id>:`)
    113 
    114 Server handlers are invoked when the client sends:
    115 - `type: "plugin:<id>:<eventName>"`
    116 
    117 ### Plugin HTTP routes (draft)
    118 
    119 Server plugins can optionally register same-origin HTTP endpoints under:
    120 - `/api/plugins/<pluginId>/<routePath>`
    121 
    122 Example:
    123 ```js
    124 module.exports = function init(api) {
    125   api.registerHttp("GET", "/list", (req, res, ctx) => {
    126     ctx.sendJson(200, { ok: true, items: [] });
    127   });
    128 };
    129 ```
    130 
    131 The `handler` receives `(req, res, ctx)` where `ctx` provides:
    132 - `ctx.readJsonBody({ maxBytes })` (async)
    133 - `ctx.sendJson(status, body)`
    134 
    135 ## Security warning
    136 
    137 Plugins run as code on your server and in every client browser.
    138 - Only install plugins you trust.
    139 - Treat plugins like installing an app, not a theme.