The Plugin Contract
A plugin is an app within the app: inside the platform's boundaries you have enormous freedom to model your domain however you like. The contract is the short list of things you must provide for the core to load and run your plugin — and far from being a cage, these are the few rules that make that freedom safe.
Required elements
Every plugin must provide:
- An
entityrow — registered automatically from your manifest at startup. One row per plugin. - One or more workspace statuses — seeded into the core
workspace_statustable, scoped to your entity viaentity_id. - An entity object table — your domain's business object (e.g.
video,default_plugin_thing). - An
{entity_slug}_workspacejoin table — linking your entity object to core workspaces, withis_deletedandis_archivedboolean columns. - A manifest (
plugins/{entity_slug}/manifest.ts) — declaring your identity, route prefix, menu entries, and extension points. - Migrations under
plugins/{entity_slug}/migrations/.
The four extension points
Your manifest must supply four functions the core calls at the right moments:
resultHandler— persists your domain artifacts when a workflow completes, inside the core's transaction.workspaceSnapshotProvider— builds your domain-specific slice of the webhook payload at launch time.dashboardStatsProvider— returns{ total, completed, label? }for your dashboard card. Completion is yours to define; the core can't compute it.workspaceDisplayResolver— resolves{ entityDisplayName, manageRoute }so the core can render your entities in its dashboard tables.
Optional elements
Beyond the essentials you're free to add: a section/grouping table ({entity_slug}_section), an output table ({entity_slug}_output), any other domain tables, and custom frontend components and pages. Naming follows the platform conventions ({entity_slug}_workspace, _section, _output).
The rules that keep it safe
The freedom above works because of a few hard boundaries. Break these and you break the platform's guarantees:
- Don't mutate core tables at runtime. The only writes a plugin makes to core tables are the startup
entityandworkspace_statusseeds; everything else goes through core services. - Don't add columns to core tables.
- Reference core tables (
workspace,workflow_execution) with proper foreign keys — and never the other way around.
The core validates your structure at startup before loading you: required manifest fields, the declared entity and join tables, the join table's required columns, the initial status, and a non-colliding route prefix. A plugin that fails validation is skipped and logged, and the rest of the platform starts normally.
Follow the contract and the platform will load your plugin, route to it, seed its statuses, and call your extension points at exactly the right times — leaving you free to build whatever your domain needs on top.