> Discover all available pages from the documentation index: https://mastra.ai/llms.txt

# SensitiveDataFilter

`SensitiveDataFilter` is a span output processor that redacts values whose field names match a configured list. [`Observability`](https://mastra.ai/reference/observability/tracing/configuration) applies it to plain observability configurations by default.

## 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.

```typescript
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`:

```typescript
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

```typescript
new SensitiveDataFilter(options?: SensitiveDataFilterOptions)
```

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

**redactionToken** (`string`): Replacement value used for full redaction and short values under partial redaction. (Default: `'[REDACTED]'`)

**redactionStyle** (`'full' | 'partial' | 'indexed'`): Controls how matched values are redacted. (Default: `'full'`)

```typescript
interface SensitiveDataFilterOptions {
  sensitiveFields?: string[]
  redactionToken?: string
  redactionStyle?: RedactionStyle
}

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

## Redaction styles

### Full redaction

Full redaction replaces each matched primitive value with `redactionToken`.

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

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

### 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.

```typescript
const filter = new SensitiveDataFilter({
  redactionStyle: 'partial',
})
```

```jsonc
// Before
{ "apiKey": "sk-abc123xyz789" }

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

### 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.

```typescript
const filter = new SensitiveDataFilter({
  redactionStyle: 'indexed',
})
```

```jsonc
// 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

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

`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:

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

## Methods

### Processing

#### `process(span)`

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

```typescript
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

#### `shutdown()`

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

```typescript
await filter.shutdown()
```

Returns: `Promise<void>`

## Properties

**name** (`string`): Processor identifier. (Default: `'sensitive-data-filter'`)