AshHooks.Endpoint (AshHooks v2.0.3)

Copy Markdown View Source

Configures an Ash resource as an outbound webhook destination, including its enable/disable state and references to signing secrets.

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

# Map a declared :active boolean instead of injecting :status.
endpoint do
  status_attribute :active
  enabled_values [true]
  disabled_value false
end

The extension injects url, status (:enabled | :disabled), and four secret references (secret_ref required, previous_secret_ref / legacy_secret_ref / legacy_previous_secret_ref for rotation and the legacy envelope). All four are AshHooks.Endpoint.SecretRef. The type rejects secret-shaped literals (whsec_ / whsk_ / whpk_ prefixed material) at cast, on every write path including consumer-defined actions: secrets live behind consumer callbacks; store references to those secrets in the endpoint rows.

With status_attribute set, the extension uses that attribute instead of injecting status. The dispatcher skips endpoints whose mapped value is outside enabled_values, and the 410 auto-disable flips the mapped attribute to disabled_value. The mapped attribute must be declared by the consumer (fail-closed at compile).

The dispatcher skips non-enabled endpoints entirely (no delivery row). URL casting checks the scheme, metadata hostnames, and literal IP addresses on every write path. Send-time validation resolves hostnames and rejects nonpublic addresses; the HTTP adapter pins the validated connection address.

Govern read access to target URLs and secret references with your own resource policies. The package does not inject read policies.

Policy obligation: protect the injected :disable action wherever you expose it to application callers. The 410 rule uses it as a system write (authorize?: false). A permissive resource policy can otherwise allow callers to disable endpoints. See AshHooks.OutboundDelivery for the delivery ledger's action policy requirements.

Summary

Functions

Whether an endpoint row is deliverable under its configured status source: the injected status attribute (default) or the consumer-owned status_attribute mapping (enabled_values / disabled_value). The dispatcher's skip decision and the send path's dead-letter decision both route through here, so exactly one switch governs delivery.

The endpoint lifecycle statuses — :disabled is the durable circuit breaker the 410 rule and operators flip.

Functions

enabled?(endpoint)

@spec enabled?(map()) :: boolean()

Whether an endpoint row is deliverable under its configured status source: the injected status attribute (default) or the consumer-owned status_attribute mapping (enabled_values / disabled_value). The dispatcher's skip decision and the send path's dead-letter decision both route through here, so exactly one switch governs delivery.

endpoint(body)

(macro)

statuses()

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

The endpoint lifecycle statuses — :disabled is the durable circuit breaker the 410 rule and operators flip.