Skip to content

[garden] Docs/OpenAPI: document outbound webhook signature + retry policy; reconcile subscription paths; add DLQ endpoints #498

Description

@khaliqgant

Source: Gil/Ashtyn asked four integration questions they could not answer from the OSS repo or either OpenAPI spec (2026-09-13). Ground truth is in code; the specs are silent or contradictory. This issue closes the doc/spec gaps. (Code lives in relayfile-cloud; specs/docs live here in relayfile.)

1. Document the outbound webhook signature scheme (currently undocumented)

Ground truth — signer relayfile-cloud/packages/relayfile/src/middleware/auth.ts:749-768, delivery queue-consumer.ts:428-458:

  • Headers: X-Relay-Signature (lowercase-hex HMAC-SHA256, no prefix), X-Relay-Timestamp (unix seconds, string), X-Relay-Event-Id.
  • Signing string: `${timestamp}.${rawBody}` — timestamp + . + the raw JSON body.
  • Key: the per-subscription secret (task.secret), auto-generated randomBase64Url(32) and returned once at creation (durable-objects/workspace.ts:3466-3492).
  • Delivery is at-least-once; receivers MUST dedupe on X-Relay-Event-Id. Any 3xx or status >= 300 counts as failure.

Add a receiver verification guide (docs + OpenAPI description) with a code snippet: read the three headers, take the raw body bytes as received (do not re-serialize), compute HMAC-SHA256 over timestamp + "." + rawBody, lowercase-hex, constant-time compare, bound the timestamp to reject replays.

⚠️ Do not conflate with the internal ingress HMAC (signInternalHmac, auth.ts:722-747), which signs `${timestamp}\n` + body under INTERNAL_HMAC_SECRET. Both use X-Relay-Signature; the specs must clearly separate inbound-internal from outbound-partner.

2. Document the outbound delivery retry/backoff policy (currently undocumented)

From queue-consumer.ts:25,210,673: 3 attempts, exponential backoff 10s → 20s, capped 600s, ±20% jitter, 5s delivery timeout, then dead-letter. Intervals are computed at retry time and not persisted (DLQ only carries the final failedAt) — either document that, or persist an attempts_json (see the reliability issue). Add this policy to the spec/docs.

3. Reconcile the webhook-subscription path mismatch

Deployed routes (routes/webhook-subscriptions.ts, routes/webhooks.ts, registered app.ts:68-69):
POST/GET/DELETE /v1/workspaces/{workspaceId}/webhooks, POST .../webhooks/ingest, GET .../webhooks/dlq, POST .../webhooks/dlq/{deliveryId}/replay.

  • Control-plane spec openapi/relayfile-control-plane-v1.openapi.yaml models these under /v1/integrations/webhook-subscriptions (different path + body shape). Pick one and fix the other.

4. Add the missing webhook-delivery DLQ endpoints to a spec

Neither spec documents GET /v1/workspaces/{workspaceId}/webhooks/dlq or POST .../webhooks/dlq/{deliveryId}/replay. (The v1 spec's replaySyncDeadLetter/getSyncDeadLetters are a different subsystem.) Add them to openapi/relayfile-v1.openapi.yaml.

Acceptance criteria

  • Receiver-facing signature verification doc + snippet published; matches code exactly.
  • Retry/backoff/timeout policy documented.
  • Subscription path reconciled between control-plane spec and deployed code.
  • DLQ list + replay endpoints present in an OpenAPI spec.
  • Inbound-internal vs outbound-partner X-Relay-Signature uses clearly distinguished.

Do not auto-merge — human review required.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions