PII Redaction Configuration

View as Markdown

Use this page when you want to configure the built-in PII redaction plugin component. The component kind is pii_redaction.

For plugin file discovery, precedence, merge behavior, editor controls, and gateway conflict rules, refer to Plugin Configuration Files.

NeMo Relay plugin configuration uses the generic plugin document shape, so field names stay snake_case in every binding and in plugins.toml.

Relation to Raw Middleware

This plugin uses the same sanitize-guardrail middleware family documented in Middleware.

The difference between raw middleware and pii_redaction is the layer of abstraction:

  • Raw middleware asks you to register sanitize callbacks directly in code
  • pii_redaction gives you a first-party, config-driven privacy contract on top of those same runtime hooks

Choose pii_redaction when you want a reusable built-in policy surface. Choose raw middleware when you need bespoke callback logic that does not fit the plugin contract.

Plugin Versus Middleware

Use this matrix when deciding whether to use the built-in pii_redaction plugin or raw sanitize-guardrail middleware directly.

Areapii_redaction pluginRaw middleware
Primary UXDeclarative config through plugins.toml, bindings helpers, and the CLI editorCallback registration in application code
Best fitReusable privacy policy shared across apps or teamsApp-specific logic tied to runtime state or custom heuristics
Built-in actionsremove, redact, regex_replace, hash, maskYou implement the behavior yourself
Built-in detectorsSupportedYou implement the matcher yourself
Codec-aware LLM handlingSupported for the documented codecsYou handle normalization and provider shapes yourself
Validation and editor supportSupportedNot provided
Cross-runtime consistencyFirst-party contract across Relay surfacesDepends on each app’s callback code
FlexibilityLimited to the plugin contractFull control over sanitize behavior
When to choose itYou want the fastest path to a supported privacy surfaceYou need behavior the plugin contract does not express

Component Shape

The top-level PII redaction object contains:

FieldPurpose
versionPII redaction config schema version. Defaults to 1.
modeBackend mode. Current values are builtin and local_model.
inputEnables managed LLM request sanitization.
outputEnables managed LLM response sanitization.
tool_inputEnables sanitization of emitted tool-request observability payloads.
tool_outputEnables sanitization of emitted tool-response observability payloads.
priorityGuardrail priority. Lower values run earlier.
codecManaged LLM provider codec. Required when input or output is enabled.
builtinBuilt-in backend settings used when mode = "builtin".
localLocal-backend settings used when mode = "local_model".
policyComponent-local handling for unknown fields and unsupported values.

At least one managed redaction surface must be enabled.

Backend Support

Areabuiltinlocal_model
Built-in component kind and config validationSupportedSupported
Managed LLM inputSupportedNot implemented
Managed LLM outputSupportedNot implemented
Managed tool_inputSupportedNot implemented
Managed tool_outputSupportedNot implemented
Built-in actionsremove, redact, regex_replace, hash, maskN/A
Codec supportopenai_chat, openai_responses, anthropic_messagesRuntime-specific future implementation
Runtime availabilityAny runtime that includes the nemo-relay-pii-redaction plugin crateRuntimes that install a local backend provider

Built-In Mode

Use builtin mode when NeMo Relay should sanitize emitted observability payloads with a deterministic first-party backend.

This is the recommended mode when the privacy behavior is common enough to be described declaratively with built-in actions, detector presets, exact target paths, and supported codecs.

Requirements

To use mode = "builtin":

  • builtin settings are required.
  • codec is required when input or output is enabled.
  • builtin.action must be remove, redact, regex_replace, hash, or mask.
  • builtin.pattern or builtin.detector is required when builtin.action = "regex_replace" or builtin.action = "redact".

plugins.toml Example

You can write this config directly in plugins.toml, or create and edit it through the CLI with nemo-relay plugins edit. For plugin file discovery, precedence, merge behavior, and editor controls, refer to Plugin Configuration Files.

1version = 1
2
3[[components]]
4kind = "pii_redaction"
5enabled = true
6
7[components.config]
8version = 1
9mode = "builtin"
10codec = "openai_chat"
11input = true
12output = true
13tool_input = true
14tool_output = true
15
16[components.config.builtin]
17action = "regex_replace"
18pattern = "sk-[A-Za-z0-9_-]+"
19replacement = "[REDACTED]"
20target_paths = [
21 "/messages/0/content",
22 "/message",
23 "/api_key",
24 "/result/secret",
25]
26
27[components.config.policy]
28unknown_component = "warn"
29unknown_field = "warn"
30unsupported_value = "error"

This example configures the built-in backend for:

  • LLM request redaction from the normalized request path /messages/0/content
  • LLM response redaction from the normalized response path /message
  • tool argument redaction at /api_key
  • tool result redaction at /result/secret

CLI Editor Support

The NeMo Relay CLI plugin editor now exposes pii_redaction directly through nemo-relay plugins edit.

Use the editor when you want to:

  • Toggle the component on or off
  • Choose builtin or local_model
  • Set the LLM codec
  • Edit builtin action settings such as action, target_paths, pattern, detector, replacement, and masking fields
  • Edit local.backend for a runtime-provided future local-model backend

The editor preserves unknown fields when it rewrites an existing pii_redaction component, so future or runtime-specific settings are not discarded by the interactive edit flow.

If you find yourself needing callback code instead of editor/config fields, it is a sign that raw middleware may be the better fit for that specific policy.

Built-In Settings

The builtin section contains:

FieldPurpose
actionSanitization action. Current values are remove, redact, regex_replace, hash, and mask.
target_pathsExact JSON-pointer paths to sanitize. Empty means every matching string leaf.
patternRegex pattern used when action = "regex_replace" or action = "redact".
detectorOptional built-in matcher preset. Current values are email, phone, api_key, ip_address, ipv6, url, uuid, bearer_token, jwt, credit_card, aws_access_key_id, aws_secret_access_key, gcp_api_key, and azure_storage_account_key.
replacementReplacement text used when action = "regex_replace" or action = "redact". Defaults to [REDACTED].
mask_charMasking token used when action = "mask". Defaults to *.
unmasked_prefixLeading character count to keep when action = "mask". Defaults to 0, unless a detector-specific masking preset is active.
unmasked_suffixTrailing character count to keep when action = "mask". Defaults to 0, unless a detector-specific masking preset is active.

Action Semantics

remove

remove is structural.

When a target matches:

  • object fields are removed
  • array elements become null
  • targeted scalar or root values become null

regex_replace

regex_replace applies the configured regex to matching string leaves and replaces matches with the configured replacement.

If you set detector instead of pattern, the built-in backend uses the detector’s stock matcher regex.

redact

redact is the deterministic whole-match replacement lane.

It uses the same pattern or detector matcher flow as regex_replace, but defaults the replacement token to [REDACTED] and is intended for cases where you do not want to preserve any matched secret characters.

Use redact when you want:

  • credential-style secrets fully replaced
  • a consistent redaction token across detectors
  • clearer policy intent than a custom regex_replace

hash

hash replaces matching string leaves with their SHA-256 hex digest.

When pattern or detector is set, hash only replaces the matching substring instead of hashing the entire string leaf.

mask

mask replaces the middle portion of each matching string leaf with the configured mask_char.

Use unmasked_prefix and unmasked_suffix when you want to preserve a small leading or trailing segment for correlation or debugging, such as the last four characters of a token.

When pattern or detector is set, mask only masks matching substrings inside the string leaf.

When detector is set and you do not specify unmasked_prefix or unmasked_suffix, the built-in backend applies detector-aware defaults:

  • email: Preserves the domain and the first local-part character
  • phone: Preserves the last four digits while keeping separators intact
  • api_key: Preserves the vendor-style prefix such as sk- and the last four characters
  • ip_address: Preserves the last octet
  • ipv6: Preserves the last segment
  • url: Preserves the scheme and host, then collapses the path/query tail
  • uuid: Preserves the last four characters
  • bearer_token: Preserves the auth scheme and the last four characters
  • jwt: Preserves the header segment and the tail of the signature
  • credit_card: Preserves the last four digits while keeping separators intact
  • aws_access_key_id: Preserves the provider prefix and the last four characters
  • aws_secret_access_key: Preserves the last four characters
  • gcp_api_key: Preserves the AIza-style prefix and the last four characters
  • azure_storage_account_key: Preserves the last four characters

Path Semantics

target_paths are exact JSON-pointer matches.

The plugin uses different payload boundaries for tools and LLMs:

  • Tools use JSON-native payloads. Paths point into the emitted tool args or tool result shape directly.
  • LLMs use the selected built-in codec. Prefer normalized Relay paths such as:
    • /messages/0/content for request message content
    • /message for the normalized assistant response text

The current implementation also preserves provider-shaped response-path compatibility for the supported codecs, but normalized LLM paths are the recommended contract for new configuration.

Choosing Between This Plugin and Middleware

Use this plugin when:

  • The privacy behavior should be reusable across applications
  • Config-driven enablement matters more than hand-written callbacks
  • You want built-in detectors and action semantics
  • You want a documented first-party NeMo Relay privacy surface

Use raw middleware when:

  • The policy depends on application-specific runtime state
  • The sanitization logic is too custom for the plugin contract
  • You need to prototype or experiment before standardizing behavior

The runtime effect is still sanitize-guardrail middleware in both cases. The plugin simply gives you a standardized policy layer on top.

Detector Presets

The built-in detector presets are grouped into three deterministic families.

Common PII:

  • email
  • phone
  • ip_address
  • ipv6
  • url

Structured secrets:

  • api_key

  • uuid

  • bearer_token

  • jwt

  • credit_card

    bearer_token is heuristic rather than vendor-specific. It can still match benign bearer-style values, so prefer a narrower detector when you know the credential family.

Cloud credentials:

  • aws_access_key_id
  • aws_secret_access_key
  • gcp_api_key
  • azure_storage_account_key

They are deterministic regex-backed helpers, not model inference.

If target_paths is empty, the built-in backend sanitizes every matching string leaf in the selected payload boundary.

Observability Semantics

The built-in plugin uses sanitize guardrails.

That means:

  • the real provider response value is unchanged
  • the emitted NeMo Relay start or end event payload is sanitized
  • annotated_response is populated from the sanitized end-event payload when a response codec is provided

Local Model Mode

local_model is reserved for a future in-process local-model backend.

Current Status

Currently:

  • the plugin contract accepts mode = "local_model"
  • the local section currently supports:
    • backend
    • model_id
    • detector_profile
    • allow_network
    • max_latency_ms
  • actual behavior depends on a runtime-installed local backend provider

Without a provider, runtimes report the local backend as unavailable during plugin initialization.