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
{
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:
- Finds the monorepo root by walking up for
pnpm-workspace.yamlthat includeskb-*patterns (fallback to any.git+pnpm-workspace.yamldirectory). - Calls
createServiceBootstrap()from@kb-labs/core-runtimeto init the global platform singleton — adapters, logger, cache, storage, everything declared inkb.config.json. - Creates the
IEntityRegistryfrom@kb-labs/core-registry, which walks.kb/marketplace.lockand loads plugin manifests. - 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). - Registers middleware, plugins (the Fastify kind, not the KB Labs kind), and routes.
- 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 file | What it exposes |
|---|---|
health.ts | Liveness probes (/api/v1/health) |
readiness.ts | Readiness probes with upstream checks |
metrics.ts | Prometheus metrics endpoint |
observability.ts | Diagnostic events, degraded mode, trace samples |
openapi.ts | OpenAPI spec at /openapi.json and /openapi-plugins.json |
platform.ts | Platform introspection (adapters, config, routes) |
adapters.ts | List registered adapters |
adapter-call.ts | Direct adapter invocation (gated) |
plugins.ts | Plugin registry — list, enable/disable |
analytics.ts | Query analytics events and stats |
cache.ts | Cache inspection and management |
events.ts | Event bus publish/subscribe over HTTP |
jobs.ts | Background job management (submit, list, cancel) |
logs.ts | Log query and streaming |
workflows.ts | Workflow run management (mirrors workflow daemon API) |
workflow-management.ts | Workflow registry and spec management |
hello.ts | Smoke test endpoint |
debug-routes.ts | Debug 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:
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#exportNameformat 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 declaredbasePath(which is constrained by the manifest schema to start with/v1/plugins/). - Per-route
timeoutMsoverrides 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 (theroutes/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:
- Stops accepting new HTTP connections.
- Waits for in-flight requests to complete (bounded by a shutdown timeout).
- Closes the metrics collector.
- Disposes the entity registry.
- Calls
platform.shutdown()to flush logger, analytics, and close adapter connections. - Exits with code 0.
Forced exit on a second signal.
Starting the service
Via kb-dev (recommended):
kb-dev start restkb-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):
cd platform/kb-labs-rest-api/apps/rest-api
node ./dist/index.jsThe 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 env —
PORT,NODE_ENV, plus any env vars consumed by loaded plugins.
There's no service-specific config file — everything flows through the platform config.
What to read next
- Gateway — the REST API is usually fronted by the gateway.
- Plugins → REST Routes — how plugin authors declare routes.
- Services → Workflow Daemon — the sibling service that owns workflow execution.
- Services → Marketplace — the install/discovery service.