Chronicle.Ecto.Policy (chronicle v0.1.2)

Copy Markdown

Schema-local change selection and protection policy.

The two settings answer different questions:

  • only and except select which field changes are worth reporting. An update touching only excluded fields records nothing at all.
  • redact, hash, and omit withhold a value from the audit store. A withheld field cannot be reconstructed, so versions containing one are reported incomplete.
  • erasable encrypts a field under a key whose identifier comes from another persisted field. Versions remain reconstructable while the key exists and fail explicitly after it is destroyed.

hash stores an unsalted SHA-256 fingerprint. It answers correlation questions — did this field change, did it revert, does it match another record — but it is not secrecy: a value drawn from a small or guessable set can be recovered by hashing candidates until one matches. Use redact or omit when the value itself must not be recoverable.

Excluding a field from diffs does not withhold it: a version still captures every persisted field, so time travel keeps working. Use omit to keep a value out of the store entirely.

Put the policy beside the schema it governs:

defmodule MyApp.Account do
  use Ecto.Schema
  use Chronicle.Schema,
    only: [:email, :password_hash, :session_token],
    redact: [:password_hash],
    hash: [:email],
    omit: [:session_token],
    erasable: [display_name: :privacy_key_id]

  schema "accounts" do
    field :email, :string
    field :display_name, :string
    field :password_hash, :string
    field :privacy_key_id, :string
    field :session_token, :string
  end
end

only: :all is the default. except removes fields from that set. Each erasable entry maps the encrypted field to the field holding its opaque external key identifier. That identifier must be a non-empty string when a non-nil protected value is written.

A field may have only one protection strategy. Options must be literals so invalid policy shapes fail during compilation, and both protected and key identifier fields are checked against the Ecto schema once the module has compiled.

Summary

Types

t()

@type t() :: %{
  only: :all | [atom()],
  except: [atom()],
  redact: [atom()],
  hash: [atom()],
  omit: [atom()],
  erasable: [{atom(), atom()}]
}

Functions

get(schema)

@spec get(module() | nil) :: t()

tracked?(policy, field)

@spec tracked?(t(), atom()) :: boolean()