How It Works
A profile is one complete configuration: the strategies to run, the keys that are safe or blocked, the patterns to look for, and what to do with what is found. Five ship: default, strict, observability, file_scan and performance. Every entry point takes a profile name, so the same value can be pseudonymised on one channel and removed on another.
Strategies run in the order the profile lists them. Key rules decide by the name a value sits under; pattern rules, known secrets, entropy and entity recognition decide by content and report what they found and where; path rules decide by location and are checked before anything else. Detections are resolved once, so two rules matching the same text produce one rewrite and a surrogate written for one detection is never re-detected by the next.
Operators decide what replaces a detection, per entity rather than per rule:
'operators' => [
'default' => 'redact', // [REDACTED]
'email' => ['surrogate' => ['preserve_domain' => true]], // u_7f3ac9@customer.com, stable
'credit_card' => ['partial' => ['keep' => 4]], // ************1111
'ssn' => 'nullify', // null, so a typed field stays typed
],
surrogate, hash and tokenize are keyed with an HMAC derived from APP_KEY (or a key of your own), so the same input always yields the same stand-in and logs stay joinable without a route back to the original.
Scanning runs the same rules over files and git history:
php artisan redactor:scan --staged --bail # the pre-commit gate
php artisan redactor:scan --diff=origin/main --output=sarif > redactor.sarif
php artisan redactor:scan --update-baseline # accept what is already there