RequestSeal.Finch (RequestSeal v0.2.0)

Copy Markdown View Source

Optional finalized Finch translation and signing. No pool is started or owned.

Requests preserve same-name header order and Finch's actual request target. body: :as_is (default) converts iodata to binary; streaming bytes remain unavailable. body: {:retain, max} enumerates a stream once under an explicit byte bound and replaces it with replayable bytes. No stream is silently read. Signing refuses unavailable body coverage, host, request req/tr components, conflicting digests, or unfaithful required fields. Cover @authority instead of host. Nil content hashes as empty bytes.

The signing specification is RequestSeal.signing_spec/0: required label, algorithm, serialized components Inner List, and positive expires_in. Defaults are created: true, nonce: :random, alg: true (false for JWS algorithm tuples), keyid: nil, tag: nil, digest: nil, and field_schemas: %{}. Explicit full specs with all six parameters keys, digest, and field_schemas retain their existing bytes, including nil expiry. Parameter order is created, expires, nonce, alg, keyid, tag. Nonces use 32 CSPRNG bytes, fresh on every signing invocation. signing_timeout defaults to 5,000 ms (1–300,000) for both KeyHandle and function signers. Function signers use custody's monitored workers; expiry or caller death terminates their work. clock defaults to system seconds. Digest is nil or an explicit unique SHA-256/SHA-512 list. Covered content-length is supplied only for retained bytes. Iodata retention defaults to a 16 MiB bound; an explicit retention choice sets its own bound. Digest computation retains its core 16 MiB limit. Unknown/duplicate options reject.

response_message/3 defaults to body: :retained; :consumed preserves unavailability for caller-fed streaming hashes. Trailers remain separate. verify/4 requires label, optionally accepts digest_state at caller EOF, and associates the exact supplied request. Streaming callers withhold their own effects until the returned verification succeeds. Errors use RequestSeal.Adapter.Error; no framework exception text is retained.

Summary

Types

spec()

@type spec() ::
  RequestSeal.signing_spec()
  | %{
      label: binary(),
      components: binary(),
      algorithm: RequestSeal.Crypto.algorithm(),
      parameters: %{
        created: boolean(),
        expires_in: pos_integer() | nil,
        nonce: :random | nil,
        keyid: binary() | nil,
        tag: binary() | nil,
        alg: boolean()
      },
      digest: [binary()] | nil,
      field_schemas: map()
    }

Functions

request_message(request, opts \\ [])

@spec request_message(Finch.Request.t(), keyword()) ::
  {:ok, RequestSeal.Message.t()} | {:error, RequestSeal.Adapter.Error.t()}

response_message(response, request, opts \\ [])

@spec response_message(Finch.Response.t(), Finch.Request.t(), keyword()) ::
  {:ok, RequestSeal.Message.t()} | {:error, RequestSeal.Adapter.Error.t()}

sign(request, spec, signer, opts \\ [])

verify(response, request, policy, opts \\ [])