Skip to content

ADR-0004: Webhook ingestion and provider apps ​

Status: accepted (2026-10-12); the enqueue path is superseded by ADR-0005, system access by ADR-0006

Decision ​

  • A provider app (e.g. a GitHub App: App ID, private key, webhook secret, client secret) belongs to the deployment, not to a tenant. It is stored in the system-only table provider_apps. Credentials are one JSON document encrypted with @quavon/crypto (AES-256-GCM envelope, per-record data key wrapped by QUAVON_ENCRYPTION_KEY), bound to the context provider_apps:<id>.
  • provider_apps.default_tenant_id assigns new installations to a tenant. Self-hosted installs set it during setup. Installations of an app without a default tenant are recorded as ignored deliveries until tenant claiming exists.
  • Webhook deliveries arrive before the tenant is known, so webhook_deliveries is system-only. System-only tables have RLS forced with no policy and no grants for quavon_app; only withSystem can read or write them.
  • The API verifies the HMAC over the raw body using the webhook secret of the app identified by X-GitHub-Hook-Installation-Target-ID, inserts the delivery (unique per provider and delivery id), enqueues ingest with job id github_<delivery>_<attempt> and returns 202. A duplicate delivery returns 200 without enqueueing, unless the stored delivery failed, in which case attempts is incremented and a new job is enqueued.
  • Queue payloads carry ids only. Webhook bodies live in webhook_deliveries.payload with limited retention; downstream handlers re-fetch current state from the provider instead of trusting event order.
  • BullMQ job ids must not contain :; ids are built with jobId() from @quavon/shared-types.
  • Trace context travels with every job envelope (trace), so one trace spans webhook, queue and worker.
  • Setup uses one-time setup tokens (setup_tokens, stored hashed). The GitHub manifest state is bound to the setup session and expires with the one-hour manifest code window.

Consequences &ZeroWidthSpace;

  • The API needs the system database connection (DATABASE_SYSTEM_URL) for webhook ingestion and setup, and must use it only on those routes.
  • Multi-tenant SaaS installation claiming is handled by the hosted edition (ADR-0015).

The open core is released under the Apache License 2.0.