Overview
How Voltro plugins compose into the runtime, what they can intercept, the catalogue, and writing your own.
A Voltro plugin is a server-side extension that hooks into the runtime. Plugins can intercept mutations / queries / actions, contribute schema mixins + tables + migrations, mount raw-HTTP and inspect routes, contribute an Effect service layer, and run install / activate / deactivate lifecycles.
The framework ships some plugins; you write your own; the contract is small enough to learn in a single read.
What's in this section
- The plugin contract —
definePlugin, lifecycle hooks, rpc interceptors, framework-version compatibility - plugin-audit — mutation audit log +
audit()mixin - plugin-auth — full auth suite: password (rehash-on-verify) + sessions (multi-key rotation + sliding-window) + magic-link/reset + passkeys (atomic clone detection, BYO multi-replica challenge store) + CSRF + session revocation + memberships/switch-tenant + TOTP/MFA (sign-in enforcement + recovery codes), mounted by
authRoutesPlugin()(see also the Authentication section) - plugin-multitenancy —
tenant()schema mixin +assertOwnTenantwrite-guard +TenantMismatch - plugin-soft-delete —
softDelete()schema mixin (hide on delete,hardDelete()bypass) - plugin-rbac — roles + permissions + the
permission()guard - plugin-ratelimit — per-endpoint / per-subject / per-tenant request limits
- plugin-billing — subscriptions, plans, entitlements + usage metering (Stripe + mock provider); seat-based billing on Stripe's own proration, retries, tax and checkout; money as integer minor units
- plugin-licensing — offline-verified EdDSA license keys + cloud-issued entitlement snapshots that feed plugin-billing; plan entitlements + pricing decided server-side, never baked into a published version
- plugin-mail — transactional email (Resend / Postmark / SendGrid / SES / Mailgun / SMTP, templates, suppression, scheduling, batch, idempotency)
- plugin-storage — file storage: public (CDN-direct) + private (access policy + per-object grants), S3 / R2 / GCS / MinIO / filesystem
- plugin-ai-flows — durable multi-step AI pipelines (deterministic + agentic) with human-in-the-loop, chaining, and cadence; code-first
defineFlowor data-driven rows - plugin-postgis — postgres-native
geography/geometrycolumns + spatial operators - plugin-broadcast — cross-replica reactivity over a pub/sub bus (Redis / NATS) for non-postgres dialects
- plugin-webhooks — durable incoming + outgoing webhooks (HMAC signing, retries, idempotency)
- plugin-atlassian —
JiraService+ConfluenceServiceover the Atlassian APIs - plugin-deactivation —
deactivation()schema mixin (visible, can't log in) - plugin-prometheus — Prometheus exporter at
GET /metrics; scrapes the unified Metrics-API (the same source the dashboard Metrics panel reads) - plugin-datadog — deep Datadog integration; agentless metrics + opt-in logs + traces (OTLP→Agent) + profiler, trace-correlated
- plugin-sentry — deep Sentry integration; trace-correlated errors + breadcrumbs from the log sink + opt-in performance traces
- plugin-flags — feature flags: per-subject / per-tenant targeting, deterministic % rollouts, kill-switch, declarative rpc gating + client UI gating
- plugin-notifications — unified notifications: one send API across email / Slack / SMS / push (first-class APNs/FCM factory) / in-app, channel preferences + in-app inbox, digest/batching, quiet hours, broadcast/topics, durable DataStore-backed store by default
- plugin-logship — ship structured logs to Better Stack / Axiom / Loki / any HTTP sink; batched, redacted, fail-soft
- plugin-moderation — moderate user content before commit: keyword or AI provider, block / flag via interceptor + in-handler redact
- plugin-search — keep an external index (Typesense / Meilisearch / Algolia) in sync via the ChangeEvent tap; tenant-scoped
search.query+ hook - plugin-cdc-out — declarative reverse-ETL: mirror table changes outward to a webhook / Kafka / Snowflake / BigQuery sink through a durable outbox; ordered per pipe, at-least-once from enqueue, dead-lettered
- plugin-governance — data governance: retention TTL sweep, GDPR export + erasure, consent ledger, field encryption
- plugin-openapi — OpenAPI 3.1 spec + Swagger-UI docs generated from your
defineRestRoutedescriptors and (opt-in) rpc procedures - plugin-versioning — full row history + time-travel (
rowHistory/rowAsOf/restoreAsOf/diffVersions); what-changed-to-what on every write - plugin-presence — ephemeral realtime presence: heartbeat roster per channel +
usePresence/useTypinghooks, cross-instance - plugin-scim — SCIM 2.0 provisioning (Users + Groups at
/scim/v2) so an enterprise IdP can create/deactivate users - plugin-sso-saml — enterprise SAML 2.0 SSO: SP-initiated login + Single Logout, ACS, metadata (+ IdP-metadata-URL auto cert rotation, encrypted assertions, SP request signing); mints a framework session
- API keys — first-class (not a plugin):
apiKeys: trueenables Bearer-key auth + admin-gated issue/list/revoke - Analytics & warehouse sinks —
AnalyticsSinkcontract + five first-party sink plugins (postgres-lite, DuckDB, ClickHouse, Tinybird, PostHog) - External identity providers — the six auth-adapter packages (WorkOS, Kinde, Clerk, Auth0, Supabase, generic OIDC)
The catalogue at a glance
Status legend: ✓ shipped · ◐ partial · — planned.
| Plugin | Status | What it does |
|---|---|---|
@voltro/plugin-audit |
✓ | Mutation audit log + audit() mixin |
@voltro/plugin-auth |
✓ | Full auth suite via authRoutesPlugin(): password (rehash-on-verify), sessions (multi-key rotation + sliding-window), magic-link + password-reset, passkeys/WebAuthn (atomic clone detection, BYO multi-replica challenge store), CSRF, session enumeration + revocation, memberships + switch-tenant, TOTP/MFA (sign-in enforcement + recovery codes); authTables schemas |
@voltro/plugin-multitenancy |
✓ | tenant() schema mixin (read-scope + write-fill) + assertOwnTenant guard + typed TenantMismatch |
@voltro/plugin-soft-delete |
✓ | softDelete() schema mixin — deletedAt / deletedBy; delete → UPDATE, hardDelete() bypass |
@voltro/plugin-rbac |
✓ | Roles compile to scopes + the permission() handler guard + typed ScopeError |
@voltro/plugin-ratelimit |
✓ | Per-endpoint / per-subject / per-tenant limits; sliding-window / fixed-window / token-bucket; memory / postgres / redis stores |
@voltro/plugin-billing |
✓ | Subscriptions, plans, entitlements + usage metering over a pluggable provider (Stripe + mock); seat-based billing; proration, failed-payment retries, tax and the checkout seat stepper are Stripe's, via the official SDK; requireEntitlement() guard + enforce interceptor; /billing/webhook via plugin-webhooks; money as integer minor units |
@voltro/plugin-mail |
✓ | Transactional email — Resend / Postmark / SendGrid / SES / Mailgun / SMTP, *.email.tsx templates, per-tenant suppression, send-time scheduling, bulk/batch send, per-send idempotency, durable via workflows |
@voltro/plugin-storage |
✓ | File storage — public (CDN-direct) + private (access policy + per-object grants), S3 / R2 / GCS / MinIO / filesystem providers, presigned URLs, listRefs browse/search, HTTP Range (206) serving, dashboard browser |
@voltro/plugin-postgis |
✓ | Postgres-native geography / geometry columns + spatial predicates (ST_DWithin, ST_Contains, ST_Intersects); GiST indexes via .expressionIndex(..., { kind: 'gist' }). No ST_Distance projection yet. Postgres-only by design (fails loud elsewhere). → details |
@voltro/plugin-broadcast |
✓ | Cross-replica reactivity — fans out app-mutation change events to every replica over a pub/sub bus (Redis / NATS). Closes the single-instance gap for every non-postgres dialect. → details |
@voltro/plugin-webhooks |
✓ | Incoming + outgoing webhooks — defineIncomingWebhook (signature verify + idempotency, Stripe/GitHub/Slack presets) and defineOutgoingEvent (durable delivery workflow, HMAC signing, retries, filters). → details |
@voltro/plugin-auth-{workos,kinde,clerk,auth0,supabase,oidc} |
✓ | Six IdP adapters over the shared jwtBearerStrategy — JWKS verify + claims→tenant mapping; WorkOS additionally ships hosted-login OAuth primitives (workosAuthorizationUrl / workosAuthenticateWithCode) for a redirect-based SSO login flow. → details |
@voltro/plugin-analytics-postgres |
✓ | First-party lite — events on the main DataStore, cross-dialect (postgres / mysql / mariadb / mssql / sqlite / turso). → details |
@voltro/plugin-duckdb |
✓ | Embedded DuckDB sidecar — real OLAP performance, no external service. → details |
@voltro/plugin-clickhouse |
✓ | Production OLAP via the official ClickHouse client. → details |
@voltro/plugin-tinybird |
✓ | Hosted ClickHouse via Events API + Pipes. → details |
@voltro/plugin-posthog |
✓ | Product analytics — track-only; compose with another sink for reads. → details |
@voltro/plugin-atlassian |
✓ | JiraService + ConfluenceService over the Atlassian REST / Greenhopper / Agile APIs — PAT or OAuth 2.0 (3LO) auth, transient retry, SSRF guard, comment-write, signature-verified inbound webhooks, avatar proxy, per-tenant cache. → details |
@voltro/plugin-deactivation |
✓ | deactivation() schema mixin — deactivatedAt + deactivatedBy (→ Actor); subject can't log in but data stays visible. → details |
@voltro/plugin-prometheus |
✓ | Prometheus exporter — GET /metrics in text exposition format over the unified Metrics-API (Effect MetricRegistry); counters / histograms / gauges + custom metrics, optional bearer gate + node process metrics. → details |
@voltro/plugin-datadog |
✓ | Deep Datadog integration — agentless metrics push to /api/v2/series + opt-in logs (/api/v2/logs, dd.trace_id-correlated) + traces (OTLP→Agent) + dd-trace profiler; DD_API_KEY/DD_SITE + unified service tagging, fail-soft. → details |
@voltro/plugin-sentry |
✓ | Deep Sentry integration — mutation/query/action errors reported correlated to the active trace (trace_id + span_id) + breadcrumbs from the framework log sink; opt-in performance traces (SentrySpanProcessor, OTel-consumer mode) + profiler. @sentry/* optional + lazy. → details |
@voltro/plugin-flags |
✓ | Feature flags — per-subject / per-tenant targeting + deterministic % rollout (FNV-1a bucket) + kill-switch; multivariate variant flags + scheduled / ramping rollouts + a durable kill-switch audit trail; declarative gatedBy (typed FlagDisabled) + requireFlag guard + flags.evaluate / flags.variants routes + useFlags/useFlag/useVariant hooks; memory / postgres store. → details |
@voltro/plugin-notifications |
✓ | Unified notifications — one send across email / Slack / SMS / push (first-class pushChannel APNs/FCM factory) / in-app channels, per-user channel preferences, in-app inbox + unread count + delivery records; digest/batching rollup, per-subject quiet hours (DND), broadcast/topic fan-out; NotificationService + useInbox/useUnreadCount/useMarkRead/useTopicSubscription/useQuietHours hooks; durable DataStore-backed store by default (auto-migrated notification_* tables). → details |
@voltro/plugin-logship |
✓ | Ship structured logs to Better Stack / Axiom / Loki / any HTTP sink — rides the log-sink hook, batched + redacted + fail-soft, trace-correlated. → details |
@voltro/plugin-moderation |
✓ | Content moderation — keyword denylist or AI provider (fails open), block (typed ContentRejected) / flag via rpc interceptor + in-handler moderate() redact helper. → details |
@voltro/plugin-search |
✓ | External search index sync — rides the ChangeEvent tap to mirror tables into Typesense / Meilisearch / Algolia (memory default), tenant-scoped search.query action (facets · highlighting · fuzziness · range/negation filters · engine-param passthrough) + useSearch hook + backfillIndex + durable cross-replica sync stats. → details |
@voltro/plugin-cdc-out |
◐ | Declarative reverse-ETL — mirror table changes outward to external sinks (webhook / Kafka / Snowflake / BigQuery) through a durable outbox; ordered per pipe, at-least-once from enqueue, retried with backoff, dead-lettered. Engine + memory/webhook sinks shipped; warehouse connectors implement the CdcSink interface. |
@voltro/plugin-governance |
✓ | Data governance — retention TTL sweep (delete / anonymise), GDPR subject export + erasure (admin-gated routes + GovernanceService), consent ledger, field encryption. → details |
@voltro/plugin-openapi |
✓ | OpenAPI 3.1 spec (GET /openapi.json) + Swagger-UI (GET /docs) generated from defineRestRoute descriptors AND (opt-in) rpc procedures (queries/mutations/actions/streams → POST /rpc/<name>) — input/output/error Schemas via JSONSchema.make. → details |
@voltro/plugin-versioning |
✓ | Full row history + time-travel — value snapshot of every insert/update/delete on listed tables into _voltro_row_history (rides the ChangeEvent tap); rowHistory / rowAsOf queries + restoreAsOf / diffVersions; TTL + per-row cap retention. → details |
@voltro/plugin-presence |
✓ | Ephemeral realtime presence — heartbeat roster per channel (presence.heartbeat/list/leave + usePresence), a useTyping typing indicator, swept _voltro_presence table, cross-instance. → details |
@voltro/plugin-scim |
✓ | SCIM 2.0 provisioning — Users + Groups REST at /scim/v2 (bearer-gated) incl. group-membership PATCH/PUT + the RFC 7644 discovery trio (ServiceProviderConfig/Schemas/ResourceTypes), userName/externalId/displayName eq filters, pagination, unique userName, active:false deactivation; scim_users/scim_groups. → details |
@voltro/plugin-sso-saml |
✓ | Enterprise SAML 2.0 SSO — SP-initiated login + Single Logout (both directions) + ACS + SP metadata under /saml; IdP-metadata-URL auto cert rotation, encrypted assertions, clock-skew, SP request signing. Signature verify via @node-saml/node-saml (optional+lazy), mints a framework session. → details |
API keys are first-class (not a plugin): apiKeys: true in app.config.ts → Bearer-key auth + admin-gated /v1/api-keys management, hash-only storage. → details
Every plugin carries a design doc in the framework's plans/ directory before it ships.
Configuration shape
// app.config.ts
import { auditPlugin } from '@voltro/plugin-audit'
import { rateLimitPlugin } from '@voltro/plugin-ratelimit'
export default {
type: 'api' as const,
name: 'api',
plugins: [
rateLimitPlugin({ default: { limit: 60, window: '1m' } }),
auditPlugin({ sink: 'console' }),
],
}Order matters: the framework composes outer→inner, so the rate-limit interceptor runs before the audit interceptor sees the request. Rejected requests never enter the audit log.
What plugins can do
| Surface | What it lets you do |
|---|---|
interceptMutation / interceptQuery / interceptAction |
Wrap every mutation / query-setup / action — gate, audit, transform input/output. |
extendSchema |
Contribute tables + custom SQL migrations (tracked in _voltro_plugin_migrations). |
services |
Provide an Effect Layer whose Tags every handler can yield* (e.g. MailService, StorageService). |
routes |
Register plugin-owned rpc queries / mutations / actions (alias-prefixed tags). |
httpRoutes |
Serve public raw-HTTP endpoints on the framework listener (e.g. GET /_voltro/storage/:id). |
inspectEndpoints |
Mount tooling under /_voltro/inspect/plugins/<alias>/…. |
onScheduleFire / onWorkflowStep / onHttpRequest |
Wrap every cron firing, every workflow step(), every pre-auth HTTP request. |
onInstall / onActivate / onDeactivate / onUninstall |
Lifecycle hooks at first-install, boot, shutdown, and removal. |
Schema mixins (defineMixin) |
The OTHER plugin shape — audit(), tenant(), softDelete() — declared in @voltro/database, not via the runtime contract. |
When NOT to write a plugin
- One-off side effect — just call it from the mutation directly.
- App-specific behaviour — keep it in app code, not a reusable plugin.
- Anything cross-cutting that only affects ONE mutation — a single
await ctx.audit.log(...)call beats a plugin's hook.
Plugins are for cross-cutting concerns. Audit-log every write, rate-limit every mutation, send a user.created event from every sign-up: that's plugin territory.
Where to read next
- The plugin contract — write your own
- plugin-audit — most complete reference implementation