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.
The privacy field accepts multiple formats depending on how much control you need:
Format Description Example trueAll locales, redact strategy privacy: truefalseExplicitly disabled privacy: falsestringSingle locale privacy: 'br'string[]Multiple locales privacy: ['br', 'us']objectFull 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 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:
Strategy CPF Example Email Example Use 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 functionCustom Custom Full 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
Category What It Detects Examples documentIdentity documents CPF, CNPJ, RG, SSN, passport contactContact information Email, phone, WhatsApp financialFinancial data Credit card, IBAN, bank account locationLocation data CEP, ZIP code personalPersonal data Date of birth, name (via field detection) networkNetwork identifiers IPv4, IPv6, MAC address credentialCredentials Bearer token, API key, JWT, AWS key healthHealth data CNS, health plan ID customCustom patterns Defined 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:
Field Tokens Match? Reason contactNamecontact, nameYes name in compound = PIInome_contatonome, contatoYes nome = always PIIemail_contatoemail, contatoYes email = always PIIname (alone)nameNo Ambiguous alone (could be tool/agent name) agentIdagent, idNo No 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:
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 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
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).
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