Publishing to the marketplace
Build your plugin, ZIP it with manifest.json and dist/ at the root, upload at the developer portal, pass the checklist, and ship to shops.
You do not need the PuntoSuite monorepo to publish. Build a ZIP with your own editor and bundler, upload it at the dashboard developer portal, pass the automated checklist, and the listing goes live for shops to install from the desktop Marketplace.
The flow at a glance
| Step | Where | Result |
|---|---|---|
| 1. Build | Your machine | dist/index.js (ESM) |
| 2. ZIP | Your machine | manifest.json + dist/ at the archive root |
| 3. Upload | Dashboard /developer/portal | Checklist runs (pass / warn / fail) |
| 4. Publish | Dashboard /developer/portal | Listing is published |
| 5. Install | Desktop Marketplace | Shops install + confirm the plugin |
1. Build
Bundle src/index.ts to a single ESM file at the path your manifest's entry points to (for example dist/index.js). Import types from @bodegasuite/plugin-sdk, or use the scaffold's offline src/types/bodega-api.d.ts snapshot. See Developer quickstart for the bundler setup.
2. ZIP layout (required)
The archive must have manifest.json and dist/ at the root — no wrapping folder:
com.example.my-plugin-1.0.0.zip
├── manifest.json
└── dist/
└── index.js
manifest.jsonis validated withpluginManifestSchema(the same Zod schema the desktop app uses — see Plugin manifest).entryis a relative POSIX path inside the ZIP (e.g.dist/index.js); that file must exist.- The desktop installer extracts in-process and enforces a zip-slip guard plus entry-count and uncompressed-size caps. Keep the archive lean and use plain ZIP deflate (avoid exotic compression methods).
The scaffold ships a packer that emits exactly this layout:
pnpm run build
pnpm run zip # writes <id>-<version>.zip with manifest.json + the entry file at root
3. Upload at the developer portal
- Sign in on the dashboard. Unauthenticated visits to
/developer/portalredirect to sign-in. - Drop your ZIP under Publish a plugin (a starter template is available there).
- Review the checklist. Checks are grouped pass / warn / fail:
| Group | What it verifies |
|---|---|
| Structure | manifest.json + dist/ at the ZIP root; the entry file exists; archive within size/entry caps |
| Manifest | Parses against pluginManifestSchema: valid id (reverse-DNS, filesystem-safe), version (semver), entry, bodegasuite.version; known permissions only; sane contribution caps |
| Security heuristics | AST surface scan of the bundle vs the declared capabilities / permissions / net.allow — flags undeclared capability use and risky patterns (eval, vm, computed require/import). See Plugin security |
| Policy | Reserved-namespace and listing rules (e.g. the org.bodegasuite.* namespace is rejected), publish rate limit, paid-plugin Stripe gate |
This is the platform's autoverification gate: every upload runs it server-side and a fail blocks
publishing (the failing steps are returned so you can fix them) — a warn is advisory. It verifies
the artifact (manifest, ZIP, security scan, signature); your plugin's tests + coverage are
enforced earlier, by your own build — pnpm run pack won't produce a ZIP unless they pass (see
Developer quickstart). Together they are the full
"can't-publish-junk" gate.
- Optionally set a display name and description (defaults come from
manifest.json).
4. Publish
Click Publish to marketplace. When all checks pass, the listing becomes published with no separate reviewer step.
Automated hold (not a manual queue): if the security scan flags a community plugin's code (e.g.
undeclared capability use, eval/vm/computed require), the listing is held as pending_review
instead of going live — it is never silently published. It auto-resolves when you fix the code and
re-submit (or an admin confirms it). Official org.bodegasuite.* plugins are first-party and skip the
hold.
The portal stores a canonical SHA-256 of the uploaded bytes. On install the desktop compares that digest to the downloaded file before extracting (see Trust + install).
Free vs paid
| Type | What to set | Goes live |
|---|---|---|
| Free | nothing extra | as soon as checks pass |
| Paid | Stripe Price ID in the paid listing options before publishing | as soon as checks pass |
Paid status is server-authoritative — the desktop derives entitlement from the server, never from a renderer-supplied flag, so a spoofed catalog price cannot turn a paid plugin free. If the server is unreachable at install time, the plugin is treated as requiring activation (fail-closed).
5. Shops install from the desktop
Operators open the desktop Marketplace, pick your listing, and install. The desktop:
- Downloads the artifact over an HTTPS URL (egress is SSRF-guarded — plain
httpand private/loopback/link-local hosts are rejected; a dev loopback escape hatch exists only in unpackaged builds). - Verifies the SHA-256 digest against the catalog value before extracting.
- Extracts with zip-slip + zip-bomb guards into
<userData>/plugins/<manifest.id>/. - Records the trust tier and applies the sandbox:
permissionsgateBodegaAPIsurfaces andcapabilitiesgate Node built-ins. See Plugin security.
Trust tiers
| Tier | Source | Behavior |
|---|---|---|
marketplace_signed | Upload included a valid Ed25519 signature against a known key | Highest marketplace trust |
marketplace_digest | Digest-only (no signature supplied) | Accepted on the SHA-256 digest match alone |
Signing is optional by default today: the publish step always emits a digest and may emit a signature, and a digest-only plugin remains installable. Trust metadata is advisory — the operator still confirms the install. The host can make a valid Ed25519 signature mandatory for every install via the BODEGASUITE_PLUGIN_REQUIRE_SIGNATURE=1 switch (off by default; flipped on when the signed marketplace opens) — a digest-only artifact is then rejected.
Sideloading (dropping a built plugin into
<userData>/plugins/<id>/and confirming in Settings → Plugins) is for local testing only and never goes through this flow. See Developer quickstart.
Updating a listing
Upload a ZIP whose manifest.json has a higher semver. There is no in-place patch: a new version is a new upload that must pass the same checklist. Open Your plugins in the portal to push a new build to an existing listing.