> ## Documentation Index
> Fetch the complete documentation index at: https://docs.runflow.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Privacy (PII Sanitization)

> Automatically redact personal data from traces and logs before they are persisted

The **privacy** module automatically sanitizes personally identifiable information (PII) from observability traces before they are stored. It works across all execution paths: standalone agents, multi-agent (supervisor), and workflows.

## Quick Start

```typescript theme={null}
import { Agent, openai } from '@runflow-ai/sdk';

const agent = new Agent({
  name: 'Support Agent',
  instructions: 'Help customers with their accounts.',
  model: openai('gpt-4o'),
  privacy: 'br',  // That's it. One line.
});
```

With `privacy: 'br'`, every trace generated by this agent will have CPFs, emails, phone numbers, credit cards, and 30+ other PII patterns automatically redacted before being sent to the observability backend.

## Configuration Formats

The `privacy` field accepts multiple formats depending on how much control you need:

| Format     | Description                    | Example                                          |
| ---------- | ------------------------------ | ------------------------------------------------ |
| `true`     | All locales, `redact` strategy | `privacy: true`                                  |
| `false`    | Explicitly disabled            | `privacy: false`                                 |
| `string`   | Single locale                  | `privacy: 'br'`                                  |
| `string[]` | Multiple locales               | `privacy: ['br', 'us']`                          |
| `object`   | Full `PrivacyConfig`           | `privacy: { locales: ['br'], strategy: 'mask' }` |

## Locales

Each locale adds a set of PII detection patterns. The `common` locale is **always included automatically**, even when you specify other locales.

| Locale   | Patterns Included                                                                          |
| -------- | ------------------------------------------------------------------------------------------ |
| `common` | Email, credit card, JWT, Bearer token, AWS key, IPv4/v6, MAC address, date of birth        |
| `br`     | CPF (with validation), CNPJ (with validation), RG, BR phone, CEP, PIS/PASEP, CNS, voter ID |
| `us`     | SSN (with validation), US phone, ZIP code, driver's license, passport                      |
| `eu`     | IBAN, EU phone, VAT number, NIF (Portugal), NIE (Spain)                                    |

```typescript theme={null}
// Brazilian company — catches CPF, CNPJ, BR phones, plus all common patterns
privacy: 'br'

// US + Brazil — catches SSN, CPF, phones from both countries, etc.
privacy: ['br', 'us']

// International — all locales
privacy: true
```

## Redaction Strategies

Control how PII values are replaced in traces:

| Strategy     | CPF Example           | Email Example         | Use Case                               |
| ------------ | --------------------- | --------------------- | -------------------------------------- |
| `'redact'`   | `[REDACTED]`          | `[REDACTED]`          | **Default.** Maximum compliance        |
| `'mask'`     | `***.***247-25`       | `j***@company.com`    | Debugging with partial data            |
| `'hash'`     | `[HASH:a1b2c3d4e5f6]` | `[HASH:x9y8z7w6v5u4]` | Correlation without exposure           |
| `'category'` | `[CPF]`               | `[EMAIL]`             | Know the TYPE without seeing the value |
| `function`   | Custom                | Custom                | Full control                           |

```typescript theme={null}
// Mask strategy — keeps partial data visible for debugging
privacy: {
  locales: ['br'],
  strategy: 'mask',
}

// Custom strategy — different handling per category
privacy: {
  locales: ['br'],
  strategy: (match, pattern) => {
    if (pattern.category === 'credential') return '[BLOCKED]';
    return `<${pattern.label}>`;
  },
}
```

## Full Configuration (PrivacyConfig)

```typescript theme={null}
const agent = new Agent({
  name: 'Compliance Agent',
  instructions: '...',
  model: openai('gpt-4o'),
  privacy: {
    // --- Basic ---
    enabled: true,                    // default: true
    locales: ['br'],                  // default: ['common']
    strategy: 'redact',               // default: 'redact'

    // --- Categories ---
    includeCategories: ['document', 'contact'],  // only these (optional)
    excludeCategories: ['location'],              // exclude these (optional)

    // --- Field name detection ---
    detectFields: true,               // default: true
    sensitiveFields: ['matricula'],   // additional custom fields
    allowFields: ['agent_id'],        // fields to NEVER sanitize

    // --- Scope ---
    scope: {
      input: true,                    // default: true
      output: true,                   // default: true
      metadata: false,                // default: false
    },

    // --- Custom patterns ---
    customPatterns: [{
      id: 'internal_id',
      label: 'Internal ID',
      category: 'document',
      pattern: /MAT-\d{8}/g,
    }],

    // --- Audit ---
    audit: true,                      // default: false
    onRedaction: (event) => {
      console.log(`PII found: ${event.patternId} at ${event.path}`);
    },

    // --- Post-sanitization hook ---
    onSanitize: (data, context) => {
      // Additional logic after standard sanitization
      return data;
    },
  },
});
```

## PII Categories

| Category     | What It Detects     | Examples                                  |
| ------------ | ------------------- | ----------------------------------------- |
| `document`   | Identity documents  | CPF, CNPJ, RG, SSN, passport              |
| `contact`    | Contact information | Email, phone, WhatsApp                    |
| `financial`  | Financial data      | Credit card, IBAN, bank account           |
| `location`   | Location data       | CEP, ZIP code                             |
| `personal`   | Personal data       | Date of birth, name (via field detection) |
| `network`    | Network identifiers | IPv4, IPv6, MAC address                   |
| `credential` | Credentials         | Bearer token, API key, JWT, AWS key       |
| `health`     | Health data         | CNS, health plan ID                       |
| `custom`     | Custom patterns     | Defined by you                            |

### Filter by Category

```typescript theme={null}
// Only sanitize documents and contacts
privacy: {
  locales: ['br'],
  includeCategories: ['document', 'contact'],
}

// Everything EXCEPT location
privacy: {
  locales: ['br'],
  excludeCategories: ['location'],
}
```

## Field Name Detection

Beyond regex patterns, the sanitizer detects PII by **JSON field names**. This catches sensitive data even when the value itself doesn't match any pattern (e.g., a name field containing "Maria Silva").

Works with all naming conventions: `snake_case`, `camelCase`, `kebab-case`.

### Built-in Sensitive Fields (60+)

* **Documents:** `cpf`, `cnpj`, `rg`, `ssn`, `passport`, `cnh`, `pis`, ...
* **Contact:** `email`, `phone`, `telefone`, `celular`, `whatsapp`, ...
* **Names:** `nome`, `nome_completo`, `full_name`, `first_name`, `last_name`, ...
* **Address:** `address`, `endereco`, `cep`, `logradouro`, `rua`, ...
* **Financial:** `credit_card`, `card_number`, `bank_account`, `iban`, ...
* **Health:** `cns`, `cartao_sus`, `health_plan`, `prontuario`, ...
* **Credentials:** `password`, `senha`, `secret`, `token`, `api_key`, ...
* **Birth:** `birth_date`, `data_nascimento`, `dob`, ...

### Compound Token Matching

Compound field names are split into tokens and matched individually:

| Field           | Tokens             | Match? | Reason                                     |
| --------------- | ------------------ | ------ | ------------------------------------------ |
| `contactName`   | `contact`, `name`  | Yes    | `name` in compound = PII                   |
| `nome_contato`  | `nome`, `contato`  | Yes    | `nome` = always PII                        |
| `email_contato` | `email`, `contato` | Yes    | `email` = always PII                       |
| `name` (alone)  | `name`             | No     | Ambiguous alone (could be tool/agent name) |
| `agentId`       | `agent`, `id`      | No     | No sensitive token                         |

### Custom Fields

```typescript theme={null}
// Add custom sensitive fields
privacy: {
  locales: ['br'],
  sensitiveFields: ['matricula', 'plano_odontologico'],
}

// Allow specific fields to never be sanitized
privacy: {
  locales: ['br'],
  allowFields: ['agent_id', 'execution_id', 'trace_id'],
}
```

## Propagation in Multi-Agent and Workflows

### Multi-Agent (Supervisor Pattern)

Configure privacy **once on the supervisor** — it automatically propagates to all child agents:

```typescript theme={null}
const agent = new Agent({
  name: 'Supervisor',
  instructions: 'Route requests.',
  model: openai('gpt-4o-mini'),
  privacy: 'br',  // Configure HERE only
  agents: {
    qualifier: { name: 'Qualifier', instructions: '...', model: openai('gpt-4o') },
    responder: { name: 'Responder', instructions: '...', model: openai('gpt-4o') },
  },
});
```

```
Supervisor (privacy: 'br')
  |-- trace collector with privacy
  |-- Qualifier agent  --> inherits collector
  |-- Responder agent  --> inherits collector
```

Child agents **do not need** their own `privacy` config.

### Workflows

```typescript theme={null}
const workflow = createWorkflow({
  id: 'qualification',
  privacy: 'br',  // Configure HERE only
  steps: [...],
  inputSchema: z.object({ message: z.string() }),
  outputSchema: z.object({ result: z.string() }),
});
```

```
Workflow (privacy: 'br')
  |-- trace collector with privacy
  |-- Function step  --> traces sanitized
  |-- Agent step     --> inherits collector
  |-- Connector step --> traces sanitized
```

## Standalone Usage (Without Agent/Workflow)

Use the sanitizer directly for custom pipelines or data processing:

```typescript theme={null}
import { createPIISanitizer } from '@runflow-ai/sdk';

const sanitizer = createPIISanitizer({
  locales: ['br'],
  strategy: 'redact',
});

// Sanitize a string
sanitizer.sanitize('My CPF is 529.982.247-25');
// → 'My CPF is [REDACTED]'

// Sanitize an object (deep traversal)
sanitizer.sanitizeDeep({
  customer: {
    nome: 'Maria Silva',
    cpf: '529.982.247-25',
    contact: { email: 'maria@test.com' },
  },
});
// → { customer: { nome: '[REDACTED]', cpf: '[REDACTED]', contact: { email: '[REDACTED]' } } }
```

## Audit and Compliance

Track every redaction event for compliance reporting:

```typescript theme={null}
const agent = new Agent({
  name: 'Audited Agent',
  instructions: '...',
  model: openai('gpt-4o'),
  privacy: {
    locales: ['br'],
    audit: true,
    onRedaction: (event) => {
      // event.patternId  = 'br_cpf'
      // event.category   = 'document'
      // event.path       = 'input.customer.cpf'
      // event.field       = 'input'
      // event.originalLength = 14
      // event.timestamp  = Date
      saveToAuditLog(event);
    },
  },
});
```

## Pattern Validation

Some patterns include mathematical validation to reduce false positives:

| Pattern     | Validation                                     |
| ----------- | ---------------------------------------------- |
| CPF         | Check digits (mod 11)                          |
| CNPJ        | Check digits (mod 11 with weights)             |
| Credit Card | Luhn algorithm                                 |
| SSN         | Cannot start with 000, 666, or 9xx             |
| IPv4        | Excludes common IPs (127.0.0.1, 0.0.0.0, etc.) |

## Safety Guarantees

| Scenario                    | Behavior                                   |
| --------------------------- | ------------------------------------------ |
| Circular reference in trace | Detected and replaced with `[Circular]`    |
| Date, Buffer, RegExp values | Preserved without modification             |
| Error with PII in message   | Message sanitized, structure preserved     |
| Map, Set values             | Traversed and sanitized                    |
| Depth > 20 levels           | Stops recursion, returns data as-is        |
| Internal sanitizer error    | **Drops the entire trace** (fail closed)   |
| `privacy` not configured    | Zero impact, identical behavior to default |

<Note>
  The sanitizer follows a **fail-closed** security model: if something goes wrong during sanitization, the trace is dropped entirely rather than risk leaking PII. This is intentional — data safety over data availability.
</Note>

## Known Limitations

1. **Names in free text**: Proper names inside message text (e.g., "Hello Maria") are **not detected** by regex. Names are only captured via **field name detection** (e.g., a field called `nome`, `contactName`).

2. **Numeric false positives**: Numeric sequences may match phone or ZIP patterns. Use `excludeCategories` or `allowFields` to tune.

## Next Steps

<CardGroup cols={2}>
  <Card title="Observability" icon="chart-mixed" href="/core-concepts/observability">
    Tracing and metrics that privacy protects
  </Card>

  <Card title="Supervisor" icon="users" href="/core-concepts/supervisor">
    Multi-agent systems with automatic privacy propagation
  </Card>

  <Card title="Workflows" icon="diagram-project" href="/core-concepts/workflows">
    Data pipelines with built-in PII protection
  </Card>

  <Card title="Best Practices" icon="lightbulb" href="/best-practices">
    Production tips for secure agents
  </Card>
</CardGroup>
