Version-to-version migration notes. ash_onetime follows semantic versioning: breaking DSL or contract changes bump the minor version (the library is pre-1.0), and each breaking change lands here with the exact edit to make.

The published package is v0.3.0. Set your dependency to the minor range to pick up patches automatically and review this page on each minor bump:

{:ash_onetime, "~> 0.3"}

Ash floor raised to 3.31.1 (v0.3.0, security-driven)

v0.3.0 tightens the Ash requirement from >= 3.29.3 and < 4.0.0 to >= 3.31.1 and < 4.0.0. Two Ash advisories published during the v0.2.0 window affect Ash below 3.31.1, both patched in 3.31.1: EEF-CVE-2026-70395 (predicate injection in manage_relationship belongs_to lookup disclosing secret lookup keys) and EEF-CVE-2026-69659 (memory exhaustion via unbounded keyset-cursor deserialization). A security library must not admit a vulnerable floor. Bump Ash to ≥ 3.31.1, then bump ash_onetime:

{:ash_onetime, "~> 0.3"}

See ADR-0004 (Security-driven Ash floor). The CI compatibility matrix narrows from [3.29.3, 3.30.1, latest] to [3.31.1, latest].

Forward response-partition maintenance (apply once to existing installs)

Two data-layer fixes shipped for the response store: the response_partition index (cleanup's partition-empty check was a full scan) and forward monthly partition creation (payloads past the install window routed to _default and were never dropped, silently defeating bounded retention). Greenfield installs on the current version get both automatically from the install migration. Existing installs should generate and run the forward migration once:

mix ash_onetime.gen.roll_forward --repo MyApp.Repo --months 18
mix ecto.migrate

It adds the index, back-fills the elapsed+forward partitions, and drains past-retention payloads stranded in _default. After that, schedule mix ash_onetime.roll_partitions (or AshOnetime.Oban.PartitionWorker) on a cadence ahead of your retention horizon — see Operations. This is additive (no DSL/contract change) and does not require a dependency version bump.

DPoP replay fence (commit: :independent)

Additive: a new opt-in commit: option on :one_time_nonce protections (default :with_action). No migration, no schema change, no new error code. Existing nonce consumers are unchanged. Adopt by adding commit :independent to a nonce protect block whose proof should survive action-body failure (RFC 9449 §11.1). See ADR-0003 (Independent-commit nonce) and the operational characteristics for pool-sizing notes. Declaring commit: on :idempotency is now a compile error (idempotency commits with its effect).

Single limits surface (shipped in v0.1.0)

The dual limits surface collapsed to one protect-level vocabulary in v0.1.0. Response-size limits are declared on the protect block, not the response entity:

protect :charge do
  strategy :idempotency
  # ...
  response MyApp.ChargeCodec, fields: [:id, :status], classify: MyApp.ChargeClassifier
  limits max_response_bytes: 8_388_608
end

The protect limits: option accepts the full 11-key union — the key/verification/cache keys (max_key_bytes, max_token_bytes, max_scope_components, max_fingerprint_bytes, verifier_timeout_ms, max_cache_entry_bytes) and the response-payload keys (max_response_bytes, max_response_depth, max_response_nodes, max_response_entries, max_response_scalar_bytes). All keys are validated at compile time. If you are upgrading from a pre-v0.1.0 snapshot, move any response ..., limits: [max_response_*: ...] onto protect ..., limits: [...].

This change is additive in coverage (no limit is lost) and removes a redundant configuration surface where two places could spell overlapping limits.

Between releases

Non-breaking additions (new optional integrations, new introspection helpers, new guides) ship in patch or minor releases and need no migration. Check the CHANGELOG for the full list per release.

If you depend on a private (non-documented) module or function, it may change in any release — the public contract is the documented DSL, the modules in the API reference, and the behaviours (AshOnetime.Codec, the classify/2 contract on AshOnetime.ResponseClassifier, verification callbacks returning AshOnetime.Verified).