Postgres 17.11 pgcrypto change: verify a Sume webhook with hmac()
The Postgres minor release changed legacy pgcrypto ciphers. Its listed changes never mention hmac(), so a Sume sume-v1 signature check in SQL still works.

Yes, you can check a Sume signature in SQL, and the pgcrypto change in this release does not touch hmac(). The Supabase changelog's pgcrypto item concerns legacy ciphers, and it never mentions hmac, so I would still test after upgrading rather than assume.
Supabase and PostgreSQL facts are from the vendor pages; Sume facts from Verifying webhooks and Job webhooks, read 2026-10-01.
What did the Postgres minor release change?
The Supabase changelog covers PostgreSQL 15.19 and 17.11, available from September 28 and fixing 44 CVEs. Its breaking changes are ltree index corruption, a pgcrypto legacy-cipher change, a btree_gist NaN float change and restrictions on custom operator estimators. For pgcrypto it names blowfish, cast5 and bf, where decryption now fails by default.
| Area | Change |
|---|---|
| ltree | Index corruption fix |
| pgcrypto | Legacy ciphers (blowfish, cast5, bf): decryption fails by default |
| btree_gist | NaN float handling |
| Custom operators | Estimator restrictions |
What signature does Sume send?
HMAC-SHA256 over <timestamp>.<raw_body>, sent as x-sume-webhook-signature: sume-v1=<hex> with x-sume-webhook-timestamp. During a secret rotation there are comma-separated entries for 24 hours and any match is accepted. The default replay window is 300 seconds. PostgreSQL's pgcrypto has hmac(data text, key text, type text) returning bytea, and encode(..., 'hex') gives hex.
What does the function look like?
I ran this on Postgres 17 with pgcrypto installed in the extensions schema, as on Supabase; qualify hmac to match your schema. It returns false on an empty secret, a bad timestamp, a stale one or a mismatch, and handles two entries. It must be stable because it reads now(). The = comparison is not constant-time, which matters little over an HMAC but is why the SDK verifier is preferable where you can use it.
create or replace function public.verify_sume_webhook(
body text, ts text, sig_header text, secret text
) returns boolean
language plpgsql stable as $$
declare
entry text;
expected text;
begin
if secret is null or secret = '' or ts is null or sig_header is null
or ts !~ '^[0-9]{1,12}$' then
return false;
end if;
if abs(extract(epoch from now()) - ts::bigint) > 300 then
return false;
end if;
expected := 'sume-v1=' || encode(
extensions.hmac(ts || '.' || body, secret, 'sha256'), 'hex');
foreach entry in array string_to_array(replace(sig_header, ' ', ''), ',') loop
if entry = expected then
return true;
end if;
end loop;
return false;
end
$$;Where would you call it?
From a function that receives the raw body unparsed, passing the secret from its environment, not from a table you log. Do not parse and re-serialize the JSON first. For the usual route see the Supabase middleware post.
Does the upgrade need other checks?
Run a signed test with POST /v1/webhooks/test-deliveries after the upgrade, and keep status polling as a backup, since the docs call delivery a convenience.
Sources
Related posts
More in Developers
- Punch-in zoom on video by API: crop or zoompan, no keyframes
Sume has no auto zoom switch. Use a crop op for a fixed punch-in or the allowlisted zoompan filter in a video-filter graph; there are no keyframes.
- Qwen Image 2.1: when you must show Built with Qwen
The Qwen Research License requires Built with Qwen or Improved using Qwen when outputs train a distributed model, and bars Qwen as a derivative's main name.
- QwenImage21Pipeline in diffusers vs an HTTP image request on Sume
QwenImage21Pipeline runs Qwen-Image-2.1 locally on a GPU. Sume has no 2.1 id: a hosted call is one POST /v1/images with a listed model. Side-by-side.
- Qwen Image 2.1 LoRA redistribution: notice file and Hangzhou courts
Sharing Qwen-Image-2.1 weights or a LoRA needs the license copy, modified-file notices and a Notice file; disputes go to the Hangzhou courts. Clause summary.
Written by Sume