Building Plugins
Developer guide to the Pomavo plugin system: the SDK, the manifest, the sandboxed UI bridge, and the lifecycle of an installable plugin.
Overview
A Pomavo plugin is a small, self-hosted web service that the host application talks to over a well-defined contract. Plugins extend Pomavo without touching the core: they add widgets, which are UI panels embedded in the app, themes, and integrations, and they run completely isolated from the host.
Everything a plugin needs is provided by the @pomavo/sdk package, which ships two halves:
Client SDK
@pomavo/sdk/client - the PomavoBridge that connects your widget's sandboxed iframe to the host: context, token, settings, theme, and auto-resize.
Server SDK
@pomavo/sdk/server - the PomavoClient API wrapper, lifecycle handlers, and token verification for your plugin's backend.
Anatomy of a Plugin
A typical plugin is an Express or another Node HTTP server that exposes a handful of endpoints and serves one or more widget bundles:
my-plugin/
├── manifest.json # id, widgets, params, lifecycle
├── src/
│ ├── index.ts # HTTP server entry
│ ├── routes/
│ │ ├── lifecycle.ts # install / uninstall / update
│ │ ├── api.ts # data endpoints (token-guarded)
│ │ └── widgets.ts # serves widget bundles
│ └── widgets/
│ └── MyWidget.tsx # React UI for the iframe
└── package.jsonThe host never runs your code directly. Instead:
The host sends lifecycle events
When an organization installs your plugin, Pomavo POSTs an installed event to your lifecycle.url, carrying an installationId and a sharedSecret you store and use to verify future requests.
The host embeds your widgets
Each widget you declare in the manifest is loaded into a sandboxed iframe at the URL you provide. The host hands the iframe a short-lived token and the current context, such as the ticket being viewed.
Your widget calls your backend
The widget fetches your own API endpoints, passing the host token as a Bearer credential. Your backend verifies the token with the shared secret and resolves the org/user/ticket from it.
Your backend can call Pomavo
If your plugin needs to read or write Pomavo data, it uses the PomavoClient with an integration API key delivered during installation.
Install the SDK
npm install @pomavo/sdkThe package exposes two entry points so the browser bundle never pulls in server-only code:
// In your widget (browser)
import { PomavoBridge } from '@pomavo/sdk/client';
// In your backend (Node)
import {
PomavoClient,
createLifecycleHandler,
createTokenVerifier,
verifyPluginToken,
} from '@pomavo/sdk/server';The Security Model
Plugins are designed so that installing one can never compromise the rest of Pomavo:
- Sandboxed UI - widget code runs in an isolated iframe (
allow-scripts allow-same-origin allow-popups). It cannot read the host DOM, cookies, or session. - Explicit context - a widget only receives what the host chooses to pass through the bridge: a token, its saved settings, and the current ticket.
- Signed tokens - the host token is an HS256 JWT signed with your per-installation
sharedSecret, so your backend can trust the org/user it names. - Scoped integrations - Pomavo API access is granted through integration API keys tied to the installing organization and the permissions your manifest requests.
- Per-organization installs - a plugin is only active for organizations that install it, and uninstalling fully revokes its widgets and keys.
The first-party GitHub plugin is built entirely on this system. See the end-to-end example for a complete, working plugin.
Next Steps
The Manifest
Declare your plugin: id, widgets, parameters, theme, and the resources it requests.
Lifecycle & Auth
Handle install/uninstall/update events and verify host tokens on your backend.
Plugin UI
Use the PomavoBridge to receive context, react to themes, and auto-size your iframe.
Server SDK
Read and write Pomavo data with the typed PomavoClient.