Node 26 enters LTS in October 2026: pin your Sume webhook receiver
Node 26 moves to LTS this month and Node 27 Alpha starts. Pin the runtime that runs your Sume webhook receiver and prove it with a signed self-test.

Pin your Sume webhook receiver to Node 26 once it reaches LTS this month, and keep Node 24 as the fallback until your own tests pass. According to the Node.js announcement, Node 26 (released April 2026) enters LTS in October 2026, and Node 27 starts its Alpha phase the same month. The post also says that if you already only upgrade to LTS versions, little changes beyond version numbering.
Why this matters for a receiver: it is the one process that must stay up while a paid job finishes, and it is usually deployed once and left alone. A runtime that reaches end of life without anyone noticing is the typical way such a service becomes a risk. Writing the end-of-life date for your chosen line into the runbook, next to the secret rotation date, is a cheap habit.
For a webhook receiver that is good news: the code that verifies a Sume signature uses only node:crypto and node:http, so the upgrade is a base-image change plus one test run.
The new schedule in one table
From October 2026 Node ships one major per year, and every release becomes LTS (no odd and even split). The dates below come from the announcement; the latest release numbers come from the release listing.
| Line | What happens | When |
|---|---|---|
| Node 26 | Moves from Current to LTS | October 2026 |
| Node 26 | Maintenance, then end of life | Maintenance Oct 2027, EOL Apr 2029 |
| Node 27 | Alpha begins | October 2026 |
| Node 27 | 27.0.0 release, LTS, end of life | Apr 2027, LTS Oct 2027, EOL Apr 2030 |
| Phases per major | Alpha 6 months, Current 6 months, LTS 30 months | 36 months total |
Steps to move the receiver
- Keep the signing secret in
SUME_COM_WEBHOOK_SIGNING_SECRET, the variable name the Sume webhook docs use, and refuse to start when it is empty. - Verify against the raw request bytes, never a re-serialized JSON body. Sume signs
<timestamp>.<raw_body>with HMAC-SHA256 and sendsx-sume-webhook-signature: sume-v1=<hex>. - Run the self-test below on Node 24 and Node 26 in CI. Treat Node 27 Alpha as an allowed-to-fail job, not a deploy target.
- Change the image tag, deploy, then use Sume's test delivery to confirm the new runtime still returns a 2xx.
Self-test script
This script starts a receiver, signs a body the way Sume does, and checks that a tampered body is refused with a 401. It stops at startup if the secret is empty, which is the behavior a production verifier should have too.
import http from "node:http";
import { createHmac, timingSafeEqual } from "node:crypto";
const secret = process.env.SUME_COM_WEBHOOK_SIGNING_SECRET ?? "";
if (!secret) throw new Error("SUME_COM_WEBHOOK_SIGNING_SECRET is empty");
function verify(raw, h) {
const ts = h["x-sume-webhook-timestamp"];
if (!ts || Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;
const want = createHmac("sha256", secret).update(`${ts}.${raw}`).digest();
return String(h["x-sume-webhook-signature"] ?? "").split(",").some((p) => {
const got = Buffer.from(p.trim().replace(/^sume-v1=/, ""), "hex");
return got.length === want.length && timingSafeEqual(got, want);
});
}
const server = http.createServer(async (req, res) => {
let raw = "";
for await (const c of req) raw += c;
res.statusCode = verify(raw, req.headers) ? 204 : 401;
res.end();
});
server.listen(0, async () => {
const body = JSON.stringify({ event: "job.completed", job_id: "j1" });
const ts = String(Math.floor(Date.now() / 1000));
const sig = createHmac("sha256", secret).update(`${ts}.${body}`).digest("hex");
const url = `http://127.0.0.1:${server.address().port}`;
const h = { "x-sume-webhook-timestamp": ts, "x-sume-webhook-signature": `sume-v1=${sig}` };
console.log("signed:", (await fetch(url, { method: "POST", headers: h, body })).status);
console.log("tampered:", (await fetch(url, { method: "POST", headers: h, body: body + " " })).status);
server.close();
});What Sume does not do
Sume does not pick your Node version, and the TypeScript SDK needs only fetch and WebCrypto, so it also runs on Node 18 and later, Bun, Deno and Workers. Sume retries a failed job delivery up to 10 times at about 30 second spacing, so a deploy that takes the receiver down for longer than that needs polling as a backup. See the webhook docs.
Sources
Related posts
More in Developers
- node:test mock.method on fetch: test a Sume status poll offline
Node 26 goes LTS this month. Test a Sume job status poll with node:test and mock.method on fetch: four cases, no network, no key, no third-party test library.
- npm audit signatures on @sume-com/sdk 0.2.0: what it proves, what not
Run npm audit signatures on @sume-com/sdk 0.2.0 before deploy. It checks the registry signature; the registry lists no provenance attestation for this version.
- Omni Flash API errors: which to retry and which to fix
A retry policy for Gemini Omni Flash 1.1 calls on Sume: 400, 401, 402, 409, 429 rate_limited, 429 queue_full and 503 each mapped to retry, wait or fix.
- Omni job status names: pending vs queued, cancelled vs canceled
Sume's /v1/videos poll uses pending, in_progress, completed, failed and cancelled; /v1/jobs uses queued, processing and canceled. Mapping table for Omni code.
Written by Sume