View Source Errata.Redaction (Errata v1.9.0)

Removes sensitive values from error context before it leaves the process.

Errata encourages putting arbitrary metadata in an error's :context, and then ships that context outward — Errata.to_map/1 (and therefore the JSON encoding), Errata.log/2 as Logger metadata, and Errata.report/2 as [:errata, :error] telemetry metadata. Without redaction, this puts a password in your log aggregator the first time someone writes context: %{params: params}.

Redaction applies at the serialization seam, not at creation. The error struct you hold locally keeps the real values, so inspect/1, a debugger, and a rescue clause all still show you what actually happened. Only the copies Errata emits are redacted.

Declaring keys

Most types declare their sensitive keys with the :redact option:

defmodule MyApp.Auth.LoginFailed do
  use Errata.DomainError, redact: [:password, :token]
end

For a floor of protection across every error type in an application, set the keys globally:

config :errata, redact: [:password, :token, :secret, :authorization, :api_key]

The two compose: a type redacts its declared keys plus the global ones. The global default is [], so redaction is opt-in and no existing context changes shape until you ask for it.

Nesting

Redaction is recursive. This is the point rather than a bonus — the common way to leak a secret is not context: %{password: pw} but a whole map or struct captured wholesale:

context: %{params: %{"email" => email, "password" => pw}}

A declared key is replaced wherever it appears: at the top level, inside nested maps, and inside lists and tuples of those. Both atom and binary keys match, so declaring :password also covers the "password" string key that arrives from a JSON body or a Plug params map.

Structs in the context are traversed as well, and come back as structs of the same type — a redacted %MyApp.User{} is still a %MyApp.User{}, with its sensitive fields replaced.

Custom rules

When a key list isn't enough, override redact_context/1 on the error module. It receives the error and returns the context map to serialize:

defmodule MyApp.Api.CallFailed do
  use Errata.InfrastructureError

  def redact_context(%{context: context}) do
    context
    |> Map.drop([:raw_response])
    |> Errata.Redaction.redact([:authorization])
  end
end

Summary

Functions

Returns the globally configured redaction keys (config :errata, redact: [...]).

Replaces the value of every occurrence of keys in term with "[REDACTED]".

Returns the marker substituted for a redacted value: "[REDACTED]".

Functions

@spec global_keys() :: [atom() | String.t()]

Returns the globally configured redaction keys (config :errata, redact: [...]).

Defaults to [], so nothing is redacted until an application opts in.

@spec redact(term(), [atom() | String.t()]) :: term()

Replaces the value of every occurrence of keys in term with "[REDACTED]".

Recurses through maps, structs, lists, and tuples. A key matches whether it is written as an atom or as a binary, so redact(term, [:password]) also redacts a "password" key.

Returns term unchanged when keys is empty.

Examples

iex> Errata.Redaction.redact(%{user: "kim", password: "hunter2"}, [:password])
%{user: "kim", password: "[REDACTED]"}

iex> Errata.Redaction.redact(%{params: %{"token" => "abc"}}, [:token])
%{params: %{"token" => "[REDACTED]"}}

iex> Errata.Redaction.redact(%{user: "kim"}, [])
%{user: "kim"}
@spec redacted_marker() :: String.t()

Returns the marker substituted for a redacted value: "[REDACTED]".