Sign and Publish a Plugin
Build, sign, package, and submit a Zync Manifest v2 plugin to the signed community marketplace.
Publishing has two separate approvals: you sign your plugin package with your publisher key, and registry operators approve and sign its marketplace entry with a registry root. A GitHub release or successful local signature check alone does not make your plugin available in the Marketplace.
This guide follows the current
registry preparation code
and SDK 2.1.0-beta.4. The official preparation tool currently accepts stable
semantic versions without prerelease or build metadata. Read the
beta section before planning a beta launch.
Prepare a release candidate
Section titled “Prepare a release candidate”- Choose your publisher namespace and plugin ID. Keep them stable across releases.
- Set the plugin version and tested
engines.zync/engines.pluginApiranges. - Build from a clean checkout with locked dependencies and run your tests.
- Validate the built folder and test it in an installed Zync build, including permission denial, multiple panes, reconnects, and uninstall.
- Review every file to be shipped. Exclude keys,
.envfiles, credentials,node_modules, development mocks, and unrelated source/build output. Include your license and any third-party notices required by bundled dependencies.
For the starter from Build your first plugin:
npm cinpm run validatenpx --no-install zync-sdk validate dist --zync-version 2.34.0Stop if any command fails. Also validate against your declared minimum desktop
version and test that version in the app. --zync-version checks a compatibility
declaration, not the behavior of every API your code calls.
Generate a publisher key
Section titled “Generate a publisher key”Run the installed SDK executable explicitly. Avoid npx zync-sdk without a local
installation because it may resolve an unrelated unscoped npm package.
npx --no-install zync-sdk keygen --out /absolute/private-storage/publisher-v1Replace the path with a new directory outside Git whose parent already exists.
On Windows, for example, use a private folder such as
C:\ZyncSigningKeys\my-publisher-v1 after creating its parent. The SDK invokes
OpenSSL; install it or supply --openssl /path/to/openssl. Git for Windows’
bundled OpenSSL is also detected.
The command requires an interactive terminal. It prompts for an optional
passphrase; Enter creates an unencrypted private key. It writes
publisher-private.pem and publisher-public.pem. Keep the private file and any
passphrase out of source, build artifacts, issues, and chat. Back up the key
securely; only the public key and fingerprint go to the registry.
This key identifies your publisher. It is separate from Zync’s application-signing key and the marketplace registry root. Generating it does not register a publisher.
Sign and verify the built directory
Section titled “Sign and verify the built directory”From your plugin project, replace the private-key path below. The signed output
must not already exist and must be outside dist. Never place the key inside
the source package.
npx --no-install zync-sdk sign --source dist --key /absolute/private-storage/publisher-v1/publisher-private.pem --out signed/hello-1.0.0npx --no-install zync-sdk verify --source signed/hello-1.0.0Encrypted PEM keys prompt for their passphrase. The sign command prints a
sha256:... publisher key fingerprint. Record it and verify it independently with
the public key during publisher approval. The signed folder includes
integrity.json and signature.json. Do not modify or add files afterward.
The verifier checks all payload hashes and the package signature. Its embedded public key is not automatically an approved publisher key. Registry review separately checks the publisher, plugin, repository, and allowed key fingerprint.
Create the ZIP and checksum
Section titled “Create the ZIP and checksum”The ZIP must contain the contents of the signed folder at its root:
hello-1.0.0-signed.zip manifest.json worker.js ui/index.html ui/pane.js ui/pane.css integrity.json signature.jsonHere is a PowerShell 7 example. Run it only after the sign and verify commands succeed. Use new output paths for every candidate.
$ErrorActionPreference = 'Stop'$assetName = 'hello-1.0.0-signed.zip'if (Test-Path -LiteralPath release-check) { throw 'Use a new extraction directory' }New-Item -ItemType Directory -Path release -ErrorAction Stop | Out-NullCompress-Archive -Path signed/hello-1.0.0/* -DestinationPath "release/$assetName"$digest = (Get-FileHash -Algorithm SHA256 -LiteralPath "release/$assetName").Hash.ToLowerInvariant()[IO.File]::WriteAllText("$($PWD.Path)/release/$assetName.sha256", "$digest $assetName`n", [Text.UTF8Encoding]::new($false))Expand-Archive -LiteralPath "release/$assetName" -DestinationPath release-checknpx --no-install zync-sdk verify --source release-checkif ($LASTEXITCODE -ne 0) { throw 'Packaged signature verification failed' }Ensure release-check does not exist before extraction. The checksum asset name
is the ZIP name plus .sha256. Its contents are exactly lowercase hex SHA-256,
two spaces, and the ZIP filename, with an optional trailing newline. The registry
preparer checks this format, not an arbitrary checksum document. Hash the final
ZIP, not manifest.json or the uncompressed folder. ZIP tools may omit hidden
files; verifying the extracted archive catches an incomplete package.
On macOS/Linux, use an installed ZIP tool to archive from inside the signed folder,
then create the same checksum format with sha256sum or shasum -a 256 as
available. Extract to a fresh directory and verify that extracted copy as well.
Publish the GitHub release assets
Section titled “Publish the GitHub release assets”The current official preparer retrieves assets from GitHub releases. Commit
the tested source, create a matching version tag such as v1.0.0, and attach:
hello-1.0.0-signed.ziphello-1.0.0-signed.zip.sha256
Use your repository’s release UI or its reviewed release workflow. The tag should
identify the exact source used to build the signed package. A custom tag convention
is supported through releaseTag in the registry input. Public metadata and
assets must be downloadable by the preparer without your private credentials.
Download the released ZIP and checksum again, compare the digest, extract the
archive, and run zync-sdk verify on that copy. Install and exercise that exact
candidate locally. Do not replace a published version with different bytes.
Request publisher approval
Section titled “Request publisher approval”Open an issue or pull request in zync-plugin-registry with:
- Publisher namespace, plugin ID, source repository, and a maintainer contact.
- Public Ed25519 PEM key and the
sha256:...fingerprint printed by the signer. - Source tag/commit, signed ZIP URL, checksum URL, and release notes.
- Supported Zync versions/platforms, test results, screenshots, and permission explanations, especially for remote commands, file writes, and network access.
- License, bundled dependency notices, and a privacy explanation if data leaves the device or remote server.
Maintainers review ownership and record the allowed binding in
approved-publishers.json. A proposed entry looks like this; it is not self-approval:
{ "publisher": "dev.example", "pluginId": "dev.example.hello", "repository": "YOUR_ACCOUNT/hello-zync", "keyIds": ["sha256:REPLACE_WITH_THE_SIGNER_FINGERPRINT"]}Submit the public key for independent comparison. Never submit a private key or request the registry root. Approval for one plugin/repository does not automatically approve every package using the same publisher name.
Propose the release entry
Section titled “Propose the release entry”Add a release to registry-input.json in the same registry repository. Preserve
earlier releases and cumulative revocations. This example is an entry inside
releases[], not a replacement for the entire input file:
{ "pluginId": "dev.example.hello", "publisher": "dev.example", "repository": "YOUR_ACCOUNT/hello-zync", "version": "1.0.0", "assetName": "hello-1.0.0-signed.zip", "channel": "stable", "publisherVerified": false}The default tag is v<version>. Add "releaseTag": "hello-v1.0.0" only if your
release actually uses that convention. Optional thumbnailUrl must be HTTPS.
Use a reviewed, stable image URL. Leave publisherVerified false unless registry
maintainers authorize that identity label; a valid package signature alone does
not confer it.
For local preparation, install Rust, clone your registry fork, and run from that checkout with a fresh output directory:
cargo run --locked -- prepare --input registry-input.json --approvals approved-publishers.json --output prepared-registryThis downloads the input releases, checks ZIP checksums, verifies package contents and signatures against approved keys, and creates an unsigned preparation bundle. It never executes the plugins. It is not a publication command and needs no private signing key. A failed run can leave partial output; retry in a new directory.
Registry operator publication
Section titled “Registry operator publication”After review and merge, the repository’s Publish signed registry workflow
prepares and verifies packages, signs metadata using its protected root, verifies
the result, and commits the generated registry.json. It also runs daily to
refresh the seven-day expiry. Registry versions increase independently of plugin
versions. The published metadata must retain previous releases and revocations.
Publishers do not manually edit the generated signature or receive the root. Operator setup, root recovery, and rotation belong in the registry operations runbook.
Finally, refresh Marketplace in an installed Zync build configured for the official registry. Confirm the correct version/channel, approve permissions, install, exercise the plugin, and inspect its details. Announce marketplace availability only after this succeeds. A green plugin release workflow is an earlier step.
Updates, rollback, and key changes
Section titled “Updates, rollback, and key changes”For every update, increase the plugin version, rebuild, validate, test, sign, and publish new immutable assets; then append a registry release entry. Explain added or changed permissions. Zync reviews those changes before activation and checks compatibility with the host version.
Marketplace updates cannot silently downgrade an installed version or replace the same semantic version with different bytes. The user’s explicit rollback action uses a retained version when available and rechecks its eligibility. Keep private storage migrations backward compatible because rollback does not revert arbitrary data migrations or remote side effects.
For key rotation, request review of the new public fingerprint before releasing packages under it. Keep prior approvals needed by retained releases. For a compromised key or release, contact registry maintainers promptly; cumulative revocations, not overwriting ZIP files, provide the containment mechanism.
Beta releases and version selection
Section titled “Beta releases and version selection”The desktop can select per-plugin stable/beta releases from compatible signed
metadata. The current official Rust prepare command rejects prerelease and
build-metadata versions. Do not submit 1.1.0-beta.1 to that workflow expecting
it to pass. Coordinate a supported registry process with maintainers first, or
distribute a clearly marked local-development test package to willing testers.
SDK npm beta versions are unrelated to plugin marketplace channels. On clients using compatible beta metadata, disabling beta does not automatically downgrade an installed beta; a newer stable release or explicit eligible rollback is needed.
CI and common failures
Section titled “CI and common failures”Use separate jobs for untrusted PR checks, building the candidate, signing, and publishing. Protect signing secrets and release environments. Give the publishing job only the verified artifacts and narrowly scoped release access. Do not run unreviewed plugin code after exposing a signing key. The SDK interactive CLI cannot prompt in headless CI; encrypted-key automation needs a reviewed signing integration that handles its passphrase securely. Do not put it in command arguments.
| Failure | Resolution |
|---|---|
| Unknown publisher/key | Obtain approval for the exact publisher, plugin, repository, and key fingerprint |
| Release checksum mismatch | Hash the final ZIP; use lowercase hex, two spaces, and the exact asset filename |
| Package identity mismatch | Align manifest ID, publisher, version, registry entry, and release assets |
| Signature/integrity failure | Rebuild and sign a fresh candidate; do not edit signed output |
| Stable versions only | Use a stable version or coordinate a separate beta preparation path |
| Output already exists | Choose a fresh signing/preparation/extraction directory |
| Marketplace unavailable | Check host registry configuration and metadata expiry with operators |
| Installed plugin will not update | Check engine ranges, channel choice, version ordering, permissions, and revocations |
See best practices and the release checklist before submitting.