Anvil.PII.Retention (Anvil v0.1.1)

View Source

Retention policy enforcement for PII fields.

This module provides functions for identifying expired labels and applying retention actions (soft delete, hard delete, or field-level redaction).

Retention Actions

  • :soft_delete - Tombstone: keep metadata, strip payload
  • :hard_delete - Permanent deletion (breaks reproducibility)
  • :field_redaction - Redact only expired fields, keep unexpired

Examples

iex> schema_def = %{
...>   fields: [
...>     %{name: "notes", pii: :possible, retention_days: 90}
...>   ]
...> }
iex> label = %{submitted_at: ~U[2024-01-01 00:00:00Z], payload: %{"notes" => "test"}}
iex> now = ~U[2025-01-01 00:00:00Z]
iex> Anvil.PII.Retention.has_expired_fields?(schema_def, label, now)
true

Summary

Functions

Extracts field metadata from schema definition.

Finds labels with expired PII fields based on retention policies.

Checks if a label has any expired fields based on schema definition.

Processes a batch of expired labels with the specified retention action.

Types

retention_action()

@type retention_action() :: :soft_delete | :hard_delete | :field_redaction

Functions

apply_retention_action(label, action, schema_definition, opts \\ [])

@spec apply_retention_action(
  Anvil.Schema.Label.t(),
  retention_action(),
  map(),
  keyword()
) ::
  {:ok, Anvil.Schema.Label.t()} | {:error, term()}

Applies retention action to a label.

Parameters

  • label - The label to process
  • action - Retention action to apply (:soft_delete, :hard_delete, :field_redaction)
  • schema_definition - Schema definition with field metadata
  • opts - Additional options

Returns

{:ok, label} on success, {:error, reason} on failure.

extract_field_metadata(schema_definition)

@spec extract_field_metadata(map()) :: map()

Extracts field metadata from schema definition.

Returns a map of field names to metadata maps.

Examples

iex> schema_def = %{
...>   fields: [
...>     %{name: "notes", metadata: %{pii: :possible, retention_days: 90}}
...>   ]
...> }
iex> Anvil.PII.Retention.extract_field_metadata(schema_def)
%{"notes" => %{pii: :possible, retention_days: 90}}

find_expired_labels(opts \\ [])

@spec find_expired_labels(keyword()) :: [Anvil.Schema.Label.t()]

Finds labels with expired PII fields based on retention policies.

Returns a list of labels where at least one field has exceeded its retention period.

Options

  • :now - Current time for expiration check (default: DateTime.utc_now())
  • :queue_id - Filter by specific queue
  • :limit - Limit number of results

Examples

iex> labels = Anvil.PII.Retention.find_expired_labels()
[%Label{...}, ...]

has_expired_fields?(schema_definition, label, now \\ DateTime.utc_now())

@spec has_expired_fields?(map(), map(), DateTime.t()) :: boolean()

Checks if a label has any expired fields based on schema definition.

Examples

iex> schema_def = %{fields: [%{name: "notes", metadata: %{retention_days: 90}}]}
iex> label = %{submitted_at: ~U[2024-01-01 00:00:00Z], payload: %{"notes" => "test"}}
iex> now = ~U[2025-01-01 00:00:00Z]
iex> Anvil.PII.Retention.has_expired_fields?(schema_def, label, now)
true

process_expired_labels(opts \\ [])

@spec process_expired_labels(keyword()) :: {:ok, non_neg_integer()} | {:error, term()}

Processes a batch of expired labels with the specified retention action.

Returns {:ok, count} where count is the number of labels processed.

Options

  • :dry_run - If true, only counts labels without processing (default: false)
  • :now - Current time for expiration check (default: DateTime.utc_now())
  • :action - Retention action to apply (default: :field_redaction)

Examples

iex> Anvil.PII.Retention.process_expired_labels(queue_id: queue_id, dry_run: true)
{:ok, 42}

iex> Anvil.PII.Retention.process_expired_labels(action: :soft_delete)
{:ok, 15}