Plugins overview
This content is for the 1.0 version. Switch to the latest version for up-to-date documentation.
StreamHub ships a small plugin framework that lets features plug into both the backend and the dashboard with zero edits to any central registry. A plugin is just a file you drop in — the framework discovers it, lists it in the per-app Plugins marketplace, validates its config against a typed schema, and (when a plugin declares one) owns the lifecycle of its worker process.
This page explains how the framework works and how to install, enable and configure a plugin. Each built-in plugin then has its own page documenting its full configuration.
How plugins are discovered
Section titled “How plugins are discovered”Discovery happens independently on each side, so a plugin can be backend-only,
frontend-only, or both. The two halves are matched by their shared id.
- Backend — drop
streamhub-core/src/plugins/<id>/plugin.meta.tsthat default-exportsdefinePlugin({ ... })(the contract lives in the single filesrc/modules/plugins/plugin.contract.ts— the ONE file a plugin author imports). The registry service globs the filesystem and dynamically imports eachplugin.meta.ts. This backend manifest is the source of truth for a plugin’s id, category, UI slot,configSchemadefaults, and whether it needs a worker. - Frontend — drop
streamhub-web/src/plugins/<id>/index.tsxthat default-exports aPluginModule.discovery.tspicks it up eagerly at build time viaimport.meta.glob('./*/index.{ts,tsx}')— one level of sub-folder only, so the framework’s own flat files are never mistaken for plugins. The frontend module supplies the React component that renders in the UI slot.
Categories
Section titled “Categories”Every plugin declares a category, which drives grouping in the marketplace UI:
| Category | Meaning |
|---|---|
tool |
An on-demand utility (e.g. a diagnostic panel or a player overlay). |
processor |
Consumes/analyzes the media stream, typically via a worker. |
panel |
A self-contained dashboard surface for the app. |
UI slots
Section titled “UI slots”A plugin’s ui field tells the frontend where its component renders:
| Slot | Where it renders |
|---|---|
app-tab |
A full section (tab) inside an app in the dashboard. |
panel |
A panel mounted wherever a <PluginSlot placement="panel"> lives (the marketplace’s active-panels area). |
player-overlay |
Drawn on top of the video player, client-side. This is the auto-mounted slot: whenever a player-overlay plugin is installed + enabled for an app, its overlay renders on that app’s players — including the public /play and /embed pages. |
Config schema
Section titled “Config schema”Each plugin declares a configSchema: a list of typed, labelled fields. The
supported field types are string, number, boolean, select and secret.
Every field must have a default — an install with no config is immediately
valid. Fields may also declare required: true (an install cannot be enabled
until the field is non-empty), min/max bounds for numbers, options for
selects, and placeholder/help hints for the form. The dashboard renders a
generic settings form straight from this schema; the backend validates any
config you submit against it.
Workers
Section titled “Workers”A plugin sets needsWorker: true and provides a worker.spawn(ctx) function to
run an external process per app. spawn is a pure function of a read-only
context (app, resolved config, appDir, dataDir, livekitUrl) that returns
exactly what to run — { command, args, env, cwd }. The framework owns the
whole lifecycle: enabling the plugin (re)starts the worker, disabling stops it,
and start/stop/status/logs are exposed over the API. The core never needs to know
what the worker actually is. The yolo plugin uses this to spawn
a Python detection worker.
Installing, enabling and configuring a plugin
Section titled “Installing, enabling and configuring a plugin”An install is per app. There are two equivalent paths.
- Open the app and go to its Plugins tab (the marketplace).
- Find the plugin card and click Install.
- Toggle the install to Active.
- Click Configure, adjust the fields (the form is generated from the plugin’s config schema), and save.
All routes are scoped under /api/v1/apps/:app/plugins and use a sk_ bearer
token.
-
Install (idempotent):
Terminal window curl -X POST https://YOUR-DOMAIN/api/v1/apps/live/plugins/timestamp/install \-H "Authorization: Bearer sk_..." -
Enable and/or configure —
PATCHacceptsenabledand/or aconfigobject (validated against the plugin’s schema; for worker plugins, enabling (re)starts the worker and disabling stops it):Terminal window curl -X PATCH https://YOUR-DOMAIN/api/v1/apps/live/plugins/timestamp \-H "Authorization: Bearer sk_..." \-H "Content-Type: application/json" \-d '{"enabled": true, "config": {"position": "top-left"}}' -
Inspect logs (worker output + persisted lines, newest last;
limitoptional, default 200, max 1000):Terminal window curl https://YOUR-DOMAIN/api/v1/apps/live/plugins/yolo/logs?limit=200 \-H "Authorization: Bearer sk_..."
Other plugin routes
Section titled “Other plugin routes”| Method & path | Purpose |
|---|---|
GET /apps/:app/plugins |
Marketplace: every built-in plugin + this app’s install/config state. |
GET /apps/:app/plugins/:id |
One marketplace entry (manifest + install state). |
POST /apps/:app/plugins/:id/install |
Install into the app (idempotent). |
PATCH /apps/:app/plugins/:id |
Enable/disable and/or reconfigure. |
DELETE /apps/:app/plugins/:id |
Uninstall (stops its worker). |
GET /apps/:app/plugins/:id/logs |
Per-plugin logs. |
POST /apps/:app/plugins/:id/worker/start |
Explicitly (re)start a worker (needsWorker plugins). |
POST /apps/:app/plugins/:id/worker/stop |
Explicitly stop a worker. |
GET /apps/:app/plugins/:id/worker/status |
Current worker state. |
GET /apps/:app/plugins/public |
Public, no auth — this app’s enabled player-overlay plugins for anonymous players. |
Built-in plugins
Section titled “Built-in plugins”| Plugin | Category | UI slot | Worker | Summary |
|---|---|---|---|---|
| Cockpit | panel |
panel |
No | CCTV-style, drag-and-drop grid of every live stream in the app. |
| Quality / Stream Health | tool |
panel |
No | Bandwidth + latency test distilled into a green/amber/red traffic light. |
| Radio | panel |
app-tab |
No | Audio-only WebRTC radio: go on air, count listeners, mint listen tokens. |
| Video Streaming | tool |
app-tab |
No | Go live with webcam + mic and forward the room to RTMP via server egress. |
| Timestamp CCTV | tool |
player-overlay |
No | Live CCTV-style date/time stamp drawn on the player. |
| Watermark | tool |
player-overlay |
No | Text watermark drawn in a corner of the player. |
| YOLO Object Detection | processor |
player-overlay |
Yes | Python worker runs YOLO over the live stream and POSTs detections to a callback. |