PermDock
Research

OpenAPI ecosystem, September 2026

How PermDock's Overlay and standard security output fit the TypeScript OpenAPI toolchain (next-openapi-gen, hono-openapi, TypeSpec, Hey API, Orval, Kubb, Redocly, Bump.sh, Scalar, Mintlify, Fern, Speakeasy, Schemathesis, Arazzo runners, OpenAPI-to-MCP bridges), which tools are producers, appliers or consumers, and what PermDock adopts, adapts and avoids.

Source: a review run in September 2026 of the tools a TypeScript team already has in its OpenAPI pipeline, asking what PermDock must emit so each of them works without knowing PermDock exists, and where a recipe is enough versus where an adapter would be needed. Sources were the tools' documentation and repositories linked below. The review produced ADR 0023 and the recipes on the OpenAPI adapter, Next.js adapter, MCP adapter, CLI: openapi and Overlay pages.

The pipeline

Every TypeScript OpenAPI setup, whatever the framework, reduces to the same four stages. PermDock occupies one of them.

Routes with protect Producer: spec from code permdock openapi emit --format overlay Applier Applied description with security Docs UI: Scalar SDK generators: Hey API, Orval OpenAPI-to-MCP bridge Arazzo runner plus simulate
  1. Producer. Something turns route handlers into an OpenAPI description: a framework plugin (hono-openapi, @hono/zod-openapi, @orpc/openapi, trpc-to-openapi, @elysia/openapi, @fastify/swagger, @nestjs/swagger) or a scanner that reads route files (next-openapi-gen). Producers own paths, parameters, schemas and operationIds.
  2. PermDock. permdock openapi emit --format overlay reads the catalog and the description and writes an Overlay that adds securitySchemes, per-operation security and x-permdock-*. Where PermDock has an in-process hook (Hono, oRPC, tRPC, Elysia, Fastify, Nest) the same fields are attached during generation and the Overlay is optional.
  3. Applier. An Overlay 1.x applier merges the Overlay into the description. PermDock does not ship one (0019); the project's existing tool does it.
  4. Consumers. Docs UIs, SDK generators, OpenAPI-to-MCP bridges, Arazzo runners, gateways and permdock openapi import read the applied description. They see standard security; the ones that opt in also read x-permdock-*.

The join key across all four stages is operationId. Producers generate it, the Overlay targets it, Arazzo steps reference it, and --check fails when it is missing.

Producers

ToolFrameworksSecurity inputPermDock pathNotes
next-openapi-genNext.js, TanStack Start, React Router, Remix, SvelteKit, Nuxt, Astro, Hono, ExpressJSDoc @auth, authPresets; overlay.applyOverlayScans route files; targets 3.0 to 3.2; scaffolds Scalar; compiles Arazzo. The Next.js recipe
hono-openapiHonodescribeRoute({ security })In-process describe()Already a hook on the Hono adapter
@hono/zod-openapiHonocreateRoute({ security })In-process security()Same
@orpc/openapioRPCoo.spec()In-process spec()oRPC adapter
trpc-to-openapitRPCmeta.openapi.protect: trueIn-process describe(), Overlay for per-scopeBoolean protect only; the Overlay adds the scopes (tRPC adapter)
@elysia/openapiElysiadetail.securityIn-processScalar is the default UI (Elysia adapter)
@fastify/swagger, @nestjs/swaggerFastify, NestSchema security, decoratorsIn-processFastify, Nest
Hand-written YAML or JSONAnyAuthor writes securityOverlayDesign-first teams; the Overlay keeps PermDock's contribution separate
TypeSpec 1.0Design-first, any framework@useAuth decorators compiled by @typespec/openapi3Overlay on the emitted document1.0 GA; emits OpenAPI 3.0, 3.1 and 3.2; an MCP server emitter is being explored upstream. The Overlay is applied to the emitter output, never to the .tsp source, so TypeSpec stays the single authoring surface
tsoa, ts-rest, Effect HttpApi, feTSExpress, Koa, Hapi (tsoa); any (ts-rest, Effect); any Fetch runtime (feTS)Decorators or contract-level securityOverlayCode-first producers with no per-route hook PermDock can attach to; the same two-command recipe as next-openapi-gen

next-openapi-gen matters most because it is the only producer for Next.js route handlers, which is where permdock/next lives. Its overlay.apply option means the Overlay is applied inside the same generate run, so the recipe is two commands and no glue code.

Appliers

ToolWhere it runsOverlay versionsNotes
next-openapi-gen overlay.applyInside openapi-gen generate1.0, 1.1, 1.2Applies before writing the spec, so Scalar and Arazzo see the applied result; the first applier PermDock's --overlay 1.2 output is tested against
Redocly CLI join --overlayLint, bundle, generate-client pipelines1.xExperimental flag; redocly lint also validates Overlay documents. Common in design-first teams
Scalar CLIDocs and registry pipelinesnone yetOverlay support is on Scalar's roadmap; apply with one of the above before scalar registry publish
Bump.sh CLI bump overlayDocs deploy pipelines; the Bump.sh GitHub Action takes an overlay: input1.xApplies at deploy time so the hosted docs show the applied description while the repository keeps the producer's file untouched
Speakeasy CLI speakeasy overlay applySDK generation workflows1.xSpeakeasy's own extensions arrive through overlays too, so a project can chain PermDock's Overlay and Speakeasy's in one workflow
Zuplo CLIGateway import1.xApplies before the description is imported as gateway routes
openapi-format, oas-patch, overlays-jsGeneric CLIs and libraries1.xVendor-neutral appliers for pipelines without one of the above; overlays-js is the reference JavaScript implementation

PermDock emits Overlay 1.1.0 by default and, behind --overlay 1.2, the pinned Overlay 1.2 draft, whose one addition is reusable actions (components.actions plus $ref references); the earlier targetFormat idea is not in the v1.2-dev text. The 1.2 form collapses the repeated per-operation update bodies into one reusable action per permission set. It stays opt-in until 1.2.0 ships and the appliers below accept it (Overlay, ADR 0025).

Consumers

ToolRoleReadsWhat PermDock must doRecipe or adapter
Hey API (@hey-api/openapi-ts)Typed SDKs, TanStack Query hooks, validatorssecurity, securitySchemesEmit standard fieldsRecipe: point it at the applied description. A plugin reading x-permdock-* is Phase 4 at earliest
OrvalClients, TanStack Query, MSW mocks, MCP serverssecurityEmit standard fields; x-permdock-permissions for the MCP outputRecipe; see bridges below
Redocly generate-clientClientssecurityEmit standard fieldsRecipe
openapi-typescript with openapi-fetchTypes-only generation plus a typed fetch wrapper; the most used TypeScript generatorSchemas; security is not projected into typesNothing; credentials are attached by the applicationNothing to do. openapi-fetch is in maintenance mode, which is one reason Hey API is the recommended SDK path
KubbPlugin-based generator: types, clients, TanStack Query, MSW, Zod, Redoc docs, MCP serverssecurity; x-permdock-permissions for the MCP pluginEmit standard fieldsRecipe; the MCP output follows the bridge recipe below
OpenAPI Generator (typescript-fetch, typescript-axios), KiotaMulti-language generatorssecurity, securitySchemesEmit standard fieldsNothing to do; teams generating non-TypeScript clients from the same description get the same scopes
Fern, SpeakeasyCommercial SDK, docs and MCP-server generatorssecurity; own x-fern-* and x-speakeasy-* extensionsEmit standard fields; never write their namespacesRecipe; see bridges below for the MCP output. Stainless, the third of this group, was acquired by Anthropic on 18 May 2026 and its hosted generator is being wound down; the x-stainless-* never-write rule stays because published descriptions still carry the fields
Scalar API ReferenceDocs UI, Try-it clientsecurity, OAuth scopes, x-badges, several vendor code-sample extensionsStandard fields; optional x-badges approval hintRecipe; an operation-level plugin for x-permdock-* needs upstream plugin API support first
Swagger UI, Redoc, Stoplight Elements, RapiDoc, fumadocs-openapiDocs UIssecurityStandard fieldsNothing to do
API gateways (Kong, Envoy, Tyk, Zuplo)Enforcement from the description or from AuthZENsecurity; AuthZENStandard fields; permdock/authzenAlready covered on AuthZEN
Spectral, Redocly lint, vacuumLintersWhole documentShip a ruleset filePhase 2 artefact, not a package
permdock openapi importPermDock's own consumersecurity, x-permdock-*Round-trips its own outputCLI: openapi
Arazzo runners, permdock.simulateWorkflow pre-flightoperationId, x-permdock-permissionsRun on the applied descriptionArazzo

Docs hosts

Hosted documentation products are consumers too, and every one of them is a commercial product a team may already pay for. All read standard security and securitySchemes; several add their own extensions, which PermDock never writes (registries).

HostOwn extensionsApplies OverlaysAlso generatesNotes
Mintlifyx-mint (page metadata, href), x-mcp (per-operation MCP exposure)No; apply before uploadAn MCP server from the docs and the description, honouring securitySchemesThe most direct overlap with the bridge recipe below: an operation's x-mcp opt-in should match whether it carries x-permdock-permissions
Bump.shx-topics (narrative sections)Yes, bump overlay and the GitHub ActionChangelog and diffThe applier and the host are one tool; a natural fit for --check in the same workflow
Fern docsx-fern-*NoSDKs and an MCP server from the same descriptionDocs, SDK and MCP from one applied description
Zudokunone requiredNononeZuplo's open-source docs UI; reads security and lets a team self-host
Redocly Realmx-* rendering hints of its ownYes, through Redocly CLIMock serverThe Redocly CLI applier feeds it
Docusaurus OpenAPI, GitBook, PostmanPostman collections import securityNoPostman: collections and an MCP serverImport the applied description

OpenAPI-to-MCP bridges

Orval, Kubb, Scalar, Speakeasy, Fern, Mintlify, Postman's MCP Generator, Zuplo, Kong's openapi2mcp, AWS Bedrock AgentCore Gateway and several smaller tools generate an MCP server from an OpenAPI description, one tool per operation. Agents then call the API through that server. Without PermDock in the loop the generated tools have no permission binding and permdock/mcp's list_tools filtering and approval-required elicitation never run. The recipe on the MCP adapter: read the operation's x-permdock-permissions from the applied description and bind it as the generated tool's permission. Standard security is not enough here because the bridge needs the permission key, not the scope; this is the one consumer for which the extension carries information the standard field cannot.

Several bridges carry their own per-operation extension for tool exposure, and each is a consumer-side concern that PermDock never writes: Mintlify x-mcp, Zuplo x-zuplo-route.mcp, Kong x-kong-mcp-tool-name and the ai-mcp-proxy plugin, Speakeasy x-speakeasy-mcp. Two deserve a note:

  • Speakeasy scopes. x-speakeasy-mcp.scopes tags each tool with scope strings and the generated server takes --scope flags at start time, so it is a coarse, deploy-time filter, not a per-caller check. The recipe is a consumer-side overlay that derives x-speakeasy-mcp.scopes from the operation's permission.scope values (post:delete becomes a Speakeasy scope of the same name), so the two models agree, while permdock/mcp remains the per-caller list_tools and call_tool check inside the generated server.
  • AgentCore Gateway. The gateway turns OpenAPI targets into MCP tools and applies Cedar policies whose context.toolName is the operationId (commercial landscape). Because the tools are MCP, protectServer still applies when the server runs in your process; when Amazon hosts the target, the Cedar policy is the enforcement point and PermDock's contribution is the applied description's security and the operation ids it can check against.

Testing and diff

Three tool classes read security in ways that turn PermDock's output into a runtime check:

  • Schemathesis has an ignored_auth check: for every operation declaring security, it sends the request without credentials and fails if the server answers anything other than 401 or 403. Run against the applied description, it is a parity test that every operation PermDock says is protected actually rejects an anonymous call, which catches a route that lost its protect after the description was published.
  • oasdiff treats a removed or weakened security requirement as a breaking change; a CI step comparing the previous and current applied descriptions is a second line behind permdock openapi --check.
  • Prism and Microcks mock servers answer 401 for operations with security when credentials are absent, so client-side tests exercise the denied path against the same description.

Scalar specifics

Scalar owns the registered scalar namespace; PermDock never writes into it (registries). Scalar renders x-badges (unregistered, rendering-only) on operations, which is enough to show "Approval required" next to a publishPost operation without Scalar understanding PermDock. Scalar's plugin API has historically bound custom x-* components to Info, Tag and Schema objects rather than operations (plugins); an operation-level PermDock plugin waits for that to change upstream.

Hey API specifics

Hey API clients attach credentials from the description's security schemes (clients), so a PermDock-annotated description produces SDKs whose calls carry the right scopes with no PermDock code. Its custom plugin API iterates operations (plugin.forEach('operation', ...), custom plugins), which is where a later plugin would read x-permdock-permissions and emit a typed permissions map beside each SDK function. Recorded as Phase 4, only if the recipe proves insufficient.

Two sources of truth

The one failure mode of composing is two tools writing security on the same operation. next-openapi-gen's @auth JSDoc tag and authPresets, hono-openapi's security option and hand-written security in a YAML file all compete with the Overlay. The rule: on operations PermDock covers, PermDock owns security and the producer owns everything else. permdock openapi emit --check reports operations whose source already carries security that the Overlay would replace, so the conflict is caught in CI rather than in a published description.

Adjacent formats

  • AsyncAPI. Arazzo 1.1 accepts AsyncAPI sources, and an Overlay's JSONPath targets are not bound to OpenAPI. PermDock has no AsyncAPI emitter; if one is ever needed it is the same Overlay engine pointed at an AsyncAPI document, not a new adapter. Tracked.
  • Problem Details. The 403 body PermDock documents on protected operations (Problem Details) is what generated SDKs surface as the typed error, and what MSW fixtures in @permdock/testing return (testing).
  • Arazzo. next-openapi-gen's arazzo block compiles workflow files against the generated operationIds after the spec write; simulate({ arazzo, openapi }) must read the applied description so x-permdock-permissions is present (Arazzo).

Adopt / adapt / avoid

Adopt:

  • The Overlay as the hand-off between PermDock and every producer it has no in-process hook for; operationId as the join key.
  • Standard security and securitySchemes as the contract with SDK generators, docs UIs and gateways, so Hey API, Orval, Redocly and Scalar work with no PermDock code.
  • next-openapi-gen overlay.apply as the Next.js applier and Scalar as the default docs UI it scaffolds.
  • The existing appliers (next-openapi-gen, Redocly CLI, Bump.sh, Speakeasy, overlays-js) instead of shipping one.
  • An existing OpenAPI parser for permdock openapi rather than a hand-rolled AST.
  • Schemathesis ignored_auth and oasdiff as the runtime and diff checks behind --check, documented on the CLI page.
  • TypeSpec and other design-first producers through the same Overlay, applied to emitter output.

Adapt:

  • x-badges as an opt-in approval hint for docs UIs that render it; no semantics, off by default.
  • A Spectral / Redocly / vacuum ruleset expressing PermDock's document invariants, shipped as a file.
  • x-permdock-permissions as the binding OpenAPI-to-MCP bridges read, documented as a recipe on the MCP adapter page.
  • A consumer-side overlay deriving Speakeasy x-speakeasy-mcp.scopes and Mintlify x-mcp opt-ins from PermDock's fields, so a team's docs host and SDK vendor agree with the policy without PermDock writing their namespaces.
  • Later first-party plugins (Hey API operation plugin, Scalar operation plugin) only if recipes fail, and only after upstream plugin APIs support the operation level.
  • Overlay 1.2 as an opt-in output (--overlay 1.2) pinned to v1.2-dev, default after release and applier support.

Avoid:

  • Wrapping a producer, generator or docs UI in a PermDock package.
  • Two sources of security on one operation.
  • Writing into x-scalar-*, x-stainless-*, x-readme, x-mint, x-mcp, x-speakeasy-*, x-fern-*, x-topics, gateway import namespaces (x-amazon-apigateway-*, x-google-*, x-kong-*, x-zuplo-*) or any x-oai-* name that is not registered.
  • A docs-host or generator package (permdock/mintlify, permdock/bump, permdock/fern): each is reached through the applied description.
  • Making the Overlay the CLI default before the document form is unnecessary.
  • Moving Arazzo simulate earlier than Phase 4 because a producer happens to compile Arazzo.

Decisions informed

On this page