Anvil.PII (Anvil v0.1.1)

View Source

PII (Personally Identifiable Information) field annotation handling.

This module provides functions for managing PII metadata on schema fields, including PII levels, retention policies, and redaction strategies.

PII Levels

  • :none - No PII expected (e.g., boolean labels, enums)
  • :possible - May contain PII (free-text fields with guidelines to avoid PII)
  • :likely - Expected to contain PII (e.g., labeler feedback, error reports)
  • :definite - Always contains PII (e.g., labeler email, IP address)

Retention Policies

  • :indefinite - Keep forever (structural labels with no PII)
  • <integer> - Days until eligible for deletion (e.g., 90, 365)

Redaction Policies

  • :preserve - Keep field unchanged (explicit opt-in)
  • :strip - Remove field entirely
  • :truncate - Truncate to first N characters
  • :hash - Hash value (preserves uniqueness for grouping)
  • :regex_redact - Apply regex-based redaction patterns

Examples

iex> field_metadata = %{pii: :possible, retention_days: 365, redaction_policy: :truncate}
iex> Anvil.PII.pii_level(field_metadata)
:possible

iex> Anvil.PII.redaction_policy(field_metadata)
:truncate

Summary

Functions

Calculates the expiration date for a field based on retention policy.

Checks if a field is expired based on retention policy.

Returns true if the field has any PII risk (level other than :none).

Returns the PII level for a field based on its metadata.

Returns the redaction policy for a field based on its metadata.

Returns the retention policy for a field based on its metadata.

Checks if a field should be redacted during export based on redaction mode.

Validates PII metadata structure.

Types

pii_level()

@type pii_level() :: :none | :possible | :likely | :definite

redaction_policy()

@type redaction_policy() :: :preserve | :strip | :truncate | :hash | :regex_redact

retention_policy()

@type retention_policy() :: :indefinite | pos_integer()

Functions

expiration_date(metadata, submitted_at)

@spec expiration_date(map(), DateTime.t()) :: DateTime.t() | nil

Calculates the expiration date for a field based on retention policy.

Returns nil for indefinite retention.

Examples

iex> submitted_at = ~U[2025-01-01 00:00:00Z]
iex> metadata = %{retention_days: 90}
iex> Anvil.PII.expiration_date(metadata, submitted_at)
~U[2025-04-01 00:00:00Z]

iex> metadata = %{retention_days: :indefinite}
iex> Anvil.PII.expiration_date(metadata, ~U[2025-01-01 00:00:00Z])
nil

expired?(metadata, submitted_at, now \\ DateTime.utc_now())

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

Checks if a field is expired based on retention policy.

Examples

iex> submitted_at = ~U[2024-01-01 00:00:00Z]
iex> now = ~U[2025-12-01 00:00:00Z]
iex> metadata = %{retention_days: 90}
iex> Anvil.PII.expired?(metadata, submitted_at, now)
true

iex> metadata = %{retention_days: :indefinite}
iex> Anvil.PII.expired?(metadata, submitted_at, now)
false

has_pii_risk?(metadata)

@spec has_pii_risk?(map()) :: boolean()

Returns true if the field has any PII risk (level other than :none).

pii_level(metadata)

@spec pii_level(map()) :: pii_level()

Returns the PII level for a field based on its metadata.

Defaults to :none if not specified.

redaction_policy(metadata)

@spec redaction_policy(map()) :: redaction_policy()

Returns the redaction policy for a field based on its metadata.

Defaults to automatic policy based on PII level if not specified:

  • :none -> :preserve
  • :possible -> :truncate
  • :likely -> :strip
  • :definite -> :strip

retention_policy(metadata)

@spec retention_policy(map()) :: retention_policy()

Returns the retention policy for a field based on its metadata.

Defaults to :indefinite if not specified.

should_redact?(metadata, atom)

@spec should_redact?(map(), :none | :automatic | :aggressive) :: boolean()

Checks if a field should be redacted during export based on redaction mode.

Redaction Modes

  • :none - No redaction (trusted internal exports)
  • :automatic - Apply schema-defined redaction policies
  • :aggressive - Strip all fields with PII level :possible or higher

Examples

iex> field_meta = %{pii: :none}
iex> Anvil.PII.should_redact?(field_meta, :automatic)
false

iex> field_meta = %{pii: :possible, redaction_policy: :truncate}
iex> Anvil.PII.should_redact?(field_meta, :automatic)
true

iex> field_meta = %{pii: :possible}
iex> Anvil.PII.should_redact?(field_meta, :aggressive)
true

validate_metadata(metadata)

@spec validate_metadata(map()) :: :ok | {:error, String.t()}

Validates PII metadata structure.

Returns :ok if valid, {:error, reason} otherwise.