fayzfayz sdk
PTEN

Plugin manifest

A plugin describes itself entirely through one object: the PluginManifest. It declares what the plugin is — navigation, routes, widgets, data, permissions — and the runtime does the rest. Plugins declare; core resolves. This page is the tour of the fields; the exhaustive reference is in plugin manifest reference.

The required core

Four fields identify any plugin, plus the two seams that give it a place in the UI:

{
  id: 'loyalty',
  name: 'Loyalty',
  icon: 'Puzzle',
  version: '0.1.0',
  navigation: [
    { section: 'main', position: 50, label: 'Loyalty', route: '/loyalty', icon: 'Puzzle' },
  ],
  routes: [{ path: '/loyalty', component: Page }],
}
  • id — the plugin's unique identifier.
  • name / icon / version — label, icon and version.
  • navigation — the menu items (see Routing and navigation).
  • routes — the path → component mapping.

A component can always be given directly (component) or by registry id (componentId) — exactly one of the two.

The extension seams

Beyond the core, the manifest has a family of optional fields — each one a "seam" through which the plugin extends the platform without touching any other plugin:

SeamWhat it declares
settingsTabs on the plugin's settings screen.
widgetsComponents injected into named zones of the shell (WidgetZone).
dashboardWidgetsKPIs, charts and tables the plugin contributes to the dashboard.
eventsEvents the plugin emits on the bus (agenda.booking.confirmed, …).
aiToolsTools an AI agent can call (read or persist mode).
capabilitiesThe plugin's declared capabilities (for introspection).
entitiesData entities the plugin registers for CRUD.
permissions / declaredFeaturesFeatures and actions for access control.
connectorsConnectors for external providers (the plugin as an add-on to another).
migrationsThe SQL for the tables the plugin owns (plg_ convention), each with tenant_id + RLS.
diagnosticsBackend prerequisites that fayz doctor checks (RPCs, views, tables, env).
onboardingA first-run flow.
localesTranslations (en, pt-BR, …).

You declare only what you use. A minimal plugin — like the one the incubator generates — ships id, name, icon, version, navigation and routes, and nothing else.

apiVersion: the contract is versioned

The optional apiVersion field pins the plugin contract version the manifest was written against. The runtime refuses a plugin built for a contract newer than it supports — that's the mechanism that stops a plugin from a future generation from running half-broken on an older platform. Omitting it means "compatible/legacy".

Why declarative

The manifest being data (and not imperative code) is what gives you the three properties the platform rests on:

  • Introspection — the platform knows exactly what a plugin contributes without executing it; that's how fayz doctor checks prerequisites and the editor builds its pickers.
  • Symmetry — an app-local plugin and an official one use the same type, so promoting one is packaging, not rewriting.
  • Upgrade safety — the seams are a versioned contract; the implementation behind them can change freely.

Next: the conventions that keep a plugin well-behaved, in Patterns.