Marketplace
Last updated April 7, 2026
Install, manage, and discover entities (plugins, adapters, workflows) via npm.
The marketplace service is how plugins, adapters, and other entities get installed into a KB Labs workspace. It wraps npm (today — a native registry is on the roadmap) behind a unified HTTP API and writes the resolved installations into .kb/marketplace.lock, which is then read by the discovery layer at CLI and service startup.
It's a small Fastify service. Source: platform/kb-labs-marketplace/apps/marketplace/.
Service manifest
{
schema: 'kb.service/1',
id: 'marketplace',
name: 'Marketplace',
version: '1.0.0',
description: 'Unified entity marketplace — install, manage, discover',
runtime: {
entry: 'dist/index.js',
port: 5070,
healthCheck: '/health',
},
env: {
PORT: { description: 'HTTP port', default: '5070' },
NODE_ENV: { description: 'Environment mode', default: 'development' },
},
}Source: apps/marketplace/src/manifest.ts.
Why a service?
Install, uninstall, and upgrade operations all have to:
- Resolve the package from a registry.
- Verify integrity and (eventually) signatures.
- Update
.kb/marketplace.lock. - Surface progress events so Studio can show a spinner.
- Trigger a registry refresh in any running services (CLI, REST API, workflow daemon).
Doing all that from inside every consumer (CLI, REST, Studio) would mean duplicating the install logic and coordinating concurrent writes to the lock file. Centralizing it in a single service solves both problems — everyone else becomes a client of the marketplace HTTP API.
Architecture
CLI / Studio / REST
│
▼
Marketplace HTTP :5070
│
┌────┴─────┐
▼ ▼
Fastify MarketplaceService
│
▼
NpmPackageSource
│
▼
pnpm / npm
│
▼
.kb/marketplace.lockFrom bootstrap.ts:
- Detect repo root via
findRepoRoot(cwd). - Load env from
.envin the repo root. createServiceBootstrap({ appId: 'marketplace', repoRoot })— init the platform singleton.- Construct
MarketplaceServicewith anNpmPackageSource. - Create a Fastify server via
@kb-labs/marketplace-api. - Listen on
port(default 5070). - Wire
SIGTERM/SIGINTfor graceful shutdown.
The service is deliberately thin. MarketplaceService holds the business logic; the Fastify layer is pure HTTP wrapping.
Packages
The marketplace is a small monorepo in itself — the daemon lives alongside the libraries it consumes:
| Package | Purpose |
|---|---|
@kb-labs/marketplace-core | The MarketplaceService class and PackageSource interface |
@kb-labs/marketplace-npm | NpmPackageSource — pnpm wrapper that installs, resolves, and introspects packages |
@kb-labs/marketplace-api | Fastify routes (createServer()) |
@kb-labs/marketplace-app | The daemon binary that wires everything together |
Only marketplace-npm depends on execa / pnpm — everything else is vendor-agnostic, so a future RegistryPackageSource can be dropped in without touching the core or API.
PackageSource interface
NpmPackageSource is the current implementation; the interface exists so that other sources (internal registry, OCI registry, local link) can be added without changing core behavior. The contract is in @kb-labs/marketplace-contracts.
The source is responsible for:
- Resolving — turning a package spec (name, version) into a concrete downloadable artifact.
- Installing — fetching the artifact into
node_modules. - Inspecting — reading the installed package's
package.json+ manifest. - Uninstalling — removing the artifact from
node_modules.
Entity kinds
The marketplace is unified — it handles more than just plugins. The entity kinds come from @kb-labs/core-discovery:
type EntityKind =
| 'plugin'
| 'adapter'
| 'cli-command'
| 'rest-route'
| 'ws-channel'
| 'workflow'
| 'webhook'
| 'job'
| 'cron'
| 'studio-widget'
| 'studio-menu'
| 'studio-layout'
| 'skill'
| 'hook'
| (string & {});When a package is installed, the marketplace inspects its manifest and records primaryKind (the discriminator) plus provides (all kinds it contributes) in the lock file. This lets clients filter — "list all installed plugins" or "list everything that provides a workflow".
Lock file format
Every install writes to .kb/marketplace.lock:
interface MarketplaceLock {
schema: 'kb.marketplace/2';
installed: Record<string, MarketplaceEntry>;
}
interface MarketplaceEntry {
version: string;
integrity: string; // SRI hash of package.json (sha256-...)
resolvedPath: string; // relative to repo root
installedAt: string; // ISO 8601
source: 'marketplace' | 'local';
signature?: EntitySignature;
primaryKind: EntityKind;
provides: EntityKind[];
enabled?: boolean; // default true
}The lock file is the single source of truth for the discovery layer. Every CLI startup and every service bootstrap reads this file via DiscoveryManager.discover() (core-discovery) and loads the manifests it points at.
source: 'marketplace'— installed viakb marketplace install. Integrity mismatches are fatal.source: 'local'— linked viakb marketplace link. Integrity mismatches are silently refreshed, because local packages change on every build.
See Plugins → Overview → Discovery for the full discovery flow.
HTTP API
The API is mounted by createServer() in @kb-labs/marketplace-api. The surface covers:
- Install —
POST /installwith a package spec; returns the resolved entry. - Uninstall —
DELETE /packages/<name>. - Update —
POST /packages/<name>/update. - Link —
POST /linkwith a local path for dev mode. - Unlink —
DELETE /packages/<name>/link. - List —
GET /packageswith optionalkindfilter. - Inspect —
GET /packages/<name>returns the entry plus the parsed manifest. - Enable/Disable —
PATCH /packages/<name>with{ enabled: boolean }. - Health / Readiness / OpenAPI — standard
@kb-labs/shared-httpendpoints at/health,/ready,/openapi.json,/docs.
The CLI commands (kb marketplace install, kb marketplace list, etc.) are thin HTTP clients that call this API.
Port 5070
The service binds to port 5070 by default. Override via KB_MARKETPLACE_PORT or PORT in env. The bind host is 0.0.0.0 by default; override via KB_MARKETPLACE_HOST.
kb-dev launches it on the default port and kb-dev status will show it under the backend group.
Starting the service
kb-dev start marketplaceOr directly:
cd platform/kb-labs-marketplace/apps/marketplace
node ./dist/index.jsThe service reads kb.config.json from the repo root for platform configuration (logger, cache). There's no marketplace-specific config file.
CLI and lock cache
After an install, running services (CLI, REST API, workflow daemon) each have their own registry cache in memory. Freshly installed entities don't appear until those caches are refreshed:
pnpm kb marketplace clear-cacheThis is the #1 source of "my plugin built but doesn't show up" confusion. The rule is: after any marketplace install, link, or rebuild of a linked plugin, clear the cache.
Graceful shutdown
On SIGTERM / SIGINT:
- Log receipt of the signal.
- Close the Fastify server.
- Exit with code 0.
No in-flight operation draining — installs are typically fast, and the npm subprocess (if one is running) is left to finish or fail on its own. If an install gets interrupted in the middle of writing the lock file, the lock is left in whatever state it was in and you may need to recover manually.
What to read next
- Plugins → Publishing — how plugin authors get their packages into the marketplace.
- Plugins → Overview → Discovery — how installed packages turn into live plugins.
- Concepts → Plugin System — the conceptual picture.
- Services → REST API — sibling service with similar bootstrap patterns.