9.9 KiB
Plugins
Runtime gate: Plugin runtime is staging/foundation only until the external WASM sandbox security review is signed off in Plugin Runtime Signoff. Do not describe third-party plugin execution as production-enabled before that sign-off is complete.
Status
Plugins are an installable WASM extension surface for optional tenant behavior. The current codebase ships the runtime foundation, hook fanout, lifecycle service, tenant settings surface, operator CLI commands, manifest validation, capability gating, migrations guardrails, crash tracking, and audit views.
This page is the product-facing entrypoint. It intentionally links to deeper architecture and operator docs instead of duplicating their implementation details.
| Area | Current state | Production interpretation |
|---|---|---|
| Runtime foundation | Shipped for staging/foundation use: manifest validation, signatures, capability gateway, isolated invoke queue, lifecycle service, settings shell, audit rows, and crash tracking. | Keep PLUGIN_RUNTIME_PRODUCTION_ENABLED=false until the external WASM sandbox review is signed off. |
| Hook payload ABI | Shipped foundation ABI: plugins can read the current invocation payload as UTF-8 JSON via env.hivegrid_payload_len() and env.hivegrid_payload_byte(index). Numeric scalar hooks still receive the scalar argument directly. |
Available for reviewed staging plugins; higher-level SDK language bindings remain follow-up work. |
| Safe host bindings | log and audit are wired through the capability gateway and audited. |
Available for reviewed staging plugins and future production plugins after the runtime gate is opened. |
| Higher-risk or deferred host bindings | http.outbound, secret.read, db.read, db.write, storage.read, storage.write, and tenant.info are declared capability concepts but not exposed as production host functions. |
Install/grant/enable paths fail closed until each binding has security review, tests, operator copy, and audit behavior. |
| Hooks | Foundation hooks cover tenant/user creation, row-backed entity changes, tenant-scoped service-path workflow definition changes, reviewed tenant template-import creates, terminal workflow.executed, and failure-only workflow.failed from both web-service and worker-native execution paths. |
Platform-scoped workflow definitions, definition writes that bypass reviewed services, billing, auth, chatbot, outbound webhook, and synchronous transform/cancel hooks remain follow-ups. |
| Marketplace / hot reload / SDK publishing | Architecture contracts exist; operational surface remains intentionally deferred. | Do not describe plugin marketplace or hot-reload publishing as shipped product behavior. |
What Exists
- Runtime package:
@hivegrid/plugin-runtime - SDK package:
@hivegrid/plugin-sdk - Manifest schema and signature verification
- Capability enum and capability gateway
- Tenant-scoped
PluginandPluginUserConfigpersistence - Lifecycle mutations: install, grant/revoke consent, enable, disable, list, settings, and audit through tenant service/API/UI surfaces. Uninstall is a CLI/operator-only escape hatch that deletes the tenant plugin row; it is not exposed through tenant API or UI.
- Hook registry and hook fanout for the foundation hook set
- Isolated plugin invocation queue:
hivegrid.plugin-invoke - Tenant plugin settings page and plugin audit page
- Crash tracker with tenant-level auto-disable support
- Durable per-invocation audit rows via
PluginInvocation, correlated with capability-use rows bypluginInvocationId
Foundation Hooks
The active foundation hook set is documented in Plugin Hooks & Lifecycle:
Registered hook ABI names include:
tenant.createduser.createdentity.createdentity.updatedentity.deletedworkflow.createdworkflow.updatedworkflow.deletedworkflow.executedworkflow.failed
Currently producer-wired hooks are tenant.created, user.created,
row-backed entity.created/entity.updated/entity.deleted, tenant-scoped
service-path workflow.created/workflow.updated/workflow.deleted,
tenant-scoped workflow template-import workflow.created, and terminal
workflow.executed plus failure-only workflow.failed. Platform-scoped
workflow definitions are not treated as covered unless they are routed through
the reviewed workflow service/import paths.
Additional billing hooks, auth hooks, chatbot hooks,
outbound webhook hooks, and synchronous transform/cancel hooks are not complete
yet.
Capability Model
Plugins declare capabilities and hook subscriptions in the manifest. The host validates those declarations and only exposes host functions through the capability gateway. Hook subscriptions are also treated as explicit tenant-admin consent items using the synthetic hook:<hook-name> grant name, so enabling a hook-only plugin still requires an acknowledged review. The active capability catalog is maintained in Capability Matrix.
Important current limitation: several capabilities are declared and documented before their host bindings are fully wired. The worker wires the safe synchronous log and audit imports through the capability gateway. Deferred capabilities (http.outbound, secret.read, db.read, db.write, storage.read, storage.write, and tenant.info) need explicit security review, operator copy, tests, and audit behavior before production use. Production lifecycle paths reject install, grant, and enable attempts for capabilities that do not yet have reviewed host bindings. The runtime already contains the fail-closed per-call scope guard for future http.outbound, secret.read, and db.read/db.write host calls: HTTP calls must target exact HTTPS host grants, secret reads must match the tenant-approved key pattern, and database calls must stay within the approved table prefix and structured non-raw query contract.
Tenant Admin Flow
Tenant admins can view installed plugins, inspect manifest capability and hook details, and enable or disable plugins from tenant settings. Missing consent grants are approved from the tenant settings list: the form shows each missing manifest capability name/scope and each hook subscription as an explicit review checkbox and submits only the selected consent names with a server-verified review digest derived from the current plugin id, version, selected capability scopes, and hook names. If the manifest changes between render and submit, the action rejects the stale review before granting anything. Already approved consent grants can be revoked individually; revoking a grant disables the plugin before removing the grant and records plugin.capabilities.revoked.
Tenant plugin API lifecycle mutations are JSON-only: install, grant, revoke,
enable, and disable require Content-Type: application/json, return
400 invalid_json for malformed JSON, and return
415 unsupported_media_type when the media type is missing or unsupported.
Enable/disable do not need fields, but callers must still send {} as the
JSON body.
The reusable plugin settings iframe shell lives in
apps/web/app/modules/plugins/components/PluginSettingsFrame.tsx. It renders
trusted settings UI HTML in an opaque-origin iframe, denies allow-same-origin,
embeds a restrictive CSP including navigate-to 'none', quarantines unexpected
iframe reload/navigation attempts by remounting the srcDoc frame, denies
browser permissions through the iframe allow policy, and accepts only
strictly-shaped, versioned plugin.settings.* postMessage payloads from the
current iframe window with the per-render plugin-settings-channel nonce.
Tenant settings link enabled plugins with manifest ui.settings.entry metadata
to a server-side bundle loader and persist non-secret per-user settings into
PluginUserConfig.configJson.
Rejected postMessage attempts and unexpected iframe reloads are also reported
as rate-limited, client-reported audit events (plugin.settings_frame.violation)
with only the plugin id, plugin row id, and violation reason retained.
The route rejects unsafe setting keys, secret-like keys, non-JSON values, and
settings payloads larger than 64 KiB; secrets remain out of scope until a
dedicated encrypted host binding exists.
Permissions:
app.plugins.use: read/list installed plugins, open plugin settings surfaces, and write the current user's non-secretPluginUserConfigsettings for enabled pluginsapp.plugins.manage: install, grant, revoke, enable, and disable through tenant API/UI surfacesapp.settings.auditTrails.view: open plugin capability-use audit trails
Operator Flow
Operators manage installed plugins with the plugin CLI and lifecycle service. See Plugin Lifecycle Operations for the operational runbook.
Author References
- Manifest Reference
- SDK Reference
- CLI Reference
- Versioning Policy
- Plugin Runtime Signoff
- Plugin Threat Model
Known Open Slices
These are implementation-ready but not done:
- Extend workflow hook producer coverage beyond the tenant-scoped service/import paths, including platform-scoped definitions and other definition writes that bypass reviewed services, if those become plugin-observable product behavior.
- Add disabled-plugin route/nav tests for plugin-candidate surfaces such as Workflow Engine.
- Add encrypted secret/settings host bindings after a dedicated security review.
- Complete external WASM sandbox security review before production enablement.
Verification
For documentation-only changes:
pnpm run verify:docs-links
For runtime/plugin changes:
pnpm --filter @hivegrid/worker build
pnpm --filter @hivegrid/api build
pnpm exec vitest run packages/plugin-runtime