Rindle.Delivery (Rindle v0.1.5)

Copy Markdown View Source

Delivery policy and URL resolution helpers.

Private delivery is the default. Public delivery is an explicit profile opt-in, and authorization (when configured) runs before any URL is issued.

Production delivery remains redirect-oriented: Rindle resolves URLs and lets the underlying storage or future streaming provider serve bytes directly.

Signed URL TTL guidance stays intentionally policy-level rather than widening the profile DSL:

  • images: about 15 minutes (900 seconds)
  • audio: about 1 hour (3600 seconds)
  • video-on-demand: about 2 hours (7200 seconds)
  • long-form playback: keep the same delivery surface and refresh tokens on the adopter side instead of introducing per-request TTL knobs

Telemetry contract:

  • [:rindle, :delivery, :signed] measurements: %{system_time: integer()} metadata: %{profile: module(), adapter: module(), mode: :public | :private}
  • [:rindle, :delivery, :streaming, :resolved] measurements: %{system_time: integer()} metadata: %{profile: module(), adapter: module(), mode: :public | :private, kind: :progressive, mime: String.t()}

Summary

Functions

Returns the configured delivery authorizer module, or nil if none is set.

Returns the delivery policy map declared by a profile module.

Returns true when the profile opts in to public delivery.

Returns the signed URL TTL (seconds) for a profile.

Returns a streaming URL for an asset.

Returns a deliverable URL for an asset's storage key.

Returns a deliverable URL for a variant, falling back to the original asset when the variant is not yet ready.

Types

delivery_mode()

@type delivery_mode() :: :public | :private

Functions

delivery_authorizer(profile)

@spec delivery_authorizer(module()) :: module() | nil

Returns the configured delivery authorizer module, or nil if none is set.

Authorizers implement Rindle.Authorizer.authorize/3 and run before any delivery URL is issued.

Examples

# Requires a profile module.
iex> Rindle.Delivery.delivery_authorizer(MyApp.MediaProfile)
nil

profile_delivery_policy(profile)

@spec profile_delivery_policy(module()) :: map()

Returns the delivery policy map declared by a profile module.

Examples

# Requires a profile module that defines `delivery_policy/0`.
iex> Rindle.Delivery.profile_delivery_policy(MyApp.MediaProfile)
%{public: false, signed_url_ttl_seconds: 900}

public_delivery?(profile)

@spec public_delivery?(module()) :: boolean()

Returns true when the profile opts in to public delivery.

Defaults to false (private-by-default).

Examples

# Requires a profile module.
iex> Rindle.Delivery.public_delivery?(MyApp.MediaProfile)
false

signed_url_ttl_seconds(profile)

@spec signed_url_ttl_seconds(module()) :: pos_integer()

Returns the signed URL TTL (seconds) for a profile.

Falls back to the application-wide default when the profile does not override it.

Examples

# Requires a profile module.
iex> ttl = Rindle.Delivery.signed_url_ttl_seconds(MyApp.MediaProfile)
iex> is_integer(ttl) and ttl > 0
true

streaming_url(profile, asset_or_key, opts \\ [])

@spec streaming_url(module(), String.t() | map(), keyword()) ::
  {:ok, %{url: String.t(), kind: :progressive | :hls, mime: String.t()}}
  | {:error, term()}

Returns a streaming URL for an asset.

Phase 33 — Promotes the v1.4 no-op delegate to a deterministic 8-branch dispatch tree (per CONTEXT D-19). When a profile has not opted into streaming via the :streaming key, behaviour is byte-for-byte identical to v1.4: progressive playback wrapped as %{url, kind: :progressive, mime} with the existing [:rindle, :delivery, :streaming, :resolved] telemetry emit.

When a profile has opted in:

  1. profile streaming nil → existing v1.4 progressive path (Branch 1)
  2. streaming + binary key → :streaming_provider_requires_asset_struct (Branch 2)
  3. row in (pending|uploading|processing) → :provider_asset_not_ready (Branch 3)
  4. row in :errored → :provider_sync_failed (Branch 4)
  5. row in :ready + playback_id → provider.signed_playback_url/3 (Branch 5)
  6. no row + opts[:strict] == false → progressive fallback (Branch 6)
  7. no row + opts[:strict] == true → :provider_asset_not_ready (Branch 7, D-20)

[:rindle, :delivery, :streaming, :resolved] telemetry is preserved verbatim on Branches 1 and 6 (kind: :progressive); fires with kind: :hls on Branch 5 (D-24).

url(profile, key, opts \\ [])

@spec url(module(), String.t(), keyword()) :: {:ok, String.t()} | {:error, term()}

Returns a deliverable URL for an asset's storage key.

Public profiles return the storage adapter's bare URL; private profiles return a signed URL with the profile's configured TTL. Emits [:rindle, :delivery, :signed] telemetry on success.

Examples

# Requires a configured storage adapter and a key that exists in storage.
iex> {:ok, url} = Rindle.Delivery.url(MyApp.MediaProfile, "uploads/abc.png")
iex> is_binary(url)
true

variant_url(profile, asset, variant, opts \\ [])

@spec variant_url(module(), map(), map(), keyword()) ::
  {:ok, String.t()} | {:error, term()}

Returns a deliverable URL for a variant, falling back to the original asset when the variant is not yet ready.

Stale variants are resolved against the configured stale-serving policy; missing or failed variants fall back to the original asset URL so callers never see broken links.

Examples

# Requires a configured storage adapter and ready/stale variant rows.
iex> {:ok, url} = Rindle.Delivery.variant_url(MyApp.MediaProfile, asset, variant)
iex> is_binary(url)
true