Skip to Content
Customising & extendingPublishing a plugin

Publishing a plugin

You have a plugin that works locally. This page takes it from there to installable by someone else, and explains what each step actually guarantees.

If you have not built one yet, start with Writing a plugin.

Publishing needs a hosted build and an account. The registry does not exist in the open-source local edition — see the edition table.

The shape of it

Claim a namespace

Your plugin’s id is reverse-DNS: studio.acme.easing-lab. The acme part is a namespace, and it is yours — nobody else can publish under it.

In the app, the publisher shelf is in the Plugins panel. It asks for a namespace (acme) and a display name (Acme Studio), then Register namespace. The same shelf is on the web at Dashboard → Plugins → Mine.

Signed out, the shelf simply says “Sign in to publish plugins.” — publishing is the only part of the plugin system that needs an account. Installing and running plugins does not.

Write the listing

Edit listing on the plugin’s row: a README (Markdown), a licence, links.

This metadata lives outside the signed package on purpose — fixing a typo in your description should not mean re-signing and re-publishing your code. Save it with Save listing.

Make a signing key

A signing key is a file you make, not something the registry issues you. There is nothing to find beforehand.

In the app, hit publish and choose Create a new key… — it asks where to save one, makes it, and signs with it immediately. From a terminal:

node scripts/sign-plugin.mjs keygen --out ./my-plugin.key.json

Both produce the same thing: P-256, { privateKey, publicKey } as base64 PKCS8 and SPKI. You can start in the app and script it later, or the other way round.

Publish it

Choose signing key and publish on the plugin’s row, then Use an existing key… and pick your key file.

From a terminal, for scripted releases:

node scripts/sign-plugin.mjs publish my-plugin.zip --key ./my-plugin.key.json --token <access token>

What goes over the wire is the package, the signature, and the public key. Nothing else.

Choose who can see it

The row shows a Private or Public badge, and one button flips it: Make public / Make private.

Publish privately first. It is the only way to test the real install path — download, signature check, package reader, consent screen — as a user experiences it, rather than as a folder install you already trust.

It goes through review

Packages are scanned at publish time and a reviewer queue sits behind the listings. The scan is advisory; the queue is a person.

The signing key is your identity — treat it that way

This is the part worth reading twice, because the failure is unrecoverable.

The registry pins your key to your plugin id the first time you publish (trust on first use). Every later version must verify against that same key. That is what lets a user’s editor know an update came from you rather than from whoever got into your account.

IfThen
You lose the keyThe plugin must be republished under a new id. Your installs do not follow
Someone else gets the keyThey can publish as you
You want to change keys laterIt needs your account password, and it prompts every user who already has the plugin

Register a backup key on your first publish. Do it while you have no install base, where it costs nothing and interrupts nobody. Authorising one later is the expensive version of the same action.

Premation never stores your private key, and deliberately does not offer to remember it in your OS keychain. Anything running as you could then publish as you, which is precisely the compromise the signing model exists to survive. You are asked for the key file each time.

This is also why the web dashboard cannot publish for you. A browser upload that asked for your private key would defeat the whole guarantee — so the dashboard prints the commands with your own values already filled in, which is the most a browser can honestly do.

Versions are immutable

Re-publishing an existing version number is refused. Two different sets of bytes both claiming to be 1.2.0 would make the signature guarantee unusable — a user could not tell which one they verified.

Ship 1.2.1. There is no force flag.

Shipping an update

Publish a new version the same way. Then:

  • Users see it when they open the manager. Update checks never run on a timer or in the background.
  • An update that asks for more permissions than were granted goes back through the consent screen. It cannot quietly widen its own access.
  • An update that asks for the same or fewer permissions installs on request without re-consent.

Because apiVersion and requires answer different questions, adding a host method your plugin calls does not mean bumping apiVersion — see the two version numbers.

Taking it down

Two different actions, and the app pushes you at the reversible one first.

ActionWhat it does
Make privateStops new public downloads. Reversible, and the listing and versions stay
Withdraw permanentlyDeletes the listing and every published version from the registry. Not reversible

Installed copies keep working either way. Someone in the middle of a project does not lose their afternoon because you withdrew a plugin — breaking their file is usually a bigger harm than the one a takedown addresses.

If you only want to stop new downloads, use Make private. Withdrawing is the one action here you cannot undo, and because published versions are immutable you cannot re-publish the same version numbers afterwards either.

An operator takedown works differently from your own withdrawal: it blocks the package rather than deleting it, and everyone running it is told in the plugin’s row. A notice marked malicious cannot be dismissed unread.

What a user actually sees

Worth knowing, because it is what your consent screen is competing with.

They find it

In the app under Plugins ▸ Manage Plugins… ▸ Browse, or on the public listing at premation.com/plugins.

They install it

The download is verified, then parsed by the same package reader a local file goes through, then shown on the same consent screen. A registry install is not a shortcut past the permission screen.

They read your permissions

Every permission your manifest asks for, each one tickable. They can untick any of it and install anyway — so write for a refusal:

if (await motion.has('scene:write')) { // offer the feature } else { // hide it, and say why }

A grant is always intersected with the manifest, so nothing can end up with more than you disclosed.

They use it

Your commands appear in the Plugins menu under your plugin’s name and in the command palette. Effects appear in a folder named after your plugin. Layer types appear in Layer ▸ New.

Asking for less gets you installed more

The consent screen is the list, verbatim. Two examples where the narrow ask exists specifically so you do not have to frighten people:

  • Building a subtree under your own layer type? Ask for scene:read + scene:proxy, not scene:write. The wide one makes your screen say “create, change, delete and reparent layers” — indistinguishable from a plugin that could rearrange someone’s whole project.
  • Need the network? Declare the exact hosts. There is deliberately no runtime host allowlist and no way to fetch a URL handed to you at runtime — a plugin that could be given an arbitrary URL has consent for “contact the internet” no matter what the screen said.

What the registry deliberately does not do

Settled decisions, listed so you do not plan around them:

  • No ratings, comments or curation. It lists what was published. The only public number is a deduplicated install count, and nothing is ranked on it.
  • No automatic blocking on a report threshold. Reports need no account, so a count that blocks would be a takedown button handed to anyone who can make the count go up. Cases escalate to a human; nothing else.
  • No plugin-to-plugin communication. One worker and one frame each. Two plugins that can talk are two plugins whose combined permissions are the union of what was granted separately — which is not what either consent screen said.
Last updated on