KB LabsDocs

Manifest Reference (v3)

Last updated April 7, 2026


Every field in the kb.plugin/3 plugin manifest.

The plugin manifest is the declarative contract between a plugin and the KB Labs runtime. It describes the plugin's identity, capabilities (CLI commands, REST routes, Studio pages, workflows, webhooks, jobs, cron schedules, WebSocket channels), handler entry points, and the permissions it requests.

This page is the canonical reference for schema kb.plugin/3. It mirrors the TypeScript types in infra/kb-labs-plugin/packages/plugin-contracts/src/manifest.ts — if the code disagrees with the docs, the code wins.

Plugins typically author the manifest in TypeScript (so presets and imports work) and export it as the default export of packages/<plugin>-cli/src/manifest.ts. Any JSON-serializable shape is also accepted.

Top-level shape

TypeScript
interface ManifestV3 {
  schema: 'kb.plugin/3';
  id: string;
  version: string;
  configSection?: string;
  display?: DisplayMetadata;
  permissions?: PermissionSpec;
  dependencies?: PluginDependency[];
  platform?: PlatformRequirements;
  setup?: SetupSpec;
  lifecycle?: LifecycleHooks;
  cli?: { commands: CliCommandDecl[]; groupMeta?: CliGroupMeta[] };
  rest?: RestConfig;
  ws?: WebSocketConfig;
  workflows?: { handlers: WorkflowHandlerDecl[] };
  webhooks?: { handlers: WebhookHandlerDecl[] };
  jobs?: JobsConfig;
  cron?: { schedules: CronDecl[] };
  studio?: StudioConfig;
}

Only schema, id, and version are required. Every other section is optional — omit it and the runtime simply won't load that capability.

Core fields

schema

Literal string 'kb.plugin/3'. Used by the loader to discriminate manifest versions. Manifests with any other value are rejected.

id

Plugin identifier in npm-scope format: @scope/name. The loader validates against /^@[a-z0-9-]+\/[a-z0-9-]+$/ — lowercase letters, digits, and hyphens only.

TypeScript
id: '@kb-labs/commit'

version

Semantic version. Validated against /^\d+\.\d+\.\d+/ (loose — the loader only enforces the MAJOR.MINOR.PATCH prefix).

configSection

Optional string. Names the key under which this plugin's config is stored in .kb/kb.config.json. If set to 'commit', the runtime will load config from profiles[].products.commit and expose it via useConfig('commit').

display

Human-readable metadata shown by the CLI --help, the marketplace, and Studio.

TypeScript
interface DisplayMetadata {
  name: string;
  description?: string;
  author?: string;
  homepage?: string;
  repository?: string;
  tags?: string[];
  icon?: string; // URL or emoji
}

Example:

TypeScript
display: {
  name: 'Commit Generator',
  description: 'AI-powered commit generation with conventional commit support.',
  tags: ['commit', 'git', 'ai', 'conventional-commits'],
}

permissions

Plugin-wide permission defaults. Handlers can override these at the command, route, or channel level. The shape is documented on the Permissions page.

In idiomatic plugins this is composed from presets via combinePermissions():

TypeScript
import { combinePermissions, gitWorkflowPreset, kbPlatformPreset } from '@kb-labs/sdk';
 
const pluginPermissions = combinePermissions()
  .with(gitWorkflowPreset)
  .with(kbPlatformPreset)
  .withEnv(['COMMIT_ENV_VARS'])
  .withFs({ mode: 'readWrite', allow: ['.kb/commit/**'] })
  .withPlatform({ llm: true, cache: ['commit:'], analytics: true })
  .withQuotas({ timeoutMs: 600_000, memoryMb: 512 })
  .build();

dependencies

Other plugins that must be present for this plugin to work.

TypeScript
interface PluginDependency {
  id: string;      // @scope/name
  version: string; // semver range, e.g. '^1.0.0'
  optional?: boolean;
}

A non-optional dependency that can't be resolved causes the plugin to fail loading. An optional one is silently skipped.

platform

Declares which platform services the plugin needs.

TypeScript
interface PlatformRequirements {
  requires?: Array<
    | 'vectorStore'
    | 'llm'
    | 'embeddings'
    | 'cache'
    | 'storage'
    | 'logger'
    | 'analytics'
  >;
  optional?: string[];
}

Services listed in requires must be configured (via kb.config.json) for the plugin to load. optional services are ones the plugin can run without — features dependent on them degrade gracefully.

TypeScript
platform: {
  requires: ['storage', 'cache'],
  optional: ['llm', 'analytics', 'logger'],
}

setup

An installation-time handler that runs once, typically to create directories or seed configuration.

TypeScript
interface SetupSpec {
  handler: string;           // path to handler file
  describe: string;
  permissions: PermissionSpec; // usually broader than runtime perms
}

lifecycle

Handlers fired at well-defined points in the plugin lifecycle.

TypeScript
interface LifecycleHooks {
  onLoad?: string;
  onUnload?: string;
  onEnable?: string;
  onDisable?: string;
}

Each value is a path to a handler file (same format as command handlers — see below).

cli

Declares commands exposed via pnpm kb.

TypeScript
cli?: {
  commands: CliCommandDecl[];
  groupMeta?: CliGroupMeta[];
}

CliCommandDecl

TypeScript
interface CliCommandDecl {
  id: string;                    // 'commit:commit', 'marketplace:install', ...
  group?: string;                // 'commit'
  subgroup?: string;             // 'plugins' → `kb marketplace plugins <id>`
  describe: string;              // short description (--help one-liner)
  longDescription?: string;
  flags?: CliFlagDecl[];
  examples?: string[];
  handler: string;               // './cli/commands/run.js#default'
  handlerPath?: string;          // './cli/commands/run.js' (legacy)
  permissions?: PermissionSpec;  // overrides plugin defaults
}

Handler format. The handler string is a file path followed by #exportName. #default targets the default export. Paths are resolved relative to the plugin's package root (normally the built dist/ directory).

Flag declarations.

TypeScript
interface CliFlagDecl {
  name: string;
  type: 'string' | 'boolean' | 'number' | 'array';
  alias?: string;
  default?: unknown;
  description?: string;
  choices?: string[];
  required?: boolean;
}

Most plugins use the defineCommandFlags helper from @kb-labs/sdk to keep flag definitions co-located and typed.

CliGroupMeta

Optional human-readable descriptions for command groups and subgroups, shown in kb --help.

TypeScript
interface CliGroupMeta {
  name: string;     // e.g. 'marketplace' or 'marketplace/plugins'
  describe: string;
}

Example

From plugins/kb-labs-commit-plugin/packages/commit-cli/src/manifest.ts:

TypeScript
cli: {
  commands: [
    {
      id: 'commit:commit',
      group: 'commit',
      describe: 'Generate and apply commits (default flow).',
      longDescription:
        'Analyzes changes, generates commit plan with LLM, applies commits locally. ' +
        'Use --dry-run to preview without applying, --with-push to push after applying.',
      handler: './cli/commands/run.js#default',
      handlerPath: './cli/commands/run.js',
      flags: defineCommandFlags(runFlags),
      examples: [
        'kb commit commit',
        'kb commit commit --dry-run',
        'kb commit commit --with-push',
      ],
    },
    // ... more commands
  ],
}

rest

Declares HTTP endpoints hosted by the REST API service.

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

basePath is a template-literal type: it must begin with /v1/plugins/. All route paths are relative to it.

RestRouteDecl

TypeScript
interface RestRouteDecl {
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';
  path: string;                 // relative to basePath
  description?: string;
  timeoutMs?: number;
  input?: SchemaRef;
  output?: SchemaRef;
  errors?: ErrorSpec[];
  handler: string;
  security?: ('none' | 'user' | 'token' | 'oauth')[];
  permissions?: PermissionSpec;
}

SchemaRef

Used to reference input/output schemas for routes and handlers.

TypeScript
type SchemaRef =
  | { $ref: string }  // OpenAPI JSON Schema ref
  | { zod: string };  // './path/to/schema.ts#exportedSchema' or '@pkg#ExportedSchema'

ErrorSpec

Declared error responses for OpenAPI generation.

TypeScript
interface ErrorSpec {
  code: string;        // 'CONFIG_NOT_RESOLVED'
  http: number;        // 400–599
  description?: string;
}

Example

TypeScript
rest: {
  basePath: '/v1/plugins/commit',
  routes: [
    {
      method: 'POST',
      path: '/generate',
      handler: './rest/handlers/generate-handler.js#default',
      input: { zod: '@kb-labs/commit-contracts#GenerateRequestSchema' },
      output: { zod: '@kb-labs/commit-contracts#GenerateResponseSchema' },
      timeoutMs: 300_000,
    },
  ],
}

ws

WebSocket channels for real-time, bidirectional communication.

TypeScript
interface WebSocketConfig {
  basePath?: `/v1/ws/plugins/${string}`;
  defaults?: {
    timeoutMs?: number;
    maxMessageSize?: number;
    auth?: 'none' | 'token' | 'session';
    idleTimeoutMs?: number;
  };
  channels: WebSocketChannelDecl[];
}

WebSocketChannelDecl

TypeScript
interface WebSocketChannelDecl {
  path: string;                  // '/live', '/chat'
  description?: string;
  protocol?: string;             // subprotocol
  handler: string;
  inputMessage?: SchemaRef;      // client → server
  outputMessage?: SchemaRef;     // server → client
  permissions?: PermissionSpec;
  timeoutMs?: number;
  maxMessageSize?: number;       // bytes
  auth?: 'none' | 'token' | 'session';
  idleTimeoutMs?: number;
}

workflows

Workflow handlers that can be referenced from workflow step specs.

TypeScript
workflows?: { handlers: WorkflowHandlerDecl[] }
 
interface WorkflowHandlerDecl {
  id: string;              // 'sync-dependencies'
  describe?: string;
  handler: string;
  input?: SchemaRef;
  output?: SchemaRef;
  permissions?: PermissionSpec;
}

webhooks

Handlers triggered by external events.

TypeScript
webhooks?: { handlers: WebhookHandlerDecl[] }
 
interface WebhookHandlerDecl {
  event: string;           // 'github:push', 'slack:message'
  describe?: string;
  handler: string;
  input?: SchemaRef;
  permissions?: PermissionSpec;
}

jobs

Background task handlers invoked on-demand via ctx.api.jobs.submit(). These differ from cron schedules: jobs are one-shot handlers; cron entries schedule them.

TypeScript
interface JobsConfig {
  handlers: JobHandlerDecl[];
  defaults?: {
    timeout?: number;
    maxRetries?: number;
    retryBackoff?: 'exp' | 'lin';
  };
}
 
interface JobHandlerDecl {
  id: string;
  describe?: string;
  handler: string;
  input?: SchemaRef;
  output?: SchemaRef;
  timeout?: number;
  maxRetries?: number;          // default 3
  retryBackoff?: 'exp' | 'lin';
  permissions?: PermissionSpec;
}

cron

Recurring schedules that fire job handlers.

TypeScript
cron?: { schedules: CronDecl[] }
 
interface CronDecl {
  id: string;
  schedule: string;         // '0 * * * *' or '5m' / '1h' / '1d'
  job: {
    type: string;           // 'pluginId:jobId' or just 'jobId'
    payload?: unknown;
  };
  describe?: string;
  enabled?: boolean;
  timezone?: string;        // default UTC
  permissions?: PermissionSpec;
}

The loader rejects cron entries missing schedule or job.type.

studio

Studio UI extensions exposed via Module Federation.

TypeScript
interface StudioConfig {
  version: 2;
  remoteName: string;        // MF remote name, e.g. 'commitPlugin'
  pages: StudioPageEntry[];
  menus?: StudioMenuEntry[];
}

The version field is a literal 2. There is no Studio v1 migration path in the v3 manifest — legacy widget configs are not supported.

StudioPageEntry

TypeScript
interface StudioPageEntry {
  id: string;                // 'commit.overview' — dot-namespaced
  title: string;
  icon?: string;             // AntD icon name
  route: string;             // '/commit'
  entry: string;             // MF exposed module path, e.g. './CommitOverview'
  permissions?: string[];    // permission IDs required to view this page
  order?: number;
}

StudioMenuEntry

TypeScript
interface StudioMenuEntry {
  id: string;
  label: string;
  icon?: string;
  target: string;            // page ID or external URL
  order?: number;
  parentId?: string;         // for nesting
  permissions?: string[];
  badge?: string;
}

Example

TypeScript
studio: {
  version: 2 as const,
  remoteName: 'commitPlugin',
  pages: [
    {
      id: 'commit.overview',
      title: 'Commit',
      icon: 'GitlabOutlined',
      route: '/p/commit',
      entry: './CommitOverview',
      order: 1,
    },
  ],
  menus: [
    {
      id: 'commit',
      label: 'Commit',
      icon: 'GitlabOutlined',
      target: 'commit.overview',
      order: 30,
    },
  ],
}

Validation

The loader in infra/kb-labs-plugin/packages/plugin-contracts/src/manifest-loader.ts enforces:

  • schema must be the literal 'kb.plugin/3' — any other value raises Invalid manifest: expected schema "kb.plugin/3", got "<value>".
  • id is present and matches /^@[a-z0-9-]+\/[a-z0-9-]+$/.
  • version is present and matches /^\d+\.\d+\.\d+/.
  • Every cli.commands[i] has a handler.
  • Every rest.routes[i] has a handler.
  • Every workflows.handlers[i] has a handler.
  • Every webhooks.handlers[i] has a handler.
  • Every jobs.handlers[i] has a handler.
  • Every cron.schedules[i] has both schedule and job.type.

Validation errors are collected and returned from validateManifest() as a list, so a single run surfaces every problem at once.

Handler resolution helper

The contracts package exports two helpers for resolving handler paths and permissions at runtime:

TypeScript
getHandlerPath(manifest, host, id): string | undefined
getHandlerPermissions(manifest, host, id): PermissionSpec

Where host is one of 'cli' | 'rest' | 'ws' | 'workflow' | 'webhook'. Permission resolution merges plugin-wide permissions with the handler-specific overrides — handler fields win where they're set.

Full working example

The most exercised reference manifest in the monorepo is plugins/kb-labs-commit-plugin/packages/commit-cli/src/manifest.ts. It demonstrates:

  • Composable permissions via combinePermissions().
  • Six CLI commands with typed flags from a shared module.
  • A REST surface of 14 routes with Zod schemas and per-route timeouts.
  • A Studio v2 page and matching menu entry.

Copy it as a starting point when writing your own plugin.

Manifest Reference (v3) — KB Labs Docs