Rindle.Delivery.WebhookPlug (Rindle v0.1.5)

Copy Markdown View Source

Mountable provider-aware Plug for streaming-provider webhooks.

Adopter wiring

Step 1 — install the body reader globally in endpoint.ex (BEFORE Plug.Parsers):

plug Plug.Parsers,
  parsers: [:urlencoded, :multipart, :json],
  pass: ["*/*"],
  body_reader: {WebhookBodyReader, :read_body, []},
  json_decoder: Jason

Step 2 — mount the Plug in router.ex, one forward per provider:

forward "/webhooks/rindle/mux", Rindle.Delivery.WebhookPlug,
  provider: Rindle.Streaming.Provider.Mux,
  secrets: {:application, :rindle, [Rindle.Streaming.Provider.Mux, :webhook_secrets]}

Step 3 — set RINDLE_MUX_WEBHOOK_SECRETS (comma-separated) in your runtime config, and configure your Mux dashboard webhook to POST to https://yourapp.example.com/webhooks/rindle/mux.

Secrets resolver shapes (D-02)

The :secrets option supports four shapes, resolved at every call/2 (NOT init/1) so runtime rotation works without a restart:

  • [binary()] — direct list (tests).
  • {:system, env_var :: String.t()} — comma-split env var.
  • {:application, app :: atom(), [atom()]}Application.get_env getter with optional path traversal into nested keyword lists / maps.
  • (-> [binary()]) — 0-arity function.

Response codes (D-12..D-16)

StatusBodyWhen
202 AcceptedemptyVerified + enqueued.
200 OKemptyVerified but dropped (event not in adapter dispatch table).
400 Bad Requestprovider_webhook_invalidSignature mismatch, replay-window failure, missing secrets, callback raised.
405 Method Not Allowedmethod not allowedNon-POST request.
500 Internal Server Errorserver_misconfiguredBody reader assign missing AND fallback empty.
503 Service UnavailableemptyOban enqueue raised (transient downstream failure — Mux retries).

Telemetry (Plug edge)

Events under [:rindle, :provider, :webhook, _]:

  • :verified — verify path returned {:ok, event}. Metadata: %{provider, event_type, event_id, kind} where kind: :enqueued | :dropped.
  • :rejected — verification or pre-verify check failed. Metadata: %{provider, reason, ...} where reason: :method_not_allowed | :body_reader_missing | :no_secrets_configured | :provider_callback_raised | :sig_mismatch | :oban_unavailable.

Provider-internal telemetry under [:rindle, :provider, :mux, :webhook_attempt, _] (emitted from inside Rindle.Streaming.Provider.Mux.verify_webhook/3):

  • :secret_used — metadata %{secret_index}.
  • :rejected — metadata %{secret_index, sdk_reason}.

Summary

Functions

init/1 validates the provider module exposes verify_webhook/3 and that the secrets resolver shape is one of the four locked forms. Raises ArgumentError for misconfigurations so deployment-time mistakes surface immediately, not at first webhook delivery.

Functions

init(opts)

init/1 validates the provider module exposes verify_webhook/3 and that the secrets resolver shape is one of the four locked forms. Raises ArgumentError for misconfigurations so deployment-time mistakes surface immediately, not at first webhook delivery.