Input Controls
PII Detector
pii-detector finds personal identifiers such as email addresses, Social Security numbers, and card numbers, and replaces them before the model sees them. My SSN is 123-45-6789 reaches the model as My SSN is [REDACTED:SSN].
It can also block the request instead. On Chat Completions, Responses, and Messages, it redacts the model response too. For a streamed response, the gateway holds the whole response until it has redacted it, so the client waits longer for the first token.
Configuration
Save this as policy-config.yaml in a new test directory. To add it to a running gateway, use the config-first workflow.
pack:
name: pii-protection
version: 1.0.0
enabled: true
policies:
chain:
- pii-detector
policy:
pii-detector:
action: redact
healthcare_mode: false
pci_mode: true
detect_patterns: []
redaction:
marker_format: label
include_metadata: true
preserve_length: false
custom_markers: {}To block requests with card data instead of redacting them, set action: block and keep pci_mode: true.
Try it without a provider
Add these tests to the end of the file:
testing:
suites:
- name: pii-detector
cases:
- name: redacts-identifier
input:
messages:
- role: user
content: "My SSN is 123-45-6789"
expected:
verdict: redact
reason_code: redact.applied
- name: allows-greeting
input:
messages:
- role: user
content: "Hello there"
expected:
verdict: allow
reason_code: okverdictan policy lint --file policy-config.yaml
verdictan policy test --jsonBoth cases report passed: true. The overall reason for the redaction is redact.applied, and the nested policy_results entry names the match: pii.ssn_detected.
A verdict does not show the forwarded text. To see the replacement itself, send the same synthetic identifier through a test gateway and examine the redaction result.
Fields
| Field | Type | Default | Description |
|---|---|---|---|
action | string | redact | What happens to a match in the request: redact replaces it, block rejects the request. Response redaction runs with either value. |
healthcare_mode | boolean | false | Adds HIPAA identifiers: names, addresses, cities, fax numbers, MRNs, health plan IDs, license numbers, device IDs, biometric terms, and photo terms |
pci_mode | boolean | true | Adds payment card numbers, CVV or CVC values, and expiration dates. Cardholder names are not detected. |
detect_patterns | string[] | [] | Extra regular expressions for request and response text. Each match has the type generic_id. |
redaction.marker_format | string | label | label writes [REDACTED:SSN]. asterisk writes ***. partial masks SSNs, card numbers, and email addresses, and writes a label for other types. |
redaction.include_metadata | boolean | true | Adds the type, replacement, offsets, and a SHA-256 hash of each original span to the decision details. false removes only the redaction metadata: the span list, the x-verdictan-redaction-count and x-verdictan-redacted-entities headers, and the true value of x-verdictan-response-redacted. With gateway 0.1.26 or later, the client still gets the redacted text for each redacting policy on Chat Completions, Responses, and Messages. |
redaction.preserve_length | boolean | false | With marker_format: asterisk, writes one asterisk per original character |
redaction.custom_markers | object | {} | Replacement text by lowercase type, such as ssn, email, mrn, pan, or generic_id. Overrides marker_format for that type. |
How it works
- The gateway joins the content of all request messages into one string.
- The base detector runs on it.
healthcare_modeadds the HIPAA checks, andpci_modeadds the card checks.- Each
detect_patternsregular expression runs, and each match is recorded asgeneric_idwith confidencelow. - Matches are sorted by position and by the priority of their type.
- No match: the verdict is
allowwithpii.clean. - A match with
action: block: the verdict isblockwith the reason code of the first match, for examplepii.ssn_detected. - A match with
action: redact: the verdict isredactwith the same reason code. The gateway replaces each match in the request and sets the response headerx-verdictan-prompt-redacted: true. - When the provider responds, the same detectors run on the response text. A match is replaced and adds an output result with verdict
redactand reasonpii.detected. - A redacted response carries the headers
x-verdictan-response-redacted,x-verdictan-redaction-count, andx-verdictan-redacted-entities.
The input decision details contain first_kind, first_reason_code, first_confidence, detection_count, and confidence_counts. The output details contain detection_count and entity_types.
Detection coverage
| Mode | Identifiers | Reason code prefix |
|---|---|---|
| Base detector, always on | Email addresses, SSNs, phone numbers, public IPv4 and IPv6 addresses, URLs, MAC addresses, IMEIs, VINs, ZIP codes, dates, account and routing numbers, IBANs, SWIFT codes, license plates | pii. |
healthcare_mode: true | Names, addresses, cities, fax numbers, MRNs, health plan IDs, certificate and license numbers, device IDs, biometric terms, photo terms | phi. |
pci_mode: true | Payment card numbers, CVV or CVC values, expiration dates | pii.pan_detected, pci. |
detect_patterns | Each regular expression match | pii.id_detected |
Phase and verdicts
| Item | Value |
|---|---|
| Phase | input for the request, output for response redaction |
| Request families | Chat Completions, Completions, Responses, Messages, Embeddings, Audio transcriptions, Moderation |
| Verdicts | allow, redact, block |
| Reason codes | pii.clean, pii.detected, and a pii.*, phi.*, or pci.* code for the first match, such as pii.ssn_detected or phi.mrn_detected |
A blocked request gets HTTP 400 with error code content_policy_violation. The body has a verdictan.decision object with verdict BLOCK and the reason code.
Output redaction reason codes
pii-detector, dlp-filter, hipaa-phi-detector, student-privacy, case-privacy, and dual-use-filter with action: redact use the same redaction pass on the model response. When this pass changes the response, the decision event has one output result with the verdict redact. With gateway 0.1.26 or later, the result names one policy and uses the reason code of that policy:
| Policy | Output redaction reason code |
|---|---|
pii-detector | pii.detected |
dlp-filter | dlp.redact |
hipaa-phi-detector | hipaa_phi.detected |
student-privacy | student_privacy.redact |
case-privacy | case_privacy.detected |
dual-use-filter | dual_use.triggered |
When the chain has pii-detector, the result names pii-detector. Otherwise, the result names the first policy of the chain in this sequence: hipaa-phi-detector, dlp-filter, student-privacy, case-privacy, dual-use-filter. The decision reason code is redact.applied when no other policy blocks or escalates the request.
Request results keep the reason code of each policy, for example pii.ssn_detected, phi.mrn_detected, or case_privacy.detected.
Limits and failure behavior
- The detectors are regular expressions and validators, not a model. Numbers such as order IDs can match by mistake, and unusual formats can be missed.
- The reason code names only the first match. Use
detection_countand the redaction metadata for the rest. - A
detect_patternsentry with invalid syntax is silently ignored. Lint the pack and test each pattern withverdictan policy test. - A
detect_patternsmatch is alwaysgeneric_idwith the label[REDACTED:ID]. Named capture groups do not create new types. marker_format: partialkeeps the last four digits of an SSN or card number and the first character of an email address. Other types get thelabelmarker.- With
action: block, output redaction still runs on the model response. - When
hipaa-phi-detectoris also in the chain, request and response redaction usehealthcare_mode: trueandpci_mode: false. This applies to streamed and buffered responses. - With
stream: true, the client receives no tokens until the provider has finished, because the gateway redacts the whole text first. - The request check runs for every listed request family. Response redaction runs for Chat Completions, Responses, and Messages.
Next steps
- DLP Filter: secrets and confidential patterns that are not personal identifiers.
- HIPAA PHI Detector: the HIPAA identifier set as its own control.
- Data Routing Policy: keep regulated data on approved provider targets.