Wisp Webhook Service #
Delivers HTTP webhook notifications when AT Protocol records change. Watches the firehose for place.wisp.v2.wh record creations/updates/deletions, then sends matching events to registered URLs.
Setup #
bun install
bun run start
Environment Variables #
NODE_ENV=production
DATABASE_URL="postgres://user:password@localhost:5432/wisp"
JETSTREAM_URL="wss://jetstream2.us-east.bsky.network/subscribe"
# Defaults to 127.0.0.1. The container image sets 0.0.0.0 for orchestration.
HEALTH_HOST=127.0.0.1
HEALTH_PORT=3003
DELIVERY_TIMEOUT_MS=10000
DELIVERY_MAX_RETRIES=3
REDIS_URL="redis://localhost:6379"
WEBHOOK_EVENTS_CHANNEL="webhook:events"
# Reconnects continue indefinitely; this only caps exponential backoff.
JETSTREAM_RECONNECT_MAX_EXPONENT=8
The HTTP listener exposes GET /health (readiness) and GET /live (liveness). Health returns bounded service status, including delivery.audit counts (queued, active, dropped), never webhook records, endpoints, or secrets.
Delivery lifecycle #
Delivery execution uses one Effect engine behind the existing Promise worker API. Each attempt owns its pinned response and lease heartbeat in a scope. A failed batch waits for all admitted siblings before another batch may claim work. Shutdown stops admission, grants a bounded grace period, then aborts HTTP attempts; a false stop result means transport, SQL, heartbeat, or audit work is still outstanding and shared clients must remain open. An unfinished default worker cannot be replaced by restarting it.
Audit notifications are best-effort: two workers drain at most 256 queued, fixed-size metadata entries. Overflow and oversized metadata drop new audit entries, increment the observable dropped count, and emit a sanitized, rate-limited warning. Audit failure never repeats an already completed POST. Durable outbox rows are never dropped by this policy.
Delivery remains at-least-once. Payload bytes, signing, delivery IDs, subscription checks, lease-token conditional updates, and durable enqueue/cursor transactions are unchanged.
See the webhook documentation for the full record schema, payload format, and signature verification.