Imp.Redaction (Imp v0.7.0)

Copy Markdown View Source

Shared redaction helpers for traces, telemetry, and runtime metadata.

Imp keeps prompts, tool inputs, provider metadata, and optimizer reports inspectable, but credentials and credential-shaped strings must not leak into those artifacts. redact/2 walks ordinary Elixir maps, lists, tuples, and structs, replacing known secret fields and every string that holds a credential with "[REDACTED]".

Clients, retrievers and trackers that hold a credential print through a redacting Inspect implementation, which hides every header value and the query, fragment and user info of every URL as well; inspect(term, structs: false), Erlang's ~p, and a credential kept outside those structs (a bare keyword list in a process's state) bypass it.

Summary

Functions

default_keys()

Returns the default key names treated as sensitive.

iex> :api_key in Imp.Redaction.default_keys()
true

iex> :"x-api-key" in Imp.Redaction.default_keys()
true

drop_credentials(value)

Recursively removes credential-bearing entries while preserving semantic data.

This is intended for persistence and cache identity boundaries where restoring a redaction marker as a runtime option would be misleading. The term is redacted before entries are removed, so a client, retriever or OAuth struct is still a struct when its header values and URL secrets are hidden.

redact(value, keys \\ [:api_key, :authorization, :proxy_authorization, :auth, :bearer, :session, :token, :api_token, :auth_token, :bearer_token, :refresh_token, :id_token, :session_token, :password, :secret, :access_token, :access_key, :access_key_id, :secret_key, :secret_access_key, :client_secret, :private_key, :private_token, :service_account_key, :credential, :credentials, :code_verifier, :"x-api-key"])

Redacts sensitive keys and secret-looking string values.

Key matching is intentionally conservative around common credential names: exact keys plus token-delimited or camel-case provider prefixes such as :openai_api_key are redacted.

A string that holds a credential is replaced whole: a credential is rarely alone, and the text beside a recognized one (the rest of an env dump, a cookie header, a .netrc entry) often holds others no pattern names. This is the one set of credential patterns in Imp; text cleaned anywhere else before it is kept or shown (a command's captured output, as a whole; an optimizer pricing URL) is cleaned with it. A string is a credential when it holds:

  • a private key: a PEM block of any type, a PGP private key block, or a PuTTY key file;
  • a token in a vendor's format: OpenAI, Anthropic and OpenRouter (sk-), Stripe (sk_live_, rk_live_), GitHub, GitLab (glpat-), Hugging Face (hf_), AWS access key ids (AKIA, ASIA), Google API keys (AIza) and OAuth tokens (ya29.), Slack (xox?-), SendGrid (SG.), npm (npm_), PyPI (pypi-), Vault (hvs.), or a JSON Web Token;
  • a Bearer token that ends the string or is closed by punctuation, or one of 16 or more characters with a digit in it wherever it ends (a token without a digit, followed by more text, reads as prose: Bearer token-based-auth, Bearer authentication is ...); or a Basic credential that ends the string;
  • a session= value, or a long hex value assigned to a credential name (token=<hex>, api_key: <hex>);
  • a URL with a password in its user info, or a signed URL's signature or security token (X-Amz-Signature, X-Amz-Security-Token, X-Goog-Signature, Azure's sig).

For example:

iex> Imp.Redaction.redact(%{api_key: "sk-test-secret-1234567890", model: "demo"})
%{api_key: "[REDACTED]", model: "demo"}

iex> Imp.Redaction.redact(%{nested: [%{"authorization" => "Bearer abcdefghijklmnop"}]})
%{nested: [%{"authorization" => "[REDACTED]"}]}

iex> Imp.Redaction.redact("sk-test-secret-1234567890")
"[REDACTED]"

iex> Imp.Redaction.redact(%{tenant_id: "public"}, [:tenant_id])
%{tenant_id: "[REDACTED]"}

iex> Imp.Redaction.redact({:error, "Bearer abcdefghijklmnop"})
{:error, "[REDACTED]"}

iex> Imp.Redaction.redact("key sk-test-secret-1234567890 was used")
"[REDACTED]"

iex> Imp.Redaction.redact("sha 0badcafe0badcafe0badcafe0badcafe0badcafe")
"sha 0badcafe0badcafe0badcafe0badcafe0badcafe"