Skip to main content

Module webhooks

Module webhooks 

Source
Expand description

Delivery of outgoing event webhooks.

The services write one row per matching webhook into webhook_deliveries inside the transaction of the event (services::webhooks::emit), so a delivery exists exactly when the event was committed. The background task started by spawn sends the due rows:

  • POST <url> with the JSON payload and the headers X-TeamCraft-Event, X-TeamCraft-Delivery (the row id, the same for every retry) and X-TeamCraft-Signature: sha256=<hex HMAC-SHA256(secret, body)>;
  • a 2xx response is success; anything else (including a redirect, which is never followed, and a timeout after TIMEOUT) is retried with exponential backoff and jitter (retry_delay) up to MAX_ATTEMPTS;
  • a row is claimed with FOR UPDATE SKIP LOCKED and leased for LEASE, so several servers never send the same attempt and a crashed sender’s row is retried after the lease;
  • response bodies are neither read nor stored, only the status code.

Target guard (SSRF). Only http and https URLs without user credentials are accepted. Every address a host name resolves to is checked when the connection is made (a custom resolver, so DNS rebinding cannot slip through) and IP literals are checked before: private, loopback, link-local (cloud metadata), shared and unique-local addresses are refused unless the host is listed in TEAMCRAFT_WEBHOOK_ALLOWED_HOSTS or TEAMCRAFT_WEBHOOK_ALLOW_PRIVATE=true. A non-empty allow-list also restricts webhooks to the listed hosts. Proxy variables are ignored so the check sees the real target.

Structs§

Dispatcher
Sends webhook deliveries (cheap to clone).
Outcome
The result of one attempt.
Report
What one run did.
TargetPolicy
Which webhook targets are allowed (see the module documentation).

Constants§

FIRST_RETRY
Delay before the first retry; it doubles with every further attempt.
LEASE
How long a claimed delivery is reserved for its sender.
MAX_ATTEMPTS
Attempts per delivery before it is marked failed.
MAX_RETRY
Longest delay between two attempts.
TIMEOUT
Time limit of one HTTP request (connect, send, response head).
WITHHELD_ERROR
The text recorded on a withheld delivery.

Functions§

absolutize
URLs are stored relative (/revisions/<id>, /api/v1/…) and made absolute with the public URL when they are sent, so a changed TEAMCRAFT_PUBLIC_URL applies to pending deliveries as well.
hmac_sha256
HMAC-SHA256 (RFC 2104) on top of sha2.
is_internal
Addresses inside an organisation or the host itself: loopback, private (RFC 1918), shared (100.64/10), link-local (incl. 169.254.169.254), “this network” (0/8), benchmarking (198.18/15), IPv6 unique-local (fc00::/7), link-local (fe80::/10) and site-local (fec0::/10), and NAT64 and 6to4 addresses of such IPv4 addresses.
purge_finished
Removes sent and failed deliveries older than days. Returns the count.
retry_delay
Delay after the failed attempt number attempts (1-based): one minute, doubling per attempt, capped at MAX_RETRY, plus 0–25 % jitter derived from the delivery id (deterministic, so the schedule of one delivery is reproducible while many deliveries do not retry in lockstep).
signature
Value of the X-TeamCraft-Signature header: sha256=<hex>; the key is the webhook secret as UTF-8 bytes.
spawn
Starts the background delivery.
system_lookup
The operating system’s resolver.
verify_signature
Checks a signature header in constant time (what a receiver does).

Type Aliases§

Lookup
Name resolution for webhook connections: host name → addresses. The system resolver in production (system_lookup); tests pass their own answers to Dispatcher::with_lookup (DNS rebinding without a DNS server).