0023: Compose with the OpenAPI toolchain, do not wrap it
Why PermDock reaches spec producers (next-openapi-gen, hono-openapi, @orpc/openapi), SDK generators (Hey API, Orval) and docs UIs (Scalar) through standard security fields and an Overlay rather than per-tool packages, why the same rule applies to every adjacent ecosystem, and what stays out of scope.
Status
Accepted, September 2026. Extends 0019: the x-permdock-* namespace, registered fallbacks and Overlay output stand; this decision fixes how that output meets the rest of the TypeScript OpenAPI toolchain and records the general rule for adjacent ecosystems.
The Overlay 1.2 bullet under Decision ("tracked, not emitted") is superseded by 0025, which builds the pinned 1.2 draft behind --overlay 1.2 with 1.1.0 as the default. The targetFormat field that bullet names is not in the v1.2-dev text. Everything else here stands.
Context
The docs described PermDock's OpenAPI output in isolation. The adapter page named four in-process hooks (hono-openapi, @hono/zod-openapi, @orpc/openapi, trpc-to-openapi) and nothing downstream. Three gaps followed:
- The Next.js adapter had no OpenAPI path.
permdock/nextis Phase 1 and Next.js route handlers have no built-in spec generator, so the most-used adapter could not produce a document PermDock could annotate. next-openapi-gen fills that role in the ecosystem: it scans route handlers in Next.js, TanStack Start, React Router, Remix, SvelteKit, Nuxt, Astro, Hono and Express, targets OpenAPI 3.0 to 3.2, scaffolds Scalar as the default docs UI, applies Overlay files before writing the spec, and compiles Arazzo workflows against the generatedoperationIds. - SDK generators and docs UIs were never named. Hey API (
@hey-api/openapi-ts), Orval and Redocly'sgenerate-clientturn a description into typed clients; Scalar renders it. All of them read standardsecurityandsecuritySchemes. None of them knows PermDock, and none of them needs to if PermDock writes standard fields. - The same question recurs for every neighbouring ecosystem. Auth providers, agent frameworks, sync engines, audit sinks, feature-flag SDKs: each could become an adapter, and each time the answer should be the same unless the tool exposes a hook that only an adapter can reach.
The OpenAPI ecosystem research maps the pipeline and the tools in it.
Decision
- Compose, do not wrap. PermDock is the authorization contribution to an OpenAPI description, delivered as standard
securityplussecuritySchemesandx-permdock-*extensions. It does not generate routes, SDKs or documentation UIs, and it ships no package whose only purpose is to call another tool. Nopermdock/hey-api,permdock/scalaror next-openapi-gen adapter in Phase 2. - The Overlay is the hand-off. The documented recipe for any project whose description is generated by a tool PermDock has no in-process hook for (Next.js first) is: generate the description, run
permdock openapi emit --format overlay, apply the Overlay with the applier the project already has (next-openapi-genoverlay.apply, Redocly CLIjoin --overlay), then hand the applied description to SDK generators and docs UIs.--format documentstays the CLI default for continuity (0019); the Overlay is the documented default whenever the description is generated. operationIdis the join key. Overlay actions target operations byoperationId, producers generate them, andpermdock openapi emit --checkfails on operations without one. A producer's own security annotations (next-openapi-gen@authandauthPresets, for example) must not be used on operations PermDock covers; PermDock ownssecurity, the producer owns paths and schemas.- Standard fields first, extensions second, other namespaces never. Consumers must work from
securityalone.x-permdock-*adds information for consumers that opt in (the importer,simulate, generated MCP servers). PermDock never writes into a registered namespace it does not own (x-scalar-*,x-stainless-*,x-readme,x-oai-*beyond the three registered names). One unregistered, widely rendered field is allowed as an opt-in docs hint:x-badgeswithname: "Approval required"on operations whose permission carriesapproval, off by default. - Lint rules, not a linter. A ruleset for Spectral, Redocly and vacuum ships as a file in Phase 2: every operation with
x-permdock-permissionshassecurity; nox-oai-*outside the registered three; noremoveonsecurityin an Overlay.permdock openapi emit --checkremains the authoritative gate. - Bridges read the extension. OpenAPI-to-MCP generators (Orval, Scalar, Speakeasy and similar) produce MCP servers that know nothing about PermDock. The documented recipe binds each generated tool's
permissionto the operation'sx-permdock-permissions;permdock openapi importis the reverse direction. This is how the OpenAPI and agent stories meet without a package. - The general rule. An ecosystem is reached through a wire format or a recipe (OpenAPI, Overlay, AuthZEN, Standard Schema, JWT and JWKS, OCSF, Problem Details) unless the tool exposes a runtime hook that only in-process code can occupy (a
protectmiddleware slot, acanUseToolcallback, an ORM query builder). Auth providers beyond the four planned are recipes on authentication; agent frameworks beyond the five planned are a research table; sync engines and MongoDB arewheretargets decided at Phase 3; sinks and flag SDKs are recipes. The one new build artefact this decision adds is anunplugin-basedcollecthook so Vite-family frameworks get build-time collection before their Phase 4 adapters.
Consequences
- The Next.js adapter has an OpenAPI story in Phase 2 without a new adapter: next-openapi-gen produces, PermDock overlays, next-openapi-gen applies and scaffolds Scalar.
- Hey API, Orval, Redocly, Scalar and any other standard-compliant consumer work on day one and are listed as such; PermDock carries no maintenance burden for their release cycles.
- Two producers writing
securityon one operation is the main failure mode of the recipe; the Next.js and Overlay pages warn against it and--checkreports operations whose source already carriessecurityPermDock would overwrite. - The
x-badgeshint is the only field outsidex-permdock-*and the registered set, is opt-in, and is documented as a rendering hint with no semantics; the fixture rule from 0019 ("no otherx-oai-*name") extends to "no other vendor namespace". - Later first-party plugins (a Hey API plugin emitting typed
permissionsmaps, a Scalar operation plugin renderingx-permdock-*) are Phase 4 candidates only if recipes prove insufficient, and Scalar's plugin API must first support operation-level custom extensions; an upstream issue precedes any PermDock workaround. - Overlay 1.2 (reusable actions,
targetFormat) is tracked, not emitted; next-openapi-gen already accepts 1.0 to 1.2, so PermDock can move when it needs a 1.2 feature. - Parser choice for
permdock openapiis an implementation note recorded when the CLI lands: prefer an existing parser (@scalar/openapi-parser,@hey-api/spec-typesor@readme/openapi-parser) over a hand-rolled AST so import and emit stay compatible with the tools in the pipeline. - The docs gain a runtime matrix, a provider recipes table, two research pages (agent frameworks, local-first sync) and watch-list rows (AsyncAPI Overlay, OCSF, SPIFFE, AG-UI) that all follow point 7 rather than adding entries to the adapter matrix.
Alternatives considered
- A
permdock/next-openapiadapter that drives next-openapi-gen. Rejected: it would couple a Phase 1 adapter to a third-party CLI's release cycle, duplicate the generator's configuration surface, and be obsolete the moment another producer is preferred. The Overlay recipe achieves the same result with two commands. - A Hey API plugin in Phase 2. Rejected for now: Hey API attaches auth from standard
securityalready, so the plugin would add only typed comments or apermissionsmap. Recorded as Phase 4 at earliest, behind evidence that the recipe is insufficient. - Emitting
x-scalar-*so Scalar renders approvals natively. Rejected:scalaris a registered namespace owned by Scalar; writing into it is the mistake 0019 rejected forx-oai-*.x-badgesis unregistered and rendering-only, which is why it is acceptable as an opt-in hint. - Shipping an Overlay applier. Still deferred (0019): next-openapi-gen and Redocly CLI already apply Overlays in the pipelines PermDock targets, and Scalar lists it as roadmap.
- One adapter per auth provider, agent framework and sink. Rejected by point 7:
subjectFromJwtcovers every JWKS issuer, most agent frameworks expose no hook beyond whatdecideandapproval-requiredalready serve, and aDecisionSinkis a three-line interface. Adapters are reserved for tools with a slot only in-process code can fill.
Related
- OpenAPI ecosystem research
- OpenAPI adapter, Next.js adapter, MCP adapter
- CLI: openapi
- OpenAPI Overlay, OpenAPI registries, Arazzo
- Authentication, Agent frameworks, Local-first sync engines
- 0006: explicit factory, not plugin, 0014: OpenAPI 3.2, 0019: extensions and Overlay, 0018: authentication is upstream
0022: Approvals are pluggable
Pending approvals live in an ApprovalStore interface with an in-memory default; the ApprovalRequest wire shape, approver identity, expiry, the PermDock-Approval resume header and plan-level approvals are fixed here so every agent adapter, self-hosted app and PermDock Cloud share one model.
0024: Scoped roles and memberships
Why roles gain a scope (tenant, team, resource) declared on the role, why the subject gains memberships and an active tenant, why tenant-defined custom roles are data over declared assignable roles, why RoleSource and MembershipSource are subject inputs rather than stores, and why the API stays data with chaining only on the instance.