Skip to main content

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:

  1. An entity row — registered automatically from your manifest at startup. One row per plugin.
  2. One or more workspace statuses — seeded into the core workspace_status table, scoped to your entity via entity_id.
  3. An entity object table — your domain's business object (e.g. video, default_plugin_thing).
  4. An {entity_slug}_workspace join table — linking your entity object to core workspaces, with is_deleted and is_archived boolean columns.
  5. A manifest (plugins/{entity_slug}/manifest.ts) — declaring your identity, route prefix, menu entries, and extension points.
  6. 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 entity and workspace_status seeds; 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.