Sandboxed by default
Every Power-Up runs in a sandboxed iframe with no same-origin escape. Calls to the host are HMAC-signed over a dedicated MessageChannel.
Extend Planoda with iframe-sandboxed Power-Ups that read and write issues, boards, and comments. Typed RPC, signed tokens, marketplace distribution — ship in under an hour.
Every Power-Up runs in a sandboxed iframe with no same-origin escape. Calls to the host are HMAC-signed over a dedicated MessageChannel.
Read issues, write comments, send notifications — all through a single `call(op, args)` surface with full TypeScript types.
Add buttons to cards and boards, tabs to the issue detail, widgets to the right rail, or actions to the command palette.
Run pnpm exec tsx scripts/create-plugin.ts my-plugin to generate a working starter — manifest, HTML host, RPC client included.
The manifest is the contract you ship with your plugin. It declares which extension points you render and which capabilities you need. Capabilities are the user-facing scopes shown at install time.
{
"id": "com.example.swimlane-counter",
"name": "Swimlane Counter",
"version": "1.0.0",
"description": "Show a live count + WIP limit per column.",
"author": { "name": "Acme Plugins", "url": "https://acme.dev" },
"icon": "https://cdn.acme.dev/icons/swimlane.svg",
"capabilities": ["board.read", "issue.read"],
"extensionPoints": [
{
"point": "right-rail-widget",
"entry": "dist/index.html",
"title": "Swimlane Counter"
}
],
"scopes": ["board:read", "issue:read"],
"category": "reporting"
}Inside the iframe, the SDK exposes a typed call(op, args) surface. Every request is HMAC-signed with your install secret and travels over a dedicated MessageChannel — there is no wildcard postMessage anywhere in the protocol.
import { createPluginHost } from "@planoda/plugin-sdk";
// Inside your plugin iframe — the host transfers a MessagePort on load.
const port = await waitForHandshake();
// Issue a typed RPC over the channel.
const board = await call("board.get", { boardId: ctx.boardId });
const issues = await call("issue.list", { boardId: ctx.boardId });
document.querySelector("#count")!.textContent =
`${issues.length} / ${board.wipLimit ?? "∞"}`;pnpm exec tsx scripts/create-plugin.ts my-pluginGenerates a working template with manifest, HTML host, RPC client, and a README. Drop it in any repo and pnpm dev to iterate.
Submit your manifest from the workspace marketplace (Settings → Plugins → Publish). The first time you publish, you get a draft listing visible only to your workspace; submit for review to ship to the public catalog.
The Getting Started guide walks you through your first plugin in under 30 minutes — including signing, scopes, and shipping to the marketplace.