Expand description
Webhook capability trait. See plan/ecosystem/02-capabilities.md (Webhook section).
A WebhookPlugin verifies that an inbound HTTP request really was
sent by a given third party. The shape is deliberately vendor-neutral:
Stripe’s Stripe-Signature, GitHub’s X-Hub-Signature-256, Shopify’s
X-Shopify-Hmac-Sha256, Slack’s v0:{ts}:{body} scheme and Twilio’s
URL + sorted-params scheme all satisfy the same surface, and a project
can swap implementations by editing bext.config.toml without touching
code.
§Design notes
-
Verify-only. The trait does not carry a
handle(event)method. In bext, webhook handlers are normal routes — the value of the capability is the per-vendor signature check that must run before the route body touches the payload. Each project’s business logic for “what happens when Stripe reports a charge” lives in the route, not the plugin, and the plan’s originalhandle(event)sketch would have forced every verifier crate to carry a decoder for every vendor’s event shape — an open-ended commitment that violates architecture principle 6 (no vendor-specific fields on the trait). Leaving the plugin as a pure verifier also matches the existingAuthPlugin::resolvepattern: a trait that answers one well-posed question and hands the result back to middleware. -
Sync, not async. Every other trait in this crate is sync (
middleware.rs,auth.rs,session.rs,mailer.rs,tracer.rs,scheduled.rs). All five of the in-scope verifiers are pure in-memory HMAC — no network I/O, nothing to await. Matching the existing convention keeps the WASM/QuickJS/nsjail ABI consistent and avoids draggingasync-traitinto a dependency-minimal leaf crate. Future verifiers that need network I/O (OAuth-style signature checks with a JWKS fetch) bridge withblock_onthe same waySesMailerPlugindoes today. -
Errors classify, don’t decorate. Callers need the variant to pick an HTTP status:
MissingHeader/MalformedPayload→ 400,InvalidSignature/ReplayDetected→ 401,Backend→ 500. The innerStringis vendor-provided detail for logs. Do not parse it. This mirrors theMailerErrorshape from E1. -
Raw bytes, not a structured event.
WebhookRequest::bodyis aVec<u8>because every vendor’s signature covers the exact byte stream on the wire — JSON reserialisation would mutate whitespace and break the HMAC. Headers areVec<(String, String)>rather than a map because multiple headers with the same name are legal in HTTP and preserving order aids reproducibility in logs.
Structs§
- Webhook
Request - A minimal, ABI-flat snapshot of the inbound HTTP request that a
WebhookPluginneeds in order to verify the signature.
Enums§
- Webhook
Error - Why a webhook verification failed, classified so callers can map the variant onto an HTTP response code.
- Webhook
Scheme Kind - What kind of signature scheme a provider implements. Runtime uses this to surface shape in admin UI and to help CLI wizards generate the right secret-configuration stanza. It is not an exhaustive taxonomy — new schemes land as new variants.
Traits§
- Webhook
Plugin - A plugin that verifies webhook requests from one third-party provider.