Caller-bound actor, tenant, and bounded verification context for Ash actions.
Construct with RequestSeal.Ash.scope/2. principal is a classification only;
the opaque identity value stays in caller-selected actor/tenant values.
Default inspection exposes only principal and authorize?.
to_opts/1 works without Ash installed. When Ash is present at compilation,
the guarded Ash.Scope.ToOpts implementation also enables scope: scope.
When adding Ash to a consumer that already compiled RequestSeal without it,
run mix deps.compile request_seal --force to compile the protocol implementation.
Summary
Functions
Returns explicit actor, tenant, context, and authorize?: true options for Ash calls.
Types
@type t() :: %RequestSeal.Ash.Scope{ actor: term() | nil, authorize?: true, context: %{ request_seal: %{ label: binary(), profile: %{name: :rfc9421} | %{name: :web_bot_auth, revision: binary()}, principal: :attributed | :unattributed, covered: [binary()], content: :not_required | :checked, freshness: :not_evaluated | :evaluated, replay: :not_required | :claimed } }, principal: :attributed | :unattributed, tenant: term() | nil }
Functions
Returns explicit actor, tenant, context, and authorize?: true options for Ash calls.
Works without Ash installed. Pass the result as Ash action options, or pass
scope: scope when Ash's scope protocol is compiled in.
Parameters
scope- aRequestSeal.Ash.ScopefromRequestSeal.Ash.scope/2.
Returns
- keyword list -
[actor: actor, tenant: tenant, context: context, authorize?: true].
Examples
iex> {_public, seed} = :crypto.generate_key(:eddsa, :ed25519)
iex> {:ok, handle} = RequestSeal.Custody.Local.new("ed25519", {:ed25519, seed})
iex> {:ok, key} = RequestSeal.Custody.public_key(handle)
iex> {:ok, message} = RequestSeal.Message.request("GET", "https://docs.example.com/reports/7", [], nil)
iex> {:ok, signed} =
...> RequestSeal.sign(message, %{
...> label: "sig",
...> algorithm: "ed25519",
...> components: ~s[("@method" "@path")],
...> expires_in: 60
...> }, handle)
iex> {:ok, policy} =
...> RequestSeal.Policy.new(%{
...> algorithms: ["ed25519"],
...> components: ~s[("@method" "@path")],
...> key_resolver: fn _ -> {:ok, %{algorithm: "ed25519", key: key}} end,
...> freshness: :not_evaluated,
...> content: :not_required,
...> replay: :not_required
...> })
iex> {:ok, verification} = RequestSeal.verify(signed, policy, label: "sig")
iex> {:ok, scope} =
...> RequestSeal.Ash.scope(verification, %{
...> actor: fn _principal -> :error end,
...> unattributed: :anonymous,
...> tenant: :none
...> })
iex> opts = RequestSeal.Ash.Scope.to_opts(scope)
iex> Keyword.keys(opts)
[:actor, :tenant, :context, :authorize?]
iex> {opts[:actor], opts[:tenant], opts[:authorize?], opts[:context].request_seal.label}
{nil, nil, true, "sig"}
iex> RequestSeal.Custody.Local.release(handle)
:okFragment (requires an Ash resource with a data layer; not executed here):
Ash.read(MyApp.Docs.Report, RequestSeal.Ash.Scope.to_opts(scope))