Skip to content

Marketplace Allow-List

The marketplace does not host code. Services live on npm, and @zelavis/marketplace owns the allow-list: which packages an installation may install, at exactly which versions, and the digest each must have. Installing a service executes its code with Platform authority, so “is this on the list” is the trust decision, and the design exists to make that answer trustworthy.

A list is a signed JSON envelope:

{ "keyId": "…", "payload": "<base64url of the list>", "signature": "<base64url Ed25519>" }

The payload is { schemaVersion: 1, sequence, issuedAt, expiresAt, services: […] }. Each service names its npm package, kind (app, plugin or frontend), maintainer, marketplace card, and its exact versions with the sha512 integrity of each published tarball. A range or a tag is never a listed version.

The list is signed, so a copy from a mirror, a gist or an API is exactly as trustworthy as one from the primary. An installation is given several sources and uses the first that answers with a list it accepts. A source can never move it backwards:

  • Signature. Only keys the installation trusts. Anything else is skipped.
  • Sequence. Strictly increasing. A validly signed list older than one already held is a replay (it could re-open a service that was removed) and is ignored.
  • Expiry. After expiresAt the list is stale for seven days (nothing new is installed, but running services are untouched) and then expired.
  • Transport. https only, no redirects, bounded size and time.
  • Cache. The last accepted list is kept in the System Store and verified again on every read, so an outage changes nothing and an edited cache is ignored.

The list shipped with a release is a floor and a last resort.

When the gate is on (the default), an npm install must name an exact listed version, is refused before anything is fetched if it is not listed, and is refused after download if the bytes do not have the listed digest. Other source kinds are refused unless the operator configured them explicitly. allowlist: false in the adapter’s services.marketplace option removes the gate and leaves only the explicit source policy.

In a repository checkout, ZELAVIS_OFFICIAL_SERVICES_DIR (set by pnpm dev) points at zelavis-services/. An official service that is not installed is offered from there instead of from npm, so it can be tried without publishing anything. It only ever names packages in the operator’s own checkout.

GET /zelavis/api/v1/runtime/marketplace/allowlist how current the list is (marketplace.view)
POST /zelavis/api/v1/runtime/marketplace/allowlist/refresh fetch it again (system.services.manage)

SDK: client.marketplace.allowlist() and client.marketplace.refresh(). CLI: zelavis marketplace allowlist and zelavis marketplace refresh.

Sources come from services.marketplace.sources or ZELAVIS_ALLOWLIST_SOURCES (comma separated). The default is https://zelavis.com/allowlist.json. There are no keys: the list is plain JSON and its https origin is the trust anchor, the same as for the installer. A newer list applies to the catalogue at the next start. Until that file is published, only the list shipped with the release counts.

Publish changed official services before building the Platform. Then run pnpm allowlist update, which rebuilds the list from the packages in zelavis-services/* and the digests npm serves for them, raises sequence and rewrites the list shipped with the release. pnpm allowlist publish then writes a plain copy with a fresh expiry to website/public/allowlist.json, which the next website deploy serves at https://zelavis.com/allowlist.json. pnpm allowlist check is part of release validation and refuses defaults that differ from the qualified service versions. A fresh runtime uses the shipped snapshot to compose its catalogue; background refreshes apply on its next start.

A Project has a marketplace of its own for the plugins and frontends installed into it, behind the same install gate. It never fetches the sources. The Platform hands its list to each Project (in the Project’s data folder, on every refresh and whenever the Project starts), and the Project parses it again on every read, so a malformed file is refused. The Platform is the Project’s parent authority and the folder is the Project’s own, which is the same trust a Project already places in the Platform. A Project that has been given nothing uses the list shipped with its release.