fix(spec,middleware,compliance-cli): define one canonical subscription resource - #94
Merged
ucekmez merged 1 commit intoSep 2, 2026
Conversation
9 tasks
…n resource
The subscription resource was addressed five different ways across the
repository, and two of those paths were load-bearing for conformance:
spec §5.1 POST /eep/subscribe
spec §10 POST /subscribe
spec §10 POST /subscriptions/:id/resume
middleware GET|DELETE /eep/subscribe/:id
compliance-cli GET /eep/subscriptions
compliance-cli POST /eep/subscriptions/:id/test
`POST /eep/subscriptions/:id/test` is how the Core-tier probe triggers a
delivery in order to verify Standard Webhooks headers and the HMAC
signature. It was specified nowhere and implemented nowhere — not in the
middleware, not in either reference implementation. Worse, it failed
silently: `fetch` rejects only on a transport error, so a 404 resolved
normally, the runner slept 5s, received nothing, and reported "Webhook
delivery received: FAIL". Implementers saw a delivery failure and went
hunting in their own dispatcher for a bug that was never theirs.
`GET /eep/subscriptions` (the Standard-tier rate-limit probe) had the
same shape: asserted against a 404.
The paths chosen here are not new. `docs/guides/how-to-subscribe.md`
has documented `/eep/subscriptions/{id}` with `pause`, `resume`, `test`
and `DELETE` since v0.1, and `delivery_guarantees.md` references the
same collection. The guide was right; the spec and the middleware were
the outliers. Creation stays on `POST /eep/subscribe` because that is
what the manifest advertises as `layers.layer2_webhook` and what the
`rel="subscribe"` Link header points at.
Changes:
- Spec: new §5.1.1 making the subscription resource normative — the
member operations, their status codes and scopes, the rule that
`delivery_secret` is never re-exposed, 404-not-403 for another
subscriber's id, and the semantics of a test delivery.
- Spec: §10 lifecycle now uses the canonical paths and defines what a
"failed delivery" is (a fully exhausted §5.4 retry schedule); §14.2's
conformance line points at §5.1.1 instead of claiming an unwritten
lifecycle.
- Middleware: serve list / pause / resume / test alongside the existing
status and unsubscribe handlers, all under `/eep/subscriptions`, with
the pre-§5.1.1 `/eep/subscribe/:id` paths kept as deprecated aliases.
- Middleware: `WebhookDispatcher` routes `com.eep.subscription.test` to
the single subscription in `data.subscription_id` rather than fanning
out by `event_types` — a test delivery must reach a subscriber whose
patterns would never match it, and must reach nobody else.
- compliance-cli: the trigger now reports its own outcome, naming the
missing endpoint on a 404, and downstream signature probes SKIP rather
than FAIL when no delivery could be triggered.
Refs: EEP audit 2026-08 findings A4, A9
Signed-off-by: Ugur Cekmez <ucekmez@gmail.com>
ucekmez
force-pushed
the
fix/canonical-subscription-endpoints
branch
from
August 26, 2026 18:46
6c7252e to
202e140
Compare
9 tasks
ucekmez
merged commit Sep 2, 2026
bf3d293
into
fix/spec-webhook-verification-example
21 checks passed
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 2 of a stacked series. Base is #93 — review that first; GitHub will retarget this to
mainwhen #93 merges. Not for merge without review.The subscription resource is addressed five different ways across the repo, and two of those paths are load-bearing for conformance:
POST /eep/subscribePOST /subscribePOST /subscriptions/:id/resumeGET|DELETE /eep/subscribe/:idGET /eep/subscriptionsPOST /eep/subscriptions/:id/testThe consequence
POST /eep/subscriptions/:id/testis how the Core-tier probe triggers a delivery in order to verify Standard Webhooks headers and HMAC correctness. It is specified nowhere and implemented nowhere — not in the middleware route table, not in either reference implementation.And it fails silently.
fetchrejects only on a transport error, so a 404 resolves normally, the runner sleeps 5s, receives nothing, and reports:An implementer reads that as "my dispatcher is broken" and goes hunting for a bug that was never theirs.
GET /eep/subscriptions(Standard-tier rate-limit probe) has the same shape — asserted against a 404.The paths here are not new
docs/guides/how-to-subscribe.mdhas documented/eep/subscriptions/{id}withpause,resume,testandDELETEsince v0.1, anddelivery_guarantees.mdreferences the same collection for delivery logs. The guide was right; the spec and middleware were the outliers. This PR makes the guide's API normative rather than inventing anything.Creation stays on
POST /eep/subscribe— that URL is what the manifest advertises aslayers.layer2_webhookand what therel="subscribe"Link header points at, so moving it would break every deployed publisher.What changed
Spec
delivery_secretnever re-exposed;404-not-403for another subscriber's id so the collection can't be enumerated; test-delivery semantics (202, signed exactly like production traffic,409when notactive).Middleware
/eep/subscriptions;/eep/subscribe/:idkept as deprecated0.1.xaliases.WebhookDispatcherroutescom.eep.subscription.testto the single subscription named indata.subscription_idinstead of fanning out byevent_types— a test delivery must reach a subscriber whose patterns would never match it, and must reach nobody else.compliance-cli
Scope
Checklist
0.1.x. Publishers that never implemented/testwill now see an explicit, actionable conformance failure where they previously saw a misleading one.Verification
@eep-dev/middleware@eep-dev/compliance-clitests/(vitest)npx tsc --noEmit(middleware, compliance-cli)node scripts/codegen-schema-types.mjs --checkNotes for reviewers
Design decision worth confirming: I aligned the spec and middleware to the guides, rather than aligning the guides to the middleware. The alternative — standardising on
/eep/subscribe/:id— would have required rewritinghow-to-subscribe.mdanddelivery_guarantees.mdand would leave the CLI's existing paths wrong. Happy to flip it if you'd rather.delivery_guarantees.md:92also referencesGET /eep/subscriptions/:id/delivery-log, which is still unspecified. That belongs with the webhook backfill/redelivery work (audit finding B1) and is deliberately not in this PR.Python middleware parity for the new handlers is not included here — flagging it rather than silently skipping it.