Skip to content

Plugins overview

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.

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.ts that default-exports definePlugin({ ... }) (the contract lives in the single file src/modules/plugins/plugin.contract.ts — the ONE file a plugin author imports). The registry service globs the filesystem and dynamically imports each plugin.meta.ts. This backend manifest is the source of truth for a plugin’s id, category, UI slot, configSchema defaults, and whether it needs a worker.
  • Frontend — drop streamhub-web/src/plugins/<id>/index.tsx that default-exports a PluginModule. discovery.ts picks it up eagerly at build time via import.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.

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.

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.

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.

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.

  1. Open the app and go to its Plugins tab (the marketplace).
  2. Find the plugin card and click Install.
  3. Toggle the install to Active.
  4. Click Configure, adjust the fields (the form is generated from the plugin’s config schema), and save.
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.
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.