AshHooks.InboundDelivery (AshHooks v2.0.3)

Copy Markdown View Source

Turns the consumer's resource into the inbound webhook ledger — the durable deduplication record used by AshHooks.Ingress.

Attach next to the consumer's own data layer:

use Ash.Resource,
  data_layer: AshSqlite.DataLayer,
  extensions: [AshHooks, AshHooks.InboundDelivery]

inbound_delivery do
  scope_identity([:account_id])
end

The extension injects the ledger fields (provider, external ids, payload + digest, the fenced state machine's status/token/lease/attempts), the unique_ingest identity spanning provider + external event id + the declared scope slots, and the action primitives used by AshHooks.Ingress. These include ingest, claim/finalize/renew, payload redaction, bounded retention, and explicit legacy identity adoption.

The uniqueness identity must be backed by a unique index on the consumer's data layer. Storage-level uniqueness preserves one ledger row under concurrent ingests; the lease and token govern handler ownership. Scope slots must be non-nullable attributes: a nullable slot would make nil scope values distinct on SQL unique indexes and silently break dedup for scope-less redeliveries.

Provider identity upgrades use AshHooks.Ingress.plan_legacy_identity_adoption/3 and AshHooks.Ingress.adopt_legacy_identity/3. Adoption keeps one deterministic representative per canonical identity and marks every retained sibling terminal :superseded; those rows cannot be reaped into handler execution. Applying adoption requires a transaction-capable data layer and quiesced ingress and reapers. See the ordered upgrade in UPGRADING.md.

The injected actions are low-level primitives: the conditional gates (claim only from :received or an expired lease; mark/renew only by the current token under an unexpired lease) live in the query filters that AshHooks.Ingress builds. Call the driver APIs to preserve those conditions; restrict direct access to the generated machine actions.

This ledger stores decoded provider payloads, the signed-body digest, event IDs, and scope keys. Payloads can contain personal information. Define read policies in your domain; the extension does not inject them. The README's Security section shows an example.

Summary

Functions

The fenced state machine's statuses, including terminal :superseded rows retained by legacy identity adoption.

Functions

inbound_delivery(body)

(macro)

statuses()

@spec statuses() :: [atom()]

The fenced state machine's statuses, including terminal :superseded rows retained by legacy identity adoption.