Skip to content

Publishing & updating on the marketplace

The full marketplace lifecycle for a panel extension — submit, review, signing on approval, pricing, the operator install flow, and shipping updates as an in-place atomic swap that loses no data and never re-charges buyers.

9 min readUpdated Aug 15, 2026
On this page

A finished panel is two files — a driver class and a salieno.json manifest — but a folder on a disk is not a distributable extension. Salieno Core only loads panels that arrive through marketplace.salieno.com, signed. This article walks the whole lifecycle: how you get a package published, how an operator installs it, and how you ship updates without ever touching a customer's data or charging them twice.

Why marketplace-only#

There is no local upload path, and no "drop a folder on the server" escape hatch. That is deliberate. Every time Core resolves your driver to a live instance, it checks three things, and refuses unless all of them hold:

  1. Registered — an enabled panel_extensions row exists for the slug. A hand-copied folder has no row.
  2. Genuine — the stored signed artifact re-verifies against Core's pinned Ed25519 marketplace key, and each on-disk class file's SHA-256 still matches the hash recorded from that verified artifact at install. You cannot forge the signature, and you cannot edit the code after install.
  3. Entitled — the activated licence still owns the panel, as decided by the marketplace.

Publishing is how your code earns that signature and how a buyer earns that entitlement. The mechanics of the three gates are covered in Trust model & security; this article is about the path that produces them.

Before you submit: get the package right#

The marketplace reads your salieno.json at submission, so a few fields carry more weight here than they do while you develop. See The manifest for the full field reference — the ones that matter for publishing:

  • `slug` — unique, and it is both what a server group stores in the database and the marketplace product slug. They must match. Choose it once; changing it later means a new product, not an update.
  • `version` — semver. This is the number the marketplace compares between releases, and it is what an install shows in its Extensions library.
  • `entry` — the driver file's path relative to the package root, e.g. "Cpanel.php". It is required by the marketplace and must end in `.php`. Core also refuses a package whose declared driver file is not actually present.
  • `requires_core` — a Core version window, e.g. ">=1.0.0 <2.0.0". This is the range of Salieno Core your panel supports, not your panel's own version. Set it honestly; it is what protects an operator on Core 2.x from installing a panel that only speaks the 1.x contract.
  • `capabilities` — informational at the marketplace level (it lets the listing say what your panel does). The authoritative runtime source is still your driver's capabilities() method, so keep the two in sync but understand that Core trusts the code, not the manifest.
json
{
  "schema": "salieno.panel/1",
  "kind": "panel",
  "slug": "cpanel",
  "name": "cPanel / WHM",
  "version": "1.0.0",
  "namespace": "Salieno\\Panel\\Cpanel\\",
  "driver": "Cpanel",
  "entry": "Cpanel.php",
  "requires_core": ">=1.0.0 <2.0.0"
}

Test everything against a real panel first — Testing your panel covers how to exercise the contract before you hand the code over for review.

Submit → review → signed → published#

You submit the package to marketplace.salieno.com. A reviewer looks it over, and on approval the marketplace signs the artifact with its key. That signature is the whole point of the trip: it is what Core's pinned key later verifies, and it is minted at approval time, not at build time. You never hold the signing key, so there is nothing on your side to leak.

Once signed, the product is published and appears to any licence entitled to it.

Pricing#

You set pricing when you create the product, and there are two shapes:

ModelWhat the buyer pays
freeNothing. The entitlement is granted on install.
one_timeA single paid one-off.

There is no subscription and no per-version charge. The entitlement a buyer receives covers every version of the product — this is the rule that makes updates free forever, which we come back to below. Decide free vs. paid at creation; it is a property of the product, not of any single release.

How an operator installs#

Installs happen from the admin Extensions library at admin → Server Groups → Extensions. Two things about that screen shape the buyer's experience:

  • It lists only panels the licence owns. An operator cannot browse to a panel they have not acquired and install it anyway; ownership is resolved first.
  • Install is a signed, single-use path: resolve → grant → download → verify → register.

Walking that path:

  1. Resolve the product for this licence and confirm ownership with the marketplace.
  2. Grant — the marketplace issues a single-use, signed grant. It only issues to an owner, which is exactly why a copied folder from someone else's install is inert: the grant, and therefore the entitlement, is tied to the licence.
  3. Download the signed artifact.
  4. Verify the download against Core's pinned key before any file is written to disk. Nothing lands on the box until the signature checks out.
  5. Register — Core extracts the package, validates the manifest, records a per-file SHA-256 hash tree, and writes an enabled panel_extensions row keyed by your slug.

The installed code lives at storage/app/panels/{slug}/ — a runtime artifact, gitignored, off the normal Composer autoload path. It only becomes loadable through the scoped, hash-checked autoloader the three gates unlock. From the operator's side it is one click; underneath, every step is signed and verified.

Shipping an update#

An update is just a newer release of the same product. The rule is simple: publish a package whose `version` is greater than the currently published one. Semver comparison decides "greater," so 1.0.1, 1.1.0, and 2.0.0 are all valid successors to 1.0.0, and the marketplace rejects a re-publish that isn't actually newer.

The Plesk example in the repo is a real bump: its manifest ships "version": "2.0.0". Everything else about the product — slug, entitlement, the buyers who own it — is unchanged.

Go through review and signing exactly as with the first release. Each version is signed on approval; there is no "sign once" for a product.

What installs see#

You do not push an update to anyone. Installs discover it:

  • The Extensions library shows a "vX available" badge next to the panel. That check is cached for 6 hours, and an operator can force a fresh check with the "Check for updates" button.
  • A daily admin notification surfaces available updates so an operator who never opens the Extensions page still hears about it.

When the operator clicks Update, Core runs the same signed install path — resolve → grant → download → verify → register — against the new version. The signature is verified before anything is written, identical to a first install.

The atomic swap, and why no data is lost#

Replacing the code is an atomic swap, not an in-place overwrite. Core extracts the new build into a staging directory, and only once it is fully in place does it move it over the old one; the previous build is kept aside until the new one has landed, and restored if the swap fails. The panel_extensions row is then updated in place — same row, keyed by the same slug, with the new version, driver hashes, and signed artifact recorded.

Two properties fall out of this, and they are the reassurance you give buyers:

No data is lost. A panel is a stateless artifact — pure code. Everything with state lives in Core's database, keyed by your panel slug: the server groups, the servers connected under them, and every account you ever provisioned. An update changes the code behind the slug; it never changes the slug, so all of that stays wired to the new build untouched. This is also why authors must never write state into the panel's own folder — beyond breaking the per-file hash check, it is the one place an update is allowed to replace wholesale. Keep all state in what Core passes you ($server, $hosting) and in the panel server itself.

No one is re-charged. The entitlement covers every version of the product. Updating a paid panel spends the ownership the buyer already has; it never triggers a new charge. That is what lets you ship fixes and features freely — a buyer who paid once for a one_time panel gets 2.0.0 at no cost.

Versioning in practice#

Two version numbers travel with your panel, and they answer different questions:

FieldQuestion it answersExample
versionWhich release of my panel is this?2.0.0
requires_coreWhich Core versions can run it?>=1.0.0 <2.0.0

Bump version on every release; the marketplace enforces that it climbs. Revisit requires_core only when you actually adopt or drop compatibility with a Core version — for instance, when Core reaches 2.0 and adds contract surface you depend on, you widen the ceiling and publish a release that targets it. Because requires_core is a window, you can keep supporting older Core installs while newer ones get features that need a newer contract, and each install pulls the newest version its Core falls inside.

Keep the manifest's informational capabilities list honest as you evolve — if a new version starts supporting reseller conversion, add reseller to both capabilities() and the manifest so the listing and the UI agree. Declaring capabilities honestly is a first-class design principle, not a formality; see Declaring capabilities and The driver contract for how each one maps to a control Core shows or hides.

The lifecycle end to end#

  1. Build the driver + manifest; test against a real panel.
  2. Submit to marketplace.salieno.com.
  3. A reviewer approves; the marketplace signs the artifact.
  4. The product is published — free or one_time, entitlement covering all versions.
  5. Owners install from admin → Server Groups → Extensions via the signed resolve → grant → download → verify → register path.
  6. To improve it, publish a greater semver; installs see a "vX available" badge and a daily notification.
  7. The operator clicks Update; the same signed path performs an atomic swap — new code, same slug, no data loss, no re-charge.

New to the series? Start with the Panel extensions overview or build your first driver in the Quickstart.

panelspublishingmarketplace
Was this article helpful?
Still stuck?Contact support
Publishing & updating on the marketplace · Salieno Docs