SPB Git

spb/ultra-sharp-agent-skills Public

Ultra-Sharp Agent Skills — a research-first skill-authoring system + 72 production-ready skills for AI agents.

Python 100%
3.6 KB · 115 lines markdown
Rendered Raw Blame History
1<!--2Author: Simon-Pierre Boucher3Contact: contact@spboucher.ai4-->56# Patterns — Designing Webhooks78## Contents9- Delivery envelope10- Signing (producer) and verification (consumer)11- Retry schedule12- Consumer receiver checklist13- Dead-letter and redelivery API14- Gotchas1516## Delivery envelope1718```json19{20  "delivery_id": "whd_01J8ZC4T9",21  "type": "invoice.paid",22  "created_at": "2026-08-05T14:30:00Z",23  "api_version": "2026-06-01",24  "data": { "invoice_id": "inv_42" }25}26```27`delivery_id` is unique per event and STABLE across retries — it is the28consumer's dedup key. `api_version` pins the payload shape per subscriber.2930## Signing (producer) and verification (consumer)3132Producer:3334```python35import hmac, hashlib, time3637def sign(secret: str, body: bytes) -> str:38    ts = str(int(time.time()))39    msg = ts.encode() + b"." + body40    sig = hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()41    return f"t={ts},v1={sig}"42# Header: Webhook-Signature: t=1754404200,v1=5257a86...43```4445Consumer:4647```python48# 300 s = 5 min replay window: generous for clock skew, tight for replays.49MAX_SKEW = 3005051def verify(secret, header, body) -> bool:52    parts = dict(p.split("=", 1) for p in header.split(","))53    ts, their_sig = parts["t"], parts["v1"]54    if abs(time.time() - int(ts)) > MAX_SKEW:55        return False56    msg = ts.encode() + b"." + body57    ours = hmac.new(secret.encode(), msg, hashlib.sha256).hexdigest()58    return hmac.compare_digest(ours, their_sig)   # constant time59```6061Verify against the RAW request body bytes — any re-serialization (JSON parse62then dump) changes the bytes and fails the check.6364## Retry schedule6566```67attempt  delay      cumulative681        immediate  0692        1 min      1 min703        5 min      6 min714        30 min     36 min725        2 h        ~2.6 h736        12 h       ~14.6 h    → dead-letter after this74```75Add ±20% jitter to every delay so a consumer recovering from an outage is not76hit by a synchronized thundering herd. Timeout per attempt: 10 s.7778## Consumer receiver checklist (publish this in your docs)7980```811. Verify Webhook-Signature before parsing (raw body, constant-time compare).822. Reject t older than 5 minutes.833. Return 200 immediately; process async (queue). Slow handlers get retried84   and you will process duplicates.854. Deduplicate on delivery_id (store processed IDs ≥ 24 h).865. Treat events as unordered; fetch current state from the API when it matters.876. Do not whitelist our IPs as your only security — verify signatures.88```8990## Dead-letter and redelivery API9192```93GET  /v1/webhook-deliveries?status=failed&endpoint_id=we_794POST /v1/webhook-deliveries/{delivery_id}/redeliver95```96Keep failed deliveries ≥ 30 days. Auto-disable endpoints failing every97delivery for 7 consecutive days; email the owner at 24 h, 72 h, and on98disable.99100## Gotchas101102- **Signing the parsed-then-reserialized body** — key ordering and whitespace103  differ; always HMAC the raw bytes you send/receive.104- **Redirects:** don't follow 3xx on delivery (SSRF vector + signature is now105  going somewhere unverified). Treat as failure.106- **SSRF on subscription:** validate subscriber URLs (https only, no private107  IP ranges, resolve-and-check at send time too — DNS can change).108- **Retrying 4xx forever:** a 410 Gone should disable the endpoint109  immediately; a 401/403 after working deliveries usually means the consumer110  rotated secrets — alert, don't hammer.111- **Ordering promises creep into docs via examples** — audit docs so every112  example shows dedup + unordered handling.113- **One shared secret for all endpoints of a customer** — breach of one114  endpoint burns all; scope secrets per endpoint.115