Using it
Change feed and webhooks
Every create, update and delete made through the gateway gets a number in an ordered feed.
Reading the feed
GET /v1/changes?since=<n>&limit=<k> returns { data, next, head, gap }. Keep next and ask for it next time to resume. gap: true means the feed no longer holds changes from your since; do a full re-read. Callers only see changes to records they are allowed to see.
Webhooks (admin only, API only)
POST /v1/admin/webhooks{ name, url, events }(eventsis a list of event types, or["*"]) returns the signing secretwhsec_…once. AlsoGET,PATCH /:id,DELETE /:id, andGET /:id/deliveries.- Delivery is at least once. Use the
X-Sethu-Deliveryheader (evt_<number>) to ignore repeats. - In production the URL must be https. The address is checked by the network guard on every attempt, the connection is pinned to the checked IP, redirects are not followed, 10 s timeout.
- Retries wait 10 s, 30 s, 2 min, 10 min, 30 min, 1 h, 3 h; after 8 attempts the delivery is marked dead.
Verify a delivery (Node):
import { createHmac, timingSafeEqual } from 'node:crypto';
function verify(rawBody, header, secret) {
const parts = Object.fromEntries(header.split(',').map((p) => p.split('=')));
if (Math.abs(Date.now() / 1000 - Number(parts.t)) > 300) return false;
const expected = createHmac('sha256', secret).update(`${parts.t}.${rawBody}`).digest('hex');
const a = Buffer.from(expected), b = Buffer.from(parts.v1 ?? '');
return a.length === b.length && timingSafeEqual(a, b);
}
Header: X-Sethu-Signature: t=<unix>,v1=<hex>.
Not built
- Native change capture. MongoDB change streams, Postgres logical decoding and Firestore listeners are not used. Changes made directly in the database, bypassing the gateway, do not appear.
- No admin app screen for webhooks yet.
- Live events (SSE) cannot resume with
Last-Event-ID; use the feed to catch up. - The file-based feed keeps up to one second of changes at risk in a crash; the Postgres feed does not.
Status: tested (16 tests plus a standalone worker with two gateways on Postgres 16). Not tested against real receivers on the internet.