Skip to main content
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

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:
FormatDescriptionExample
trueAll locales, redact strategyprivacy: true
falseExplicitly disabledprivacy: false
stringSingle localeprivacy: 'br'
string[]Multiple localesprivacy: ['br', 'us']
objectFull PrivacyConfigprivacy: { 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.
LocalePatterns Included
commonEmail, credit card, JWT, Bearer token, AWS key, IPv4/v6, MAC address, date of birth
brCPF (with validation), CNPJ (with validation), RG, BR phone, CEP, PIS/PASEP, CNS, voter ID
usSSN (with validation), US phone, ZIP code, driver’s license, passport
euIBAN, EU phone, VAT number, NIF (Portugal), NIE (Spain)
// 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:
StrategyCPF ExampleEmail ExampleUse Case
'redact'[REDACTED][REDACTED]Default. Maximum compliance
'mask'***.***247-25j***@company.comDebugging with partial data
'hash'[HASH:a1b2c3d4e5f6][HASH:x9y8z7w6v5u4]Correlation without exposure
'category'[CPF][EMAIL]Know the TYPE without seeing the value
functionCustomCustomFull control
// 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)

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

CategoryWhat It DetectsExamples
documentIdentity documentsCPF, CNPJ, RG, SSN, passport
contactContact informationEmail, phone, WhatsApp
financialFinancial dataCredit card, IBAN, bank account
locationLocation dataCEP, ZIP code
personalPersonal dataDate of birth, name (via field detection)
networkNetwork identifiersIPv4, IPv6, MAC address
credentialCredentialsBearer token, API key, JWT, AWS key
healthHealth dataCNS, health plan ID
customCustom patternsDefined by you

Filter by Category

// 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:
FieldTokensMatch?Reason
contactNamecontact, nameYesname in compound = PII
nome_contatonome, contatoYesnome = always PII
email_contatoemail, contatoYesemail = always PII
name (alone)nameNoAmbiguous alone (could be tool/agent name)
agentIdagent, idNoNo sensitive token

Custom Fields

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

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:
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:
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:
PatternValidation
CPFCheck digits (mod 11)
CNPJCheck digits (mod 11 with weights)
Credit CardLuhn algorithm
SSNCannot start with 000, 666, or 9xx
IPv4Excludes common IPs (127.0.0.1, 0.0.0.0, etc.)

Safety Guarantees

ScenarioBehavior
Circular reference in traceDetected and replaced with [Circular]
Date, Buffer, RegExp valuesPreserved without modification
Error with PII in messageMessage sanitized, structure preserved
Map, Set valuesTraversed and sanitized
Depth > 20 levelsStops recursion, returns data as-is
Internal sanitizer errorDrops the entire trace (fail closed)
privacy not configuredZero impact, identical behavior to default
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.

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

Observability

Tracing and metrics that privacy protects

Supervisor

Multi-agent systems with automatic privacy propagation

Workflows

Data pipelines with built-in PII protection

Best Practices

Production tips for secure agents