Plugin publishing guide

The marketplace uses GitHub distribution: plugin bytes live entirely in your
own GitHub repository, and the registry records one entry pointing at it.
Shipping a new version needs nothing but a tag — no PR per release.

1. Create a repo from the template

Generate your plugin repo from
plugin-template (one
plugin = one repo). Once the plugin works, make sure the root has
manifest.json and the entry file main.js:

{
  "id": "example.my-plugin",
  "name": "My Plugin",
  "version": "0.1.0",
  "author": "Your name",
  "description": "One sentence about what the plugin does",
  "main": "main.js",
  "trust": "safe",
  "permissions": ["canvas.registerNodeDef"],
  "categories": ["node"]
}
  • id: lowercase letters/digits/dots/hyphens; immutable after listing, and
    part of the install archive name
  • version: semver (e.g. 0.1.0), bumped on every release
  • trust: safe (canvas only) / trusted (includes nodeEntry — main-process
    access, reviewed more strictly)

2. Tag, and the release packs itself

Pushing a v* tag triggers the template's release workflow, which zips the
built manifest.json and main.js, recomputes the manifest integrity field,
and uploads three assets:

git tag v0.1.0 && git push origin v0.1.0
  • <id>-<version>.zip — versioned archive (for pinned entries)
  • <id>.zip — versionless alias, always the latest stable release
  • manifest.json — versionless alias, used by the desktop app to detect updates

Packing recomputes the SHA-256 of every entry file and injects integrity;
the desktop app enforces it at install time. The release must be a full
release (not a draft or prerelease) or releases/latest will not see it.

3. Open a PR against the registry

Open a PR against poly-creator/plugins
adding one entry to plugins.json:

{
  "id": "example.my-plugin",
  "name": "My Plugin",
  "author": "Your name",
  "repo": "your-user/your-plugin-repo",
  "description": "One sentence about what the plugin does"
}

CI verifies the repo exists, that the latest release carries all three assets,
and that the in-zip manifest matches the entry. Once merged, the plugin shows
up in the marketplace and in the desktop app's Market tab. After that, every
release is just a tag — the registry entry stays put.

To pin a specific version (byte-level replay protection), add both tag and
sha256 to the entry (they must come as a pair); the hash is verified on
install.

Rules

  • One plugin per repo — multi-plugin repos break the <id>.zip alias
    convention and CI rejects them
  • zip ≤ 30 MiB; path-traversal entries (../, absolute paths) are rejected
  • trusted plugins that use nodeEntry get a stricter manual review
  • Unlisting = removing the entry from plugins.json (already-installed users
    keep working)