Ankusa.Envelope (ankusa v0.7.0)

Copy Markdown View Source

The claim-checked record that flows through every layer.

The envelope is the only thing components share. Compaction, dispatch, and the dashboard read committed envelopes from the store; they never call the edge.

body is the exact raw bytes as received — signature checks need it verbatim. Keep it as a refcounted binary and pass it by reference.

Summary

Functions

Inverse of to_binary/1,2: reads the unprefixed legacy form (v0) and v1.

Fetch the first value of a request header (case-insensitive).

header/2 for a name that is already lowercase (String.downcase/1): a compiled signature template, a dedupe or ordering spec, which lower the name once when they are built, not on every request.

The tenant-scoped key a consumer dedupes this hook on; see docs/delivery.md.

Serialize an envelope to a compact binary for the store.

Types

t()

@type t() :: %Ankusa.Envelope{
  body: binary(),
  content_type: String.t() | nil,
  dedupe_key: String.t() | nil,
  headers: [{String.t(), String.t()}],
  id: String.t(),
  method: String.t(),
  path: String.t(),
  received_at: integer(),
  seq: non_neg_integer() | nil,
  size: non_neg_integer(),
  source_id: String.t(),
  tenant_id: String.t() | nil,
  trace: Ankusa.Trace.t() | nil,
  verification: Ankusa.Verification.t() | nil
}

Functions

from_binary(bin)

@spec from_binary(binary()) ::
  {:ok, t()} | {:error, :unsupported_envelope_version | :undecodable}

Inverse of to_binary/1,2: reads the unprefixed legacy form (v0) and v1.

Keys the struct does not know (a field added by a newer node) are ignored, so a row written by N+1 decodes on N. A version this node does not know is {:error, :unsupported_envelope_version}; bytes that are no envelope at all are {:error, :undecodable}. Never raises: callers dead-letter or log.

header(envelope, name)

@spec header(t(), String.t()) :: String.t() | nil

Fetch the first value of a request header (case-insensitive).

header_downcased(envelope, name)

@spec header_downcased(t(), String.t()) :: String.t() | nil

header/2 for a name that is already lowercase (String.downcase/1): a compiled signature template, a dedupe or ordering spec, which lower the name once when they are built, not on every request.

idempotency_key(env)

@spec idempotency_key(t()) :: String.t()

The tenant-scoped key a consumer dedupes this hook on; see docs/delivery.md.

to_binary(env, opts \\ [])

@spec to_binary(t(), keyword()) :: binary()

Serialize an envelope to a compact binary for the store.

body is kept verbatim; the term is :erlang.term_to_binary/2 with :deterministic so re-encoding a replayed record is byte-stable.

Versions

  • 0 (default): the bare term, as every release up to 0.6 wrote it.
  • 1 (version: 1): <<"AE", 1, term::binary>>. Nothing writes it by default yet: this release reads both, the next one flips the default, so a rollback from this release to the previous one still reads every row it wrote (see docs/deployment.md, "Rolling back a minor").