A content-addressed digest of a row set — what those rows looked like at a moment, so a later comparison can tell whether they have moved.
The motivating use is human sign-off: a signature binds to what was there, not to a key. Store the digest with the signature, recompute it from current rows at read time, and the signature applies only while they match — so a correction, an addition or a removal lapses it automatically, with no revocation bookkeeping and nothing stored that can drift.
Nothing here knows about signatures, though. It digests rows.
Versioning is the point
Every digest carries the scheme version it was produced under, and is compared under that version. Changing the canonicalization therefore cannot invalidate every stored digest on deploy — old records keep evaluating under the old scheme while new ones use the new.
An unknown version never matches (it does not raise): digests from a future build degrade to "stale, re-check", not to a crash on the read path. That asymmetry is deliberate and is the part worth having in a library rather than re-deriving per host — the failure mode of getting it wrong is a silent mass re-ask, which looks like a data problem rather than a deploy problem.
Choosing fields
fields: names what the digest is of, in order, and is part of the
contract: two callers digesting the same rows with different fields get
different digests, correctly. Widen a digest by introducing a new version,
never by changing what an existing one covers.
Basis.digest(rows, fields: [:key, :status])
Basis.matches?(stored, rows, fields: [:key, :status])
Summary
Functions
The current digest-scheme version.
Digest rows — a list of maps — over fields, in field order, sorted so the
row order of the input does not matter.
Does stored still describe rows? Compares under the version stored
carries, not the current one — see the versioning note above.
The scheme version a stored digest was produced under, or 0 for anything
unparseable — which no scheme claims, so it never matches.
Functions
@spec current_version() :: pos_integer()
The current digest-scheme version.
Digest rows — a list of maps — over fields, in field order, sorted so the
row order of the input does not matter.
Options:
:fields— the keys to digest (default[:key]):version— the scheme (default the current one)
Returns the digest, or :unknown_version for a scheme this build does not
know. A row missing one of fields raises: an absent value must never digest
the same as a present one, which is exactly the confusion a content digest
exists to prevent.
Does stored still describe rows? Compares under the version stored
carries, not the current one — see the versioning note above.
false for an unknown version, a nil stored, or any mismatch.
@spec version_of(String.t()) :: non_neg_integer()
The scheme version a stored digest was produced under, or 0 for anything
unparseable — which no scheme claims, so it never matches.