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
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.
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.
interface DisplayMetadata {
name: string;
description?: string;
author?: string;
homepage?: string;
repository?: string;
tags?: string[];
icon?: string; // URL or emoji
}Example:
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():
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.
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.
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.
platform: {
requires: ['storage', 'cache'],
optional: ['llm', 'analytics', 'logger'],
}setup
An installation-time handler that runs once, typically to create directories or seed configuration.
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.
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.
cli?: {
commands: CliCommandDecl[];
groupMeta?: CliGroupMeta[];
}CliCommandDecl
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.
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.
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:
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.
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
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.
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.
interface ErrorSpec {
code: string; // 'CONFIG_NOT_RESOLVED'
http: number; // 400–599
description?: string;
}Example
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.
interface WebSocketConfig {
basePath?: `/v1/ws/plugins/${string}`;
defaults?: {
timeoutMs?: number;
maxMessageSize?: number;
auth?: 'none' | 'token' | 'session';
idleTimeoutMs?: number;
};
channels: WebSocketChannelDecl[];
}WebSocketChannelDecl
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.
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.
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.
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.
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.
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
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
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
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:
schemamust be the literal'kb.plugin/3'— any other value raisesInvalid manifest: expected schema "kb.plugin/3", got "<value>".idis present and matches/^@[a-z0-9-]+\/[a-z0-9-]+$/.versionis present and matches/^\d+\.\d+\.\d+/.- Every
cli.commands[i]has ahandler. - Every
rest.routes[i]has ahandler. - Every
workflows.handlers[i]has ahandler. - Every
webhooks.handlers[i]has ahandler. - Every
jobs.handlers[i]has ahandler. - Every
cron.schedules[i]has bothscheduleandjob.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:
getHandlerPath(manifest, host, id): string | undefined
getHandlerPermissions(manifest, host, id): PermissionSpecWhere 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.