The incumbent webhook envelope, reproduced byte-identically for the
:dual migration mode (ADR-0002), with verify/5 as the in-package
oracle proving that identity.
Envelope (shape extracted first-hand from the adopting platform's
Webhooks.Signing + its deliver worker — read-only reference):
- MAC — HMAC-SHA256 over
"<unix_ts>.<body>", lowercase hex. sign/3→"v1=<hex>"; rotation appends",v1prev=<hex>"under the separately imported previous secret.- header —
x-webhook-signature: t=<unix_ts>,<signature>.
This module exists for migration and is scheduled to shrink away as
subscriptions cut over to :standard.
Summary
Functions
Assembles the header value: t=<unix_ts>,<signature>.
The legacy header set: x-webhook-signature under secret (and optional previous).
HMAC-SHA256 over <unix_ts>.<body>, lowercase hex, v1=-prefixed.
Current and previous signatures comma-joined (zero-downtime rotation).
Verifies a legacy header value: parses t=/v1=, checks the replay
window, compares hex digests in constant time.
Functions
Assembles the header value: t=<unix_ts>,<signature>.
@spec headers(binary(), binary() | nil, binary(), integer()) :: %{ required(String.t()) => String.t() }
The legacy header set: x-webhook-signature under secret (and optional previous).
HMAC-SHA256 over <unix_ts>.<body>, lowercase hex, v1=-prefixed.
Current and previous signatures comma-joined (zero-downtime rotation).
@spec verify(binary(), binary(), binary(), integer(), integer()) :: {:ok, %{unix_ts: integer()}} | {:error, :invalid_signature | :stale_timestamp}
Verifies a legacy header value: parses t=/v1=, checks the replay
window, compares hex digests in constant time.
window defaults to 300 seconds, symmetric around now_ts.