Skip to main content
“Plugins” in Pylon means two concrete things — and neither is a manifest.plugins: [...] list. There is no such array today.
  1. TS packages (@pylonsync/*) — installable npm packages that return a manifest fragment (entities, actions, queries, policies) plus handler factories. You compose the fragment into your buildManifest() call. Use these to add features the framework binary doesn’t already cover.
  2. Built-in runtime plugins — a small set compiled into the pylon binary and wired automatically from your schema or environment. You don’t name them anywhere; they activate when the relevant signal is present (a tenantId field, a .owner() annotation, an env var).
Features that are built into the framework binary already (no package, no plugin needed):
  • API keys/api/auth/api-keys routes for mint / list / revoke. See Auth → API keys.
  • TOTP / 2FA/api/auth/totp/* routes for enroll / verify / disable + backup codes. See Auth → TOTP.
  • Transactional emailPYLON_EMAIL_PROVIDER env (sendgrid / resend / stack0 / webhook) + ctx.email.send(). See Integrations → email.
  • Audit log/api/auth/audit + /api/auth/audit/tenant routes.
  • OrganizationsOrg / OrgMember / OrgInvite entities + /api/auth/orgs/* routes. See Auth → Organizations.

TS packages

Install with bun add, then spread the manifest fragment into buildManifest():
PackagePurpose
@pylonsync/stripeDeclarative billing — plans, checkout, portal, cancel/restore, signature-verified webhook, subscription entity.
@pylonsync/feature-flagsLocal-eval flags — boolean + multivariate, percentage rollouts, targeting predicates, per-variant JSON payloads.
@pylonsync/webhooksOutbound webhook delivery — Svix-compatible HMAC signatures, retry schedule, secret rotation.
Each package exports a factory (e.g. stripe({...}), webhooks({...})) that returns { manifest, handlers, ... }. Pylon loads function handlers one-per-file, so you also add one-line wrapper files under functions/ — see each package’s page for the exact list.

Built-in runtime plugins

These live in crates/plugin/src/builtin/ and are registered by the runtime at boot. They are auto-wired, not enabled through a manifest list.
Built-inHow it turns onReference
tenant_scopeAutomatic on any entity with a tenantId fieldData hygiene
owner_stampAutomatic on any field declared field.X().owner()Data hygiene
rate_limitAlways on; tuned via PYLON_RATE_LIMIT_MAX* envSecurity
csrfAlways on; allowlist from manifest.auth.trustedOrigins / envSecurity
LLM proxy (ai_proxy)Env-configured; powers /api/llm/complete, /api/ai/stream, ctx.llmSearch & AI
In-process cacheInternal; backs SSR / ISR response caching

How built-ins hook in

Built-ins observe the request and data path at fixed points. The exact Rust hooks (see Writing your own):
HTTP request


   on_request ──── rate_limit (reject if over budget)
     │       ──── csrf (origin check on cookie-authed writes)


   entity write (insert / update / delete)
     │   ──── before_insert / before_update / before_delete
     │        (tenant_scope injects + validates tenantId;
     │         owner_stamp stamps ownerId from the session)


   after_insert / after_update / after_delete
A built-in can inspect or reject a request (on_request), mutate or reject a write (before_*), react after a write lands (after_*), add HTTP routes (routes()), or contribute manifest entities (entities()).

There is no plugins: [] array

Older drafts of these docs described enabling plugins through a pylon.manifest.json plugins list with per-plugin config. That mechanism does not exist. Built-ins are automatic; you influence them through your schema (add a tenantId field, annotate a field .owner()) and environment variables. App-specific cross-cutting logic goes in server functions and policies, not a plugin config block. pylon plugins list prints a roadmap catalog of plugin names (validation, slugify, versioning, cascade, MCP, and more). These are planned, not installable today — treat the list as a preview, not an API.

Plugins vs functions

Plugins / built-insFunctions
Implicit — fire on every relevant opExplicit — called by name
Auto-wired from schema/env, or composed from a TS packageDefined in TS files under functions/
Can mutate data in-flight (built-ins)Run inside a transaction; return a result
Cross-cutting concerns (tenant scope, owner stamp, rate limit)Business logic
Compiled into the binary, or shipped as a packageHot-reloaded by the dev server
Use a built-in when the behavior should apply to every call. Use a function when callers opt in by name.

Writing your own

There is no dynamic plugin loading yet. To add a Rust built-in you build a custom runtime that registers it; for TypeScript-side logic, use functions and policies. The SDK ships a definePlugin({ name, entities, hooks }) helper, but the runtime does not consume its output yet. See Writing your own plugin for the real Plugin trait and the honest state of extensibility.