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.