AshOnetime.Resource

Copy Markdown View Source

Spark resource extension for explicit idempotency and one-time nonce protection.

A protected action must choose a strategy and a nonempty scope. Nonce protection has no stored-response or external-effect surface and always fails closed.

onetime

Protect effectful Ash actions with explicit keyed-effect semantics.

Nested DSLs

onetime.protect

protect action

Protects one effectful action with explicit keyed-effect semantics.

Nested DSLs

Arguments

NameTypeDefaultDocs
actionatomThe name of the Ash action to protect (a create, update, destroy, or action).

Options

NameTypeDefaultDocs
strategyatomThe keyed-effect strategy: :idempotency (replay-safe, stores a response) or :one_time_nonce (single-use, no stored response). Every protection must declare one.
scopelist(any)The nonempty identity scope that partitions keyed effects. Components are static strings, tenant/attribute references, or resolver modules; missing scope is an error.
keyanyThe key source that names a single keyed effect within the scope: a client idempotency argument, a verified proof, or a minted token.
fingerprintkeywordOptional content fingerprint (arguments: / attributes: lists) that distinguishes distinct effects under the same key.
retentionanyHow long a stored idempotent response is retained before it may be re-executed, as a {count, unit} tuple (e.g. {24, :hour}).
windowkeywordNonce replay window bounds: max_age: and clock_skew: as {count, unit} tuples. Applies to :one_time_nonce strategies.
commit:with_action | :independent:with_actionNonce commit boundary. :with_action (default) commits the nonce claim inside the protected action's transaction, so an action-body failure rolls the spend back (correct for a single-use authenticator whose retry bears a fresh proof). :independent commits the claim in its own transaction before the action body runs, so a body failure leaves the proof spent for the acceptance window — RFC 9449 §11.1 request-attempt scope (the DPoP replay fence). Applies to :one_time_nonce only; rejected for :idempotency.
external_effectmoduleOptional module exporting the external-effect contract for idempotent actions that must observe or reverse a side effect. Not available for nonce strategies.
on_definite_store_failure:fail_closed | :execute_untracked:fail_closedWhat to do when the authoritative store is definitively unavailable: :fail_closed (reject) or :execute_untracked (run once with telemetry, no replay safety).
limitskeyword[]The single, unified vocabulary of size bounds for this protection. Valid keys: max_key_bytes, max_token_bytes, max_scope_components, max_fingerprint_bytes, verifier_timeout_ms, and max_cache_entry_bytes bound the key/verification/cache paths; max_response_bytes, max_response_depth, max_response_nodes, max_response_entries, and max_response_scalar_bytes bound the response payload (structural limits). Each is a positive integer at or below its package ceiling. Unknown keys are rejected at compile time. Response bounds apply only to idempotent strategies (which store a payload); nonce strategies accept the keys but do not encode a response.

onetime.protect.response

response codec

Declares the response codec, field allowlist, and result classifier.

Arguments

NameTypeDefaultDocs
codecmoduleThe module implementing the response codec. Must export format_tag/0, encode/3, and decode/4 and yield a tag the store accepts.

Options

NameTypeDefaultDocs
fieldslist(atom)[]The resource attributes projected into the stored/replayed response payload. Acts as the field allowlist; attributes not named here never enter the response.
classifymoduleThe module exporting classify/2 that decides whether a result is stored, rejected, or rolled back. Required for idempotency strategies.
codec_optskeyword[]Codec-specific options forwarded to the codec: the third argument to encode/3 and the fourth argument to decode/4.

Introspection

Target: AshOnetime.Resource.Response

Introspection

Target: AshOnetime.Resource.Protection