Plugin iframe host protocol¶
Same-origin (or allowlisted) plugin tools render inside /plugins/[id] and talk to Prompt Studio via postMessage.
Channel string (required on every message):
Source of truth: src/lib/plugin-iframe-host.ts. Host page: src/app/plugins/[id]/page.tsx.
Origin security (default-deny)¶
- Messages are accepted only when
event.originmatches the iframe URL origin, or an origin listed under Plugins → Iframe origin allowlist. - Unresolvable / wildcard iframe targets are denied unless the posting origin is explicitly allowlisted.
- Same-origin relative
iframeUrlvalues (starting with/) use the Studio page origin.
Recommended remote plugin host headers:
Content-Security-Policy: frame-ancestors 'self' https://your-studio.example
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
The host iframe uses:
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-downloads"
referrerPolicy="strict-origin-when-cross-origin"
allow="clipboard-write"
Do not add allow-top-navigation unless you fully trust the plugin. Prefer plugin:navigate for in-app routes.
Host → plugin¶
type |
Purpose |
|---|---|
host:ready |
Iframe loaded; safe to send plugin messages |
host:context |
Snapshot: plugin id, model, tool, prompt, quality profile, engine, active LoRA ids, selected workflow id |
host:queue-result |
Outcome of a plugin:queue request (ok, message, optional promptId) |
host:apply-result |
Outcome of apply-* / patch / tag requests |
Plugin → host¶
type |
Purpose |
|---|---|
plugin:resize |
{ height } — grow the host frame |
plugin:navigate |
{ href } — in-app navigation (same origin paths) |
plugin:toast |
{ message } — tray toast |
plugin:apply-prompt |
{ prompt, negativePrompt? } — push text into Studio without queueing |
plugin:apply-model |
{ model } — set shared target model |
plugin:apply-quality |
{ qualityProfile: draft\|final\|max\|followSettings } |
plugin:apply-engine |
{ engine } — switch inference engine (comfyui, fal, replicate, …) |
plugin:apply-lora-stack |
{ loraIds: string[], model? } — set session LoRA stack for the active (or named) model |
plugin:patch-workflow-tokens |
{ tokens: [{ token, value }] } — merge into Comfy custom workflow tokens |
plugin:write-gallery-tag |
{ tag, entryIds?, mode?: add\|replace\|remove } — tag gallery entries (defaults to latest) |
plugin:pick-gallery |
{ target? } — open Gallery pick mode for compose/refine/controlnet/… |
plugin:queue |
{ prompt, negativePrompt?, model?, denoise?, cfg?, qualityProfile? } — run host queue path |
Minimal example¶
Serve any HTML from this app (same origin) and point a plugin tool iframeUrl at it, e.g. /plugin-examples/hello-iframe.html.
const CHANNEL = 'comfyui-prompt-studio-plugin-host';
window.addEventListener('message', event => {
const data = event.data;
if (!data || data.channel !== CHANNEL) return;
if (data.type === 'host:ready') {
// Prefer posting back to event.origin instead of '*'.
// window.parent.postMessage({ channel: CHANNEL, type: 'plugin:resize', height: 420 }, event.origin);
}
if (data.type === 'host:queue-result') {
console.log(data.ok ? 'queued' : 'failed', data.message, data.promptId);
}
});
window.parent.postMessage(
{
channel: CHANNEL,
type: 'plugin:queue',
prompt: 'a cat sitting on a windowsill, soft daylight',
},
window.location.origin
);
// Richer host controls
window.parent.postMessage(
{ channel: CHANNEL, type: 'plugin:apply-lora-stack', loraIds: ['skin', 'pose'] },
window.location.origin
);
window.parent.postMessage(
{
channel: CHANNEL,
type: 'plugin:patch-workflow-tokens',
tokens: [{ token: 'MY_TOKEN', value: 'hello' }],
},
window.location.origin
);
window.parent.postMessage(
{ channel: CHANNEL, type: 'plugin:write-gallery-tag', tag: 'from-plugin' },
window.location.origin
);
window.parent.postMessage(
{ channel: CHANNEL, type: 'plugin:apply-engine', engine: 'comfyui' },
window.location.origin
);
Queue requests still run Studio preflight, browser plugin queue hooks, and gallery registration on the host side. When PROMPT_DATA_DIR is set, server plugins under {PROMPT_DATA_DIR}/plugins also run privileged queue-preflight / queue-post hooks inside the Comfy /api/comfyui path (allowlisted prompt / params / workflow JSON rewrite). See architecture.md and GET/POST /api/plugins/server.
Server plugin registry (brief)¶
| Item | Detail |
|---|---|
| Path | {PROMPT_DATA_DIR}/plugins/{id}/manifest.json |
| Install | POST /api/plugins/server with { url } / { manifest } or multipart ZIP/JSON |
| HMAC | Optional PROMPT_PLUGIN_HMAC_SECRET — require X-Prompt-Plugin-Signature (hex HMAC-SHA256 of body) |
| ACL | Feature plugins; install/remove requires admin when auth is on |
| Bookmarks | Unchanged — tool-plugin-registry stays href-only and separate |
Example hook: POST /api/plugin-hooks/denoise-rewrite (shipped with examples/queue-rewrite-plugin.json).