Publish a package
This workflow starts after the plugin or theme has passed its local tests. Submit the same artifact that you verified locally; do not rebuild it during review.
Step 1: Generate and register a publisher key
Section titled “Step 1: Generate and register a publisher key”Generate an Ed25519 key pair once for the publisher:
zcms keygen --out ./keysThis creates publisher-private.pem with mode 0600 and publisher-public.pem. Back up the private key securely; never commit, upload or paste it into a web form.
Open Developer Portal → Publisher and register:
- A slug of 3–40 lowercase letters, digits or hyphens, without a leading or trailing hyphen.
- The display name.
- An optional contact email.
- The complete contents of
publisher-public.pem.
The public key may belong to only one publisher. If a private key is ever pasted or exposed, treat it as compromised and generate a new pair.
Step 2: Prepare the listing
Section titled “Step 2: Prepare the listing”The public listing is built from the signed manifest. Complete the following before packing:
- Reverse-DNS
id, displayname, semanticversion,descriptionandauthor. - Compatible Z-CMS range in
engine. - Built
entry, normallydist/index.js. - Plugin
permissions, capabilities and settings, or theme templates and settings. - Up to three screenshots in
media.screenshots; each must be PNG, JPEG or WebP, at most 2 MB and 4096 px per side. - An optional external HTTPS video URL in
media.video.
An update that adds permissions or changes data processing must call out the change explicitly. Do not hide a permission increase in a general changelog entry.
Step 3: Produce one release artifact
Section titled “Step 3: Produce one release artifact”Build from a clean checkout using the committed lockfile and the toolchain version documented by the project.
-
Run typecheck, lint and unit tests.
-
Confirm that the manifest version and built entry are correct.
-
Pack the built directory with the publisher key:
Terminal window zcms pack ./path/to/built-package --kind plugin \--key ./keys/publisher-private.pem \--pub ./keys/publisher-public.pem \--out ./release/package.zcmsUse
--kind themefor a theme. -
Record the checksum printed by
zcms pack. -
Verify the publisher signature:
Terminal window zcms verify ./release/package.zcms -
Pack the same built directory again when checking reproducibility; the CLI sorts files and zeroes archive timestamps, so the checksum should match.
If the checksums differ, find and remove nondeterministic inputs such as timestamps, unordered file lists or unpinned dependencies before submitting.
Step 4: Upload and submit
Section titled “Step 4: Upload and submit”Open Developer Portal → Submit a package, choose the .zcms file and select Submit for review. The upload limit is 20 MB, and each developer account may submit at most 10 packages in a sliding hour.
The portal does not ask you to select a publisher, package id or version. It reads these values from the signed envelope and resolves the publisher from the registered public key.
For API upload, send an authenticated multipart/form-data request to POST /developer/submissions with the file in the file field. Use GET /developer/submissions to list your submissions.
Step 5: Automated validation
Section titled “Step 5: Automated validation”Marketplace performs intake in this order:
- Open the archive without executing it and recompute the payload checksum.
- Resolve the registered publisher from the public key and verify that the signed-in developer owns it.
- Verify the publisher signature against the registered key, not a key supplied only by the package.
- Validate media and run the static scanner.
- Add the Marketplace co-signature and store the accepted artifact.
A scanner reject returns an error and creates no version. A scanner flag creates a QUARANTINED version with findings attached. A clean scan creates a PENDING version; it is not an approval.
Step 6: Manual review
Section titled “Step 6: Manual review”Every third-party PENDING or QUARANTINED version requires a human decision. The persisted states are PENDING, QUARANTINED, APPROVED and REJECTED. The Developer Portal and notifications show the result; a rejection includes a reason.
When changes are requested:
- Read the rejection note and scanner findings.
- Update source, tests and manifest.
- Increment the version.
- Build, sign and verify a new
.zcmsartifact. - Submit the new version.
Versions are immutable. Re-uploading different bytes under an existing version is refused; re-uploading identical bytes keeps the existing verdict.
Step 7: Marketplace signing
Section titled “Step 7: Marketplace signing”zcms pack creates the publisher signature. During intake—after publisher verification and scanning—Marketplace adds a second signature over the same checksum and stores the co-signed .zcms file.
The Marketplace co-signature does not make a pending package public. Only an APPROVED and non-revoked version is served by the registry. Z-CMS runtimes verify the co-signature with their pinned MARKETPLACE_PUBLIC_KEY before installation.
Step 8: Publish and verify
Section titled “Step 8: Publish and verify”Approval makes the version available in the public registry. After approval:
- Open the public listing and verify metadata, screenshots and changelog.
- Install the package on a clean test site from Marketplace.
- Confirm that signature verification succeeds.
- Activate the package with only its documented permissions.
- Run a short smoke test and verify uninstall or rollback behavior.
To verify a package downloaded from Marketplace outside the runtime, use the trusted Marketplace public key:
zcms verify ./downloaded-package.zcms --marketplace-key ./marketplace-public.pemStep 9: Maintain the release
Section titled “Step 9: Maintain the release”Monitor the support channel and security contact. Publish fixes as new semantic versions; do not mutate an existing release.
For a security issue, coordinate disclosure, submit the fixed package and request revocation of affected versions when necessary. Runtimes synchronize the signed revocation feed and quarantine revoked packages.