feat(schemas): canonical OpenAPI and AsyncAPI descriptions of the protocol - #109
Open
ucekmez wants to merge 1 commit into
Open
feat(schemas): canonical OpenAPI and AsyncAPI descriptions of the protocol#109ucekmez wants to merge 1 commit into
ucekmez wants to merge 1 commit into
Conversation
…tocol `find -iname '*openapi*' -o -iname '*asyncapi*'` returned zero committed files. The only OpenAPI in the project was the one `@eep-dev/setup-cli` generates *per deployment*, which inverts the dependency: every publisher authored its own description of a shared protocol, so there was no canonical document for anything to drift from. That is how the subscription resource came to be addressed five different ways across the repo, two of them load-bearing for conformance. Worse, nothing described Layer 2 SSE, outbound webhook delivery, or the Layer 3 pulse in machine-readable form at all — and those are where EEP's value lives. OpenAPI cannot express them: it models request/response, not a long-lived stream, a server-initiated delivery, or a bidirectional channel. AsyncAPI models exactly those, and the 24 JSON Schemas were already sitting there ready to be referenced. - `schemas/v0.1/openapi.yaml` — Layer 1 and the Layer 2 request/response surface, 19 operations, `$ref`ing the existing schemas and citing the spec section that defines each endpoint. - `schemas/v0.1/asyncapi.yaml` — the SSE stream, webhook delivery, WebSub intent verification and the pulse channel. Webhook delivery is modelled with the subscriber as the server, because EEP inverts the usual direction there and a reader needs to see that. - `scripts/check-openapi-routes.mjs` plus a CI job: method, path and `operationId` are diffed against the reference middleware's route table in both directions. Verified by injecting drift each way (renamed operationId, phantom path) and confirming a non-zero exit, rather than only confirming it passes on a clean tree. - Deprecated compatibility aliases are deliberately excluded from the canonical description: implementations accept them, publishers should not advertise them. - New §0 pointing at both documents, and `setup-cli`'s generated description now says it records one deployment's URLs and defers to the canonical documents on protocol semantics. Refs: EEP audit 2026-08 findings B2, O7 Signed-off-by: Ugur Cekmez <ucekmez@gmail.com>
This was referenced Aug 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
PR 17 of a stacked series. Base is #108. Not for merge without review.
The only OpenAPI in the project is the one
@eep-dev/setup-cligenerates per deployment. That inverts the dependency: every publisher authors its own description of a shared protocol, so there is no canonical document for anything to drift from. That is how the subscription resource came to be addressed five different ways across the repo — two of them load-bearing for conformance (#94).Worse: nothing described Layer 2 SSE, outbound webhook delivery, or the Layer 3 pulse in machine-readable form at all — and those are where EEP's value lives. OpenAPI cannot express them; it models request/response, not a long-lived stream, a server-initiated delivery, or a bidirectional channel. AsyncAPI models exactly those, and the 24 JSON Schemas were already there ready to be referenced.
What changed
schemas/v0.1/openapi.yaml— Layer 1 + the Layer 2 request/response surface. 19 operations,$refing the existing schemas, each citing the spec section that defines it.schemas/v0.1/asyncapi.yaml— the SSE stream, webhook delivery, WebSub intent verification, and the pulse channel. Webhook delivery is modelled with the subscriber as the server, because EEP inverts the usual direction there and a reader needs to see that immediately.scripts/check-openapi-routes.mjs+ CI job — method, path andoperationIddiffed against the reference middleware's route table in both directions.Deprecated compatibility aliases are deliberately excluded from the canonical description: implementations accept
/eep/subscribe/:id, publishers should not advertise it.New §0 pointing at both documents, and
setup-cli's generated description now says explicitly that it records one deployment's URLs and defers to the canonical documents on protocol semantics.Scope
Checklist
setup-clistill emits its per-deployment document — only itsdescriptiontext changed.Verification
The gate was verified by injecting drift in both directions, not just by watching it pass:
openapi.yamlexit 1— "middleware operationId 'pauseSubscription' != openapi.yaml 'pauseSubscriptionRENAMED'"openapi.yamlexit 1— "openapi.yaml describes GET /eep/imaginary, which the reference middleware does not serve"exit 0— 19 operations matchedtests/@eep-dev/middleware@eep-dev/gates@eep-dev/setup-cli@eep-dev/discoverytests/cross-impl/codegen-schema-types --checkNotes for reviewers
The YAML parsing in the gate is deliberately minimal — a regex walk over
paths:entries and the method keys nested under them, rather than a YAML dependency at the repo root, which nothing else there needs. The structure it reads is unambiguous at fixed indentation. If you'd rather take the dependency for robustness, say so; I optimised for not adding one to the root for a single script.I did not delete
setup-cli's generator. The right end state is probably that it renders the canonical document with deployment-specific servers and options substituted in, rather than authoring paths independently. That is a real refactor of a package with its own test suite, and folding it into this PR would have buried the point. Flagging it as the obvious follow-up.No validation of the documents themselves against the OpenAPI/AsyncAPI meta-schemas. Both parse and the parity gate reads
openapi.yamlsuccessfully, but a spectral/asyncapi-cli lint step would be a stronger check. It needs a new CI dependency, which felt like a separate decision.