Skip to main content

SensitiveDataFilter

SensitiveDataFilter is a span output processor that redacts values whose field names match a configured list. Observability applies it to plain observability configurations by default.

Usage example
Direct link to Usage example

Configure the automatically applied filter with the top-level sensitiveDataFilter option. The filter runs after user-provided span output processors so it can redact values they add or expose.

src/mastra/index.ts
import { Mastra } from '@mastra/core/mastra'
import { Observability, MastraStorageExporter } from '@mastra/observability'

export const mastra = new Mastra({
observability: new Observability({
configs: {
default: {
serviceName: 'my-service',
exporters: [new MastraStorageExporter()],
},
},
sensitiveDataFilter: {
sensitiveFields: ['password', 'authorization', 'customerSecret'],
redactionToken: '[HIDDEN]',
redactionStyle: 'full',
},
}),
})

Passing sensitiveFields replaces the default field list. Include every default field you still want to redact.

To disable automatic filtering for all plain configurations, set sensitiveDataFilter to false:

new Observability({
configs: {
debug: {
serviceName: 'debug-service',
exporters: [new MastraStorageExporter()],
},
},
sensitiveDataFilter: false,
})

If a plain configuration already contains a SensitiveDataFilter in spanOutputProcessors, Observability uses that instance and doesn't add another. Pre-instantiated ObservabilityInstance values aren't modified. Add the processor when constructing those instances if they need filtering.

Constructor
Direct link to Constructor

new SensitiveDataFilter(options?: SensitiveDataFilterOptions)

sensitiveFields?:

string[]
= ['password', 'token', 'secret', 'key', 'apikey', 'auth', 'authorization', 'bearer', 'bearertoken', 'jwt', 'credential', 'clientsecret', 'privatekey', 'refresh', 'ssn']
Complete list of field names to redact. Matching is case-insensitive and ignores separators. Replaces the default list when provided.

redactionToken?:

string
= '[REDACTED]'
Replacement value used for full redaction and short values under partial redaction.

redactionStyle?:

'full' | 'partial' | 'indexed'
= 'full'
Controls how matched values are redacted.
interface SensitiveDataFilterOptions {
sensitiveFields?: string[]
redactionToken?: string
redactionStyle?: RedactionStyle
}

type RedactionStyle = 'full' | 'partial' | 'indexed'

Redaction styles
Direct link to Redaction styles

Full redaction
Direct link to Full redaction

Full redaction replaces each matched primitive value with redactionToken.

// Before
{ "apiKey": "sk-abc123xyz789", "userId": "user_123" }

// After
{ "apiKey": "[REDACTED]", "userId": "user_123" }

Partial redaction
Direct link to Partial redaction

Partial redaction preserves the first and last three characters. Values with six or fewer characters use redactionToken instead. Non-string primitive values are converted to strings before redaction.

const filter = new SensitiveDataFilter({
redactionStyle: 'partial',
})
// Before
{ "apiKey": "sk-abc123xyz789" }

// After
{ "apiKey": "sk-…789" }

Indexed redaction
Direct link to Indexed redaction

Indexed redaction replaces each unique value with a stable token derived from the first field name that matched it, for example [APIKEY_1]. The same value maps to the same token across the spans of a trace while the trace's mapping is retained, so redacted values stay correlatable without exposing the raw value. Later occurrences under other sensitive fields reuse the first token.

const filter = new SensitiveDataFilter({
redactionStyle: 'indexed',
})
// Before (two spans in the same trace)
[{ "apiKey": "sk-alice-key" }, { "apiKey": "sk-bob-key" }]

// After
[{ "apiKey": "[APIKEY_1]" }, { "apiKey": "[APIKEY_2]" }]

The mapping is scoped per trace. Numbering restarts for each trace, so tokens from different traces can't be linked.

State is bounded in two ways. The filter keeps mappings for the 1000 most recently used traces. The least recently used trace is evicted beyond that, and spans arriving for an evicted trace start a fresh mapping, so later values may receive new tokens. Each trace also tracks up to 1000 unique values. Once that cap is reached, already-tracked values keep their tokens and new values are redacted with redactionToken.

Non-string values are converted to strings before a token is assigned.

Field matching
Direct link to Field matching

Field names are lowercased and stripped of non-alphanumeric characters before comparison:

  • api-key, api_key, Api Key, and apiKey normalize to apikey.
  • Matching is exact after normalization. token matches Token, but doesn't match promptTokens, tokenCount, or authToken.

When a matched field contains an object or array, the filter traverses that value and redacts sensitive fields within it rather than replacing the complete structure.

Processing behavior
Direct link to Processing behavior

process() filters these top-level span fields independently:

  • attributes
  • metadata
  • input
  • output
  • errorInfo

Within each field, the processor:

  • Traverses nested objects and arrays.
  • Parses and filters JSON strings that contain objects or arrays. It then serializes the filtered value.
  • Leaves invalid JSON strings unchanged.
  • Preserves Date instances.
  • Replaces repeated object references with [Circular Reference].

If filtering fails anywhere within one top-level span field, that complete field is replaced with this marker:

{
error: {
processor: 'sensitive-data-filter'
}
}

Methods
Direct link to Methods

Processing
Direct link to Processing

process(span)
Direct link to processspan

Redacts the supported fields on the supplied span in place and returns the same span.

import type { AnySpan } from '@mastra/core/observability'
import { SensitiveDataFilter } from '@mastra/observability'

const filter = new SensitiveDataFilter()

export function redactSpan(span: AnySpan): AnySpan {
return filter.process(span)
}

span:

AnySpan
Span whose attributes, metadata, input, output, and error information are filtered.

Returns: AnySpan

Lifecycle
Direct link to Lifecycle

shutdown()
Direct link to shutdown

Completes processor shutdown and clears the per-trace state used by indexed redaction.

await filter.shutdown()

Returns: Promise<void>

Properties
Direct link to Properties

name:

string
= 'sensitive-data-filter'
Processor identifier.