Plugins
Plugins extend Streamdat without forking core code. They ship as Python packages with a plugin.json manifest and load through app.plugin.loader. Today the render hooks are the production-wired extension points; the other plugin types are a stable authoring framework with reference examples.
Integration status
Six plugin types validate and register (see PLUGIN_TYPE_MAP in the app repo), but only the two render hooks are invoked by the production render task (run_pre_render_plugins / run_post_render_plugins). Plan around this table rather than assuming every type executes automatically:
| Type | Role | Production status |
|---|---|---|
pre_render | Adjust style_params before FFmpeg runs | Live in pipelineRuns on every clip render |
post_render | Inspect the rendered file; return warnings and metadata | Live in pipelineRuns after every clip render |
highlight_detector | Propose highlight spans from transcript + duration + config | FrameworkExample ships; production scoring does not call plugins |
caption_generator | Produce caption text from segments and plain text | FrameworkExample ships; production captioning does not call plugins |
style_interpreter | Map a natural-language style brief into renderer parameters | FrameworkNot invoked by production flows |
posting_strategy | Suggest a schedule over clips and linked social accounts | FrameworkNot invoked by production flows |
Writing a highlight_detector, caption_generator, style_interpreter, or posting_strategy plugin today means building against the framework API: it will install, validate, and be executable through the plugin service, but the automatic ingest/scoring/captioning/posting flows will not call it yet.
Loading model
Plugins are not loaded at application startup. They enter the in-memory registry (and the database) when an admin triggers one of:
POST /api/plugins/sync: rescan the on-disk plugin directory (PLUGIN_DIR) and sync discovered manifests.POST /api/plugins/install: clone from a publichttps://Git URL, install itsrequirements.txtinto a local.deps/folder, dry-run load, then persist and register.
After a process restart, run sync again to repopulate the registry. Install is gated twice: the caller must be an admin and on a plan that includes plugin install (Pro or Studio).
Manifest
Each plugin directory contains plugin.json with name, type, version, entrypoint (module.ClassName), and optional schema_version, compatible_core (PEP 440 specifier against core semver), permissions, and signatures. Optional .deps/ (from pip install --target) is prepended to sys.path before import.
{
"name": "my_plugin",
"type": "pre_render",
"version": "1.0.0",
"schema_version": "1",
"compatible_core": ">=1.0.0,<2.0.0",
"description": "What this plugin does.",
"entrypoint": "main.MyPreRenderPlugin",
"permissions": []
}HTTP API
Authenticated users can list plugins and read configs. Admin-only routes rescan the plugin directory into the database (sync), toggle enablement, patch config_json, and uninstall; install from Git additionally requires a plugin-install plan. Shapes are defined on the /api/plugins router in the backend; cross-check the live contract in Swagger (which itself requires a signed-in user on a plan with API access).
Sandboxing
Sandboxing is opt-in per plugin, not the default: plugins run in-process unless their config_json.sandboxed flag is true. When enabled, execution moves to a subprocess with an allow-listed environment and JSON context passed in; the child prints JSON on stdout, and a timeout (30s default) stops runaway plugins from blocking the worker.
Signatures
Manifest signatures are accepted but cryptographic verification is not implemented yet. The PLUGIN_REQUIRE_SIGNATURE setting is a strict gate: when enabled it refuses to load any plugin (fail closed); when disabled (the default) plugins load unverified and production logs a warning. Until real verification lands, review plugin code like any other code you deploy.
Examples
Reference implementations ship under backend/plugins/ in Streamdat_app: keyword_highlighter (a highlight_detector) and emoji_captions (a caption_generator). Both demonstrate framework types from the table above: useful as authoring templates, but not called by the automatic pipeline. Copy one, change the manifest and class, then run POST /api/plugins/sync or install from a Git URL where your org enables that feature.