KB LabsDocs

REST API

Last updated April 7, 2026


Fastify-based platform API: hosts plugin routes, exposes platform primitives, emits OpenAPI.

The REST API service is the HTTP face of the platform. It loads installed plugins through the entity registry, mounts their declared REST routes under a unified API tree, and exposes platform-level endpoints for health, readiness, metrics, diagnostics, and direct access to adapters (LLM, cache, storage, analytics, etc.).

It's a Fastify 5 app. Source lives in platform/kb-labs-rest-api/apps/rest-api/.

Service manifest

TypeScript
{
  schema: 'kb.service/1',
  id: 'rest',
  name: 'REST API',
  version: '1.2.0',
  description: 'Platform REST API daemon — routes, plugin execution, OpenAPI',
  runtime: {
    entry: 'dist/index.js',
    port: 5050,
    healthCheck: '/api/v1/health',
  },
  dependsOn: ['qdrant'],
  env: {
    PORT: { description: 'HTTP port', default: '5050' },
    NODE_ENV: { description: 'Environment mode', default: 'development' },
  },
}

The manifest is consumed by kb-dev to manage the process — start, stop, health check, dependency ordering. dependsOn: ['qdrant'] means kb-dev boots Qdrant before REST (the vector store is used by Mind and other plugins that may be auto-loaded).

Source: platform/kb-labs-rest-api/apps/rest-api/src/manifest.ts.

Bootstrap sequence

On startup, the entrypoint (apps/rest-api/src/index.ts) calls bootstrap(process.cwd()) from src/bootstrap.ts. The bootstrap:

  1. Finds the monorepo root by walking up for pnpm-workspace.yaml that includes kb-* patterns (fallback to any .git + pnpm-workspace.yaml directory).
  2. Calls createServiceBootstrap() from @kb-labs/core-runtime to init the global platform singleton — adapters, logger, cache, storage, everything declared in kb.config.json.
  3. Creates the IEntityRegistry from @kb-labs/core-registry, which walks .kb/marketplace.lock and loads plugin manifests.
  4. Constructs a Fastify instance with a Pino-compatible wrapper over platform.logger (the platform's logger is vendor-agnostic; Fastify 5 expects a Pino-shaped object, so the server builds a thin adapter).
  5. Registers middleware, plugins (the Fastify kind, not the KB Labs kind), and routes.
  6. Starts the HTTP listener on PORT (default 5050).

The server uses platform.logger for application logging — not Fastify's built-in logger. Fastify's own request logging is disabled via the kDisableRequestLogging symbol so that nothing bypasses the platform's structured logging pipeline.

Routes directory

Routes live in apps/rest-api/src/routes/ and are all registered from routes/index.ts. The current set:

Route fileWhat it exposes
health.tsLiveness probes (/api/v1/health)
readiness.tsReadiness probes with upstream checks
metrics.tsPrometheus metrics endpoint
observability.tsDiagnostic events, degraded mode, trace samples
openapi.tsOpenAPI spec at /openapi.json and /openapi-plugins.json
platform.tsPlatform introspection (adapters, config, routes)
adapters.tsList registered adapters
adapter-call.tsDirect adapter invocation (gated)
plugins.tsPlugin registry — list, enable/disable
analytics.tsQuery analytics events and stats
cache.tsCache inspection and management
events.tsEvent bus publish/subscribe over HTTP
jobs.tsBackground job management (submit, list, cancel)
logs.tsLog query and streaming
workflows.tsWorkflow run management (mirrors workflow daemon API)
workflow-management.tsWorkflow registry and spec management
hello.tsSmoke test endpoint
debug-routes.tsDebug utilities (gated by env flag)

Plugin-declared routes are mounted separately — see below.

Mounting plugin routes

Every plugin's manifest may include a rest block:

TypeScript
rest: {
  basePath: `/v1/plugins/${string}`,
  defaults?: { timeoutMs?: number };
  routes: RestRouteDecl[];
}

When the registry loads a plugin, the REST server walks its rest.routes[] and registers each one on the Fastify instance:

  • The handler is dynamically imported from route.handler (same ./path.js#exportName format as CLI handlers).
  • Input and output schemas declared via { zod: '@pkg#SchemaExport' } are resolved to real Zod objects and wired into Fastify's request validation.
  • The route is namespaced under /api/v1/plugins/<plugin-name> — actual prefix comes from the plugin's declared basePath (which is constrained by the manifest schema to start with /v1/plugins/).
  • Per-route timeoutMs overrides the plugin default, which overrides the server default.

The REST layer doesn't run plugin handlers in-process — it dispatches through the plugin execution backend chosen by platform.execution.mode. For in-process mode this is a direct function call; for worker-pool / container modes, the handler runs out-of-process and the REST server waits for the result.

OpenAPI

Two specs are served:

  • /openapi.json — the Fastify-native spec covering built-in platform routes (the routes/ directory above).
  • /openapi-plugins.json — plugin-contributed routes, generated from each plugin's manifest and Zod schemas.

Visibility follows the rule documented in HTTP Services & OpenAPI: hideUntagged: true means a route without a tags: field in its Fastify schema is omitted from the spec. This is the toggle the platform uses to hide internal routes (debug, admin) while still serving them.

Swagger UI is available at /docs in non-production mode and disabled by default in production (set NODE_ENV=production to disable it).

Visibility

Every route the REST server registers has a Fastify schema object. To appear in the OpenAPI spec, a route must declare tags: ['GroupName']. Omitting tags hides the route from /openapi.json without affecting its actual HTTP behavior — it's still reachable, just invisible to clients browsing the spec.

This is how admin, debug, and internal dispatch routes stay out of the public API surface. The rule is set at registerOpenAPI() time via hideUntagged: true.

Routes with schema: { hide: true } (distinct from hideUntagged) are explicitly excluded — used for SSE and WebSocket routes that can't be expressed as OpenAPI operations.

Metrics

The service emits Prometheus-style metrics via /api/v1/metrics. Two collectors run in parallel:

  • Request metrics (middleware/metrics.ts) — per-route request counts, latency histograms, error rates.
  • System metrics (services/system-metrics-collector.ts) — CPU, memory, event loop lag, open handles.

Both are designed to be scraped by a Prometheus server and graphed in Studio. Observe them live at /api/v1/metrics in the default text format.

There's also a second set of metrics tracking REST domain operations (restDomainOperationMetrics in the middleware file) that captures platform-level events like adapter calls and plugin executions, independent of the raw HTTP layer.

Multi-tenancy

Request-level tenancy is honored at the middleware layer: requests carry an X-Tenant-ID header (or fall back to KB_TENANT_ID from the environment), and the tenant ID is threaded through the platform call chain via AsyncLocalStorage so downstream adapters (LLM, cache, storage) can scope operations per tenant.

Rate limiting, quota enforcement, and isolation happen at the adapter layer, not at the REST layer. See Concepts → Multi-Tenancy.

Graceful shutdown

On SIGTERM / SIGINT, the bootstrap:

  1. Stops accepting new HTTP connections.
  2. Waits for in-flight requests to complete (bounded by a shutdown timeout).
  3. Closes the metrics collector.
  4. Disposes the entity registry.
  5. Calls platform.shutdown() to flush logger, analytics, and close adapter connections.
  6. Exits with code 0.

Forced exit on a second signal.

Starting the service

Via kb-dev (recommended):

Bash
kb-dev start rest

kb-dev reads the service manifest, resolves dependencies, starts Qdrant first if declared, then launches the REST API on port 5050.

Directly (for debugging — kb-dev is still the recommended path):

Bash
cd platform/kb-labs-rest-api/apps/rest-api
node ./dist/index.js

The service always reads kb.config.json from the monorepo root regardless of where it's launched from — the bootstrap walks up from the binary's location.

Configuration

The REST API has two layers of config:

  • kb.config.json — platform-wide adapters and execution mode. See Configuration → kb.config.json.
  • Service-local envPORT, NODE_ENV, plus any env vars consumed by loaded plugins.

There's no service-specific config file — everything flows through the platform config.

REST API — KB Labs Docs