Skip to content

feat(schemas): canonical OpenAPI and AsyncAPI descriptions of the protocol - #109

Open
ucekmez wants to merge 1 commit into
feat/asymmetric-delivery-signaturesfrom
feat/canonical-api-descriptions
Open

feat(schemas): canonical OpenAPI and AsyncAPI descriptions of the protocol#109
ucekmez wants to merge 1 commit into
feat/asymmetric-delivery-signaturesfrom
feat/canonical-api-descriptions

Conversation

@ucekmez

@ucekmez ucekmez commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

PR 17 of a stacked series. Base is #108. Not for merge without review.

$ find . -iname '*openapi*' -o -iname '*asyncapi*' | wc -l
0

The only OpenAPI in the project is the one @eep-dev/setup-cli generates 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 and operationId diffed 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

  • Spec / schema only
  • TypeScript package(s)
  • Python package(s)
  • Tests / CI
  • Docs / examples

Checklist

  • I read CONTRIBUTING.md and CODE_OF_CONDUCT.md.
  • Tests added or updated where appropriate (the parity gate is the test).
  • Breaking change? No. Two new files and a new CI gate; no schema, wire format or package API changed. setup-cli still emits its per-deployment document — only its description text changed.
  • Documentation updated for user-visible behavior.

Verification

The gate was verified by injecting drift in both directions, not just by watching it pass:

Injected Result
operationId renamed in openapi.yaml exit 1"middleware operationId 'pauseSubscription' != openapi.yaml 'pauseSubscriptionRENAMED'"
Phantom path added to openapi.yaml exit 1"openapi.yaml describes GET /eep/imaginary, which the reference middleware does not serve"
Clean tree exit 0 — 19 operations matched
Suite Result
tests/ 199 passed
@eep-dev/middleware 233 passed
@eep-dev/gates 491 passed
@eep-dev/setup-cli 44 passed
@eep-dev/discovery 42 passed
tests/cross-impl/ 56 passed, 3 skipped
codegen-schema-types --check no drift

Notes 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.yaml successfully, but a spectral/asyncapi-cli lint step would be a stronger check. It needs a new CI dependency, which felt like a separate decision.

…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>
Copilot AI lite review requested due to automatic review settings August 26, 2026 19:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants