Skip to main content
Version: v0.0.46

Plugins

Honeyframe uses the word "plugin" for two completely different things. Read the right section.

If you want to…Read
Install Oracle / MongoDB / Snowflake / FAISS / Torch / etc. driversDriver plugins below
Author or install a vertical (healthcare, finance, retail)Vertical plugins below

Driver plugins

What they are. Optional pip packages that ship connector drivers Honeyframe doesn't bundle by default. The base install supports PostgreSQL + MySQL + MariaDB + the platform's own metadata store out of the box; everything else (Oracle, MSSQL, Mongo, Snowflake, BigQuery, Chroma, FAISS, Torch, etc.) is opt-in to keep the base tarball small and ABI-stable.

Catalog

These are the aliases recognised by --install-plugins and by the plugins: field in install.conf. The right column is the pip package each alias resolves to.

AliasPackageUse for
chroma / chromadbchromadbVector storage
faissfaiss-cpuVector storage (alternative to Chroma)
oracle / oracledboracledbOracle Database connector
mysqlpymysqlMySQL / MariaDB driver (extra features beyond bundled)
mssqlpymssqlMicrosoft SQL Server connector
mongo / mongodbpymongoMongoDB connector
snowflakesnowflake-connector-pythonSnowflake connector
bigquerygoogle-cloud-bigqueryBigQuery connector
ga4google-analytics-dataGoogle Analytics 4 ingestion
ossoss2Alibaba OSS object storage
torchtorchLocal LLM / embedding inference

Adding a new alias: edit PLUGIN_MAP in iaas/scripts/setup-customer.sh and rebuild the tarball. Aliases never make it into the live UI — they're install-time only.

Installing at install time

In install.conf:

customer:
name: acme
display_name: Acme Robotics
# ...
plugins: chroma,oracle,snowflake

Runs as part of setup-customer.sh. Plugins are installed before the backend service starts, so the first boot sees them.

Installing later

sudo /opt/honeyframe/iaas/scripts/setup-customer.sh \
--install-plugins chroma,oracle,snowflake

Skips the rest of the install — only installs the listed plugins.

Where they live

/data/honeyframe/plugins/ # actual install target

This path is on the data dir, not the install dir. That's deliberate — it means a tarball upgrade (tar xzf hub-platform-vN.tar.gz) doesn't blow away your installed drivers. The systemd unit's PYTHONPATH includes this directory so the Nuitka-compiled binary picks them up at boot.

Verifying installation

ls /data/honeyframe/plugins/ # one dir per top-level package
sudo systemctl status hub-platform # service should still be active
curl -s http://localhost:8001/api/health

The Connectors page in the UI will surface the new types automatically — GET /api/connectors/catalog introspects which drivers are importable at runtime.


Vertical plugins

What they are. Architectural extension points that let entire verticals — healthcare, finance, retail — ship as drop-in packages instead of forks of the base platform. A new customer in a new vertical installs PaaS + their vertical plugin and gets a working product without ever seeing other verticals' UI or dbt models.

The plugin.yaml contract

Validated by paas/backend/services/plugin_host.py::PluginManifest. Only name, version, and display_name are required.

name: healthcare # lowercase slug, hyphenated
version: 1.0.0 # semver — plugins version independently of PaaS
display_name: Honeyframe — Healthcare

# FastAPI router entry points — dotted Python paths to a router
# instance. PaaS will include each one at startup.
# Empty list = backend-less plugin.
routers:
- plugins.healthcare.backend.routers.records:router
- plugins.healthcare.backend.routers.facilities:router

# Path (relative to plugin root) of the dbt workspace. Tenants on
# this vertical get this slug added to PLATFORM_SHARED_DBT_SLUGS.
dbt_workspace: dbt/

# Build-time merges — frontend bundler reads this when assembling
# the SPA for tenants on this vertical.
frontend_overrides:
routes:
/records: plugins/healthcare/frontend/RecordList.tsx
blocks:
RecordCard: plugins/healthcare/frontend/RecordCard.tsx

# Recipes seeded into honeyframe.flow_recipes at install time.
known_scripts:
- slug: extract_records
display_name: an external source Extractor
script_path: plugins/healthcare/scripts/extract_records.py
output_schema: raw_records

# Dataset templates installed for new projects on this vertical.
dataset_templates: []

# Default dashboards.
dashboard_seeds: []

Where vertical plugins live

PathOwned bySurvives upgrade
/opt/honeyframe/plugins/<name>/Tarball (immutable)No — overwritten on every upgrade
/data/honeyframe/plugins/<name>/Customer (mutable)Yes

Bundled platform plugins ship in $INSTALL_DIR/plugins/ — the healthcare / finance / retail packages the platform team distributes. Customer-installed extras (a vertical the platform doesn't ship by default, or a forked copy of a bundled one) go in $DATA_DIR/plugins/.

This split mirrors the v0.0.32 .env move: immutable platform code in install-dir, mutable customer state in data-dir. The plugin contract follows the same boundary.

The Plugins admin page

/plugins (admin role only — surfaced under the "More" menu) renders the live discovery output as a table. As of v0.0.38, the page also exposes per-org and per-tenant enable/disable toggles — a plugin can be discovered by the platform and enabled for one org while staying off for another.

Plugins admin page

The same data is available from the API:

GET /api/plugins/status

/api/plugins/status is also what the Connectors page polls to grey out connector types whose driver isn't installed (so a tenant can see "Oracle (driver not installed)" instead of a confusing failure when picking a type).

Per-org and per-tenant enablement (v0.0.38)

Three layers control whether a vertical plugin is visible to a given caller:

  1. Discovered — the manifest validates and the platform sees it. Set at the filesystem (under <INSTALL_DIR>/plugins/<slug>/).
  2. Org enabledPOST /api/plugins/{slug}/enable flips it on for an org. Toggles surface in the Plugins admin page; admins of that org see them.
  3. Tenant enabled — within an org that has the plugin enabled, individual tenants can opt in or out. Used for staged rollouts.

Every enable/disable transition writes an entry to the audit log (audit.events, event_type = plugin_state_change) with the actor, target plugin, target scope, and timestamp. The audit log is the source of truth for "when did this go live in production?" — the plugin's own runtime state is intentionally separate from how it was enabled.

Plugin-declared dbt workspaces (v0.0.38)

A vertical plugin can declare its own dbt workspace in plugin.yaml:

dbt_workspaces:
- slug: healthcare
profile: healthcare
project_dir: dbt/

On enable, the platform auto-registers the workspace alongside the platform's bundled honeyframe workspace. Models in the plugin's workspace are buildable from the Flow canvas without a manual PLATFORM_SHARED_DBT_SLUGS edit. On disable, the workspace is unregistered (models stay on disk; they just stop appearing in the UI).

Build-time frontend override aggregator (v0.0.38)

For UI overrides, plugins ship React components that the platform substitutes for built-in surfaces (e.g. healthcare's bespoke records dashboard replaces the generic dashboard view for that vertical). The aggregator runs at portal-build time, scans every plugins/*/frontend/overrides.json, and produces a single import manifest the runtime consumes via the useOverride hook:

import { useOverride } from '@platform/plugin-host';

function DashboardView() {
const Override = useOverride('dashboard.view');
return Override ? <Override /> : <DefaultDashboardView />;
}

The hook returns null when no plugin claims the override key, so the default surface is the fallback. Multiple plugins claiming the same key resolve in declared-priority order; the first match wins.

Discovery output (logs)

At PaaS startup, the plugin host walks <INSTALL_DIR>/plugins/*/plugin.yaml, validates each manifest, and logs a one-line summary you can read with journalctl:

INFO plugin_host: 2 plugins discovered (2 ok, 0 invalid)

Invalid manifests log a WARNING with the validation errors and are skipped — they do not block startup.

Authoring a new vertical plugin

Minimum viable plugin that loads cleanly today:

plugins/your_vertical/
└── plugin.yaml
name: your-vertical
version: 0.1.0
display_name: Your Vertical

That's it. Discovery sees it, validates it, logs it. Mounting is a no-op until tech-debt #7 lands the rest of the host.

When the rest of the host ships, the same manifest will start to actually do something — without any change on your side as long as you stay within the documented schema.

Migration: saas/plugins/healthcare/

Honeyframe's healthcare vertical currently ships in saas/ as a sibling app. The cutover plan, in order:

  1. Define the entry-point contract (plugin.yaml, plugin_host.py) — done
  2. Move SaaS healthcare routers under the contract one router at a time, with both old and new paths active — pending
  3. Build a frontend plugin loader that mounts component overrides — pending
  4. Register the SaaS dbt workspace via the manifest instead of the hardcoded PLATFORM_SHARED_DBT_SLUGS set — pending
  5. Cut over: remove saas/backend/main.py, rename saas/frontend/plugins/healthcare/frontend/ — pending
  6. A finance vertical becomes plugins/finance/ — pending

Until step 2 lands, don't put new verticals in saas/ — start them under plugins/ directly so the eventual extraction is one less migration.


Naming clash — why "plugins" means two things

Honeyframe is mid-rebrand from dataintel to honeyframe and the vertical-plugin host landed during that window (2026-04-25). The existing --install-plugins CLI shipped earlier in v0.0.31 with a narrower meaning (drivers only). Renaming either is breaking; both ship as "plugins". This page disambiguates them; future docs and error messages should always qualify which kind:

  • "driver plugin" — the pip-installed connector drivers
  • "vertical plugin" — the plugin.yaml-described extension package

If you find a doc or error message that says "plugin" without the qualifier, that's a bug — file an issue.