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 exampleDirect 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.
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.
ConstructorDirect link to Constructor
new SensitiveDataFilter(options?: SensitiveDataFilterOptions)
sensitiveFields?:
redactionToken?:
redactionStyle?:
interface SensitiveDataFilterOptions {
sensitiveFields?: string[]
redactionToken?: string
redactionStyle?: RedactionStyle
}
type RedactionStyle = 'full' | 'partial' | 'indexed'
Redaction stylesDirect link to Redaction styles
Full redactionDirect 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 redactionDirect 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 redactionDirect 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 matchingDirect link to Field matching
Field names are lowercased and stripped of non-alphanumeric characters before comparison:
api-key,api_key,Api Key, andapiKeynormalize toapikey.- Matching is exact after normalization.
tokenmatchesToken, but doesn't matchpromptTokens,tokenCount, orauthToken.
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 behaviorDirect link to Processing behavior
process() filters these top-level span fields independently:
attributesmetadatainputoutputerrorInfo
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
Dateinstances. - 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'
}
}
MethodsDirect link to Methods
ProcessingDirect 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:
Returns: AnySpan
LifecycleDirect 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>