@plumbus/api
v0.1.2
Published
Plumbus external API contract layer — partner-grade API exposure, OpenAPI, docs, and test intent
Downloads
706
Readme
@plumbus/api
Partner-grade external API contracts for Plumbus apps. Mark capabilities with
exposeAs: ['api'], maintain anapi.yamlmanifest, and serve a versioned partner surface — with the same validation, access policies, audit pipeline, and Zod schemas as your default HTTP routes.
What is this?
Plumbus is an AI-native, contract-driven TypeScript application framework. You declare capabilities, entities, events, flows, prompts, and translations through define*() functions; the framework generates routes, validation, audit, security, and types.
@plumbus/api is the external API contract layer for that framework. It layers a named, partner-facing HTTP surface on top of the default convention routes in @plumbus/core — custom paths, API versioning, OpenAPI export, Markdown docs, compatibility diffing, and test-intent fixtures — without duplicating handlers, auth, or audit.
If you're not using Plumbus, this package can't be used in isolation. The runtime composes on executeCapability and the framework's access-policy pipeline; it doesn't re-implement REST dispatch from scratch.
Why?
If you've ever published an API for partners, you've probably written something like:
- A second route table with hand-maintained paths and methods
- A separate OpenAPI spec that drifts from the real handlers
- Ad-hoc breaking-change checks before each release
- Fixture files that nobody validates against Zod schemas
Plumbus already has typed capability contracts. This package projects them into a versioned partner API with generated OpenAPI, docs, and CI-friendly diff. One handler, two HTTP surfaces, one audit log.
What you get
| Surface | What it does |
|---|---|
| exposeAs: ['api'] + api metadata | Opt-in projection per capability — operationId, method, path, stability, auth.scopes. |
| api.yaml manifest | Named API products (basePath, version, route overrides, test-intent config). Inline metadata is the default when no manifest exists. |
| registerApiRoutes() | Mount partner routes on Fastify. Full pipeline: Zod validation → scope check → access policy → handler → audit → envelope. |
| OpenAPI 3.0.3 | generateOpenApi / plumbus api generate openapi from Zod schemas + manifest. Canonical partner spec (distinct from the thin plumbus generate convention spec). |
| Markdown docs | generateApiDocs / plumbus api generate docs — per-operation reference pages. |
| Compatibility diff | diffOpenApi / plumbus api diff --against — breaking vs non-breaking change report for CI. |
| Test intent | ?intent=test / header-driven fixture replay for partner integration tests (validated by plumbus api test-fixtures validate). |
| Idempotency | Idempotency-Key support with pluggable store; in-memory default for dev/tests. |
| Policy validation | Structure policy, path-param mapping, fixture schema checks — surfaced in plumbus api validate. |
| Governance | Advisory API rules also run in plumbus verify when capabilities expose the API surface. |
When to use this vs alternatives
| You want | Reach for |
|---|---|
| App-internal routes for your own UI (/api/{domain}/{kebab-name}) | Default HTTP runtime in @plumbus/core |
| Typed client generation for your Next.js app | @plumbus/ui |
| Expose a capability to an AI agent | @plumbus/mcp |
| Versioned, documented partner API with OpenAPI + diff | @plumbus/api (this package) |
Status
Optional peer of @plumbus/core (version-locked 0.1.x; requires @plumbus/core 0.5.x). Implements manifest validation, route registration, OpenAPI/docs generation, compatibility diff, test intent, and idempotency. OAuth gateways, rate limiting, and durable idempotency stores are app-owned — see Key gotchas.
Install
pnpm add @plumbus/apiIf your agent wiring (AGENTS.md, Cursor, Copilot) predates the current template, refresh it so agents discover these instructions:
plumbus init --patch --agent agents-md # AGENTS.md only; omit --agent for all formats
plumbus doctor # confirms wiring is current@plumbus/core works without @plumbus/api — default convention routes and plumbus generate still run. Install this package when you want a published partner API with manifest validation, OpenAPI export, and registerApiRoutes(). plumbus api validate prints an install hint when the package is missing.
Peer: @plumbus/core 0.5.x. The framework provides Zod and Vitest transitively — do not add them to your own package.json for API work.
Quick start
1. Mark a capability for API exposure
import { defineCapability } from '@plumbus/core';
import { z } from '@plumbus/core/zod';
export const getRefund = defineCapability({
name: 'getRefund',
kind: 'query',
domain: 'billing',
description: 'Fetch a refund by id',
exposeAs: ['api'],
api: {
operationId: 'getRefund',
method: 'GET',
path: '/refunds/{refundId}',
stability: 'stable',
auth: { scopes: ['refunds:read'] },
},
input: z.object({ refundId: z.string() }),
output: z.object({ id: z.string(), amount: z.number() }),
access: { roles: ['partner'], scopes: ['refunds:read'], tenantScoped: true },
effects: { data: ['Refund'], events: [], external: [], ai: false },
handler: async (ctx, { refundId }) => ctx.data.Refund.byId(refundId),
});2. (Optional) Maintain api.yaml
apiVersion: plumbus.dev/v1
name: partner-api
basePath: /api/v1
expose:
- capability: billing.getRefund
operationId: getRefund
method: GET
path: /refunds/{refundId}When ./api.yaml is missing, the CLI and runtime fall back to inline api metadata. An explicit --manifest path that is missing or invalid fails with a clear error.
3. Register partner routes
import { registerApiRoutes } from '@plumbus/api';
import { onRoutesRegistered } from '@plumbus/core';
import manifest from './api.yaml' assert { type: 'yaml' }; // or load at boot
onRoutesRegistered(async (app, routeConfig) => {
await registerApiRoutes(app, routeConfig, capabilities, { manifest });
});4. Validate and publish artifacts
plumbus api validate
plumbus api generate openapi --out ./dist/openapi.json
plumbus api generate docs --out ./dist/api-docs
plumbus api diff --against ./published/openapi-v1.json
plumbus api test-fixtures validateHow requests flow
partner HTTP request
│
▼
authAdapter.authenticate(credentials) ← same adapter as convention routes
│
▼
scope check (api.auth.scopes) ← missing scope → 403 missing_scope
│
▼
createExecutionContext + evaluateAccess
│
▼
executeCapability(cap, ctx, args) ← full Plumbus pipeline:
│ Zod validation → access policy
│ → handler → audit → output validation
▼
{ ok: true, data } | { ok: false, error } ← partner envelopeConvention routes (/api/{domain}/{kebab-name}) continue to exist unless you choose not to register them. Partner routes use manifest basePath + per-operation path.
Public API
| Export | Purpose |
|---|---|
| registerApiRoutes(app, routeConfig, capabilities, opts?) | Mount partner API routes on Fastify. Opts: manifest, allowQueryIntent, appRoot, idempotencyStore. |
| parseManifest, validateManifest, resolveExposure, buildDefaultManifest | Manifest load, validation, and exposure resolution. |
| validatePathParams | Path-template ↔ input-field mapping checks. |
| generateOpenApi, serializeOpenApiDocument, parseOpenApiDocument, zodToOpenApiSchema | OpenAPI 3.0.3 generation and serialization. |
| diffOpenApi | Breaking / non-breaking diff between two OpenAPI documents. |
| generateApiDocs | Markdown documentation per operation. |
| validateApiContract | Combined manifest, policy, path-param, and fixture validation. |
| validatePolicy, validateTestFixtures | Structure-policy and test-fixture validation. |
| buildSuccessEnvelope, mapCoreError, mapUnknownError, … | Partner response envelope helpers. |
| createInMemoryIdempotencyStore, parseIdempotencyTtl, IdempotencyAbortedError | Idempotency store for dev/tests; supply a durable store in production. |
| ApiManifestError | Structured manifest parse/validation errors. |
CLI
Commands ship in @plumbus/core and dynamically import this package:
| Command | Purpose |
|---|---|
| plumbus api validate | Manifest, policy, path params, fixtures, and advisory governance. |
| plumbus api generate openapi --out <file> | Write OpenAPI JSON or YAML (--format yaml). |
| plumbus api generate docs --out <dir> | Write Markdown API reference. |
| plumbus api diff --against <file> | Compare current spec to a published baseline; exits non-zero on breaking changes. |
| plumbus api test-fixtures validate | Validate test-intent fixture files against capability schemas. |
All commands accept --manifest <path> (default ./api.yaml) and --json where applicable.
Key gotchas
exposeAs: ['api']is required. There is noapi.exposeboolean.kind: 'eventHandler'capabilities cannot be API-exposed.api.auth.scopesare enforced at runtime (union withaccess.scopeswhen both are set). Missing scopes return403 missing_scope.- Path parameters must map to input fields.
{refundId}in the route must exist on the capabilityinputschema. Duplicates in the same path are rejected. - Default idempotency store is in-memory. Fine for dev and single-instance tests; production multi-instance deployments need a durable
idempotencyStorewith TTL. plumbus generateOpenAPI ≠ partner OpenAPI. The thin convention spec fromplumbus generateis for app-internal use;plumbus api generate openapiis the canonical partner contract.- Public capabilities + test intent is forbidden.
plumbus api validateflagspolicy.public-test-forbiddenwhenaccess.public: trueenables test fixtures.
Documentation
- Agent recipes (ship in this package, readable from
node_modules/@plumbus/api/instructions/):instructions/framework.md— package boundary, public exports, critical rulesinstructions/expose-a-capability.md— full recipe withexposeAs,apimetadata, bootstrapinstructions/manifest-and-cli.md—api.yaml, validation, OpenAPI/docs, diff, CLIinstructions/testing.md— test intent, idempotency, fixture validation, route tests
- Concepts and reference (in the monorepo):
docs/api/overview.md— surfaces, workflow, monorepo relationshipexposure-model.md—exposeAs, inlineapimetadata, auth, idempotencymanifest.md—api.yamlstructure and precedenceopenapi.md— OpenAPI generation and formatsstructure-policy.md— API structure policy rulestest-intent.md— fixture replay and validationcompatibility.md— breaking-change diff workflowgovernance.md— advisory rules in verify and validate
The Plumbus ecosystem
| Package | Purpose | When to install |
|---|---|---|
| @plumbus/core | Foundation — capabilities, entities, events, flows, prompts, translations, runtime, CLI, audit, governance. | Always (required). |
| @plumbus/ui | Next.js/React UI — typed API clients, auth helpers, form metadata, scaffolds. | When building a Plumbus web UI. |
| @plumbus/api | You are here. Partner external API — manifest, OpenAPI, docs, compatibility diff, test intent. | Optional peer 0.1.x — when publishing a documented partner-facing HTTP API. |
| @plumbus/mcp | MCP runtime — serve capabilities to AI agents (tools/*, tasks/*, transports). | Optional peer 0.5.x — when exposing capabilities to MCP clients. |
| @plumbus/chat | Conversational runtime — defineChat, policy guards, context sources, streamed events. | Optional peer 0.1.x — when adding a chat surface. |
| @plumbus/chat-ui | React chat UI — hooks and <ChatPanel /> for the @plumbus/chat turn protocol. | Peer of @plumbus/chat — when adding a browser chat client. |
| @plumbus/knowledge-base | Knowledge providers — scoped sources, registry, chat knowledgeContext integration. | Optional peer of @plumbus/chat 0.1.x — when sharing named knowledge across features. |
| @plumbus/voice | Real-time voice runtime — defineVoice, STT/TTS/transport providers, session worker, cost ledger. | Optional peer 0.1.x — when adding speech I/O (not speech-to-speech); complements @plumbus/chat text surfaces. |
| @plumbus/browser-extension | Extension scaffolder — WXT Chrome/Firefox project wired to your capabilities. | With @plumbus/ui (0.1.x) — when shipping a browser extension UI. |
Links
- Plumbus framework — github.com/plumbus-framework/plumbus
- Full documentation — docs/ in the monorepo
- Top-level README —
../../README.md - Issues — github.com/plumbus-framework/plumbus/issues
Testing
Framework developers run package tests from the monorepo:
cd packages/api && pnpm testConsumer apps validate contracts via plumbus api validate and plumbus api test-fixtures validate. Route behavior is covered by integration tests that call registerApiRoutes with createTestContext from @plumbus/core/testing.
License
MIT
