When sensitive personal or financial data is sent to a language model, the enterprise loses control over where it is stored, how it is retained, and whether it will be reproduced. SAFi can now detect and block sensitive identifiers in both directions: in a user’s prompt before it is sent to a model, and in an agent’s draft response before it is delivered back to the user.
These checks are executed in deterministic code with no model involved. They are off by default, and an organization can enable them one identifier at a time.
Here is exactly what SAFi detects, how the interception works, and where the boundaries lie.
What is detected
Four checks ship with this update. Each pairs a structural format with a validation step. Three of the four are confirmed by a checksum.
1. Payment card numbers
Thirteen to nineteen digits that satisfy the Luhn algorithm. Separators are ignored.
Detected:
4111111111111111
4111 1111 1111 1111
4111-1111-1111-1111
5500005555555559
378282246310005Not detected:
4111111111111112 one digit changed, fails Luhn
1234567890123456 right length, fails Luhn2. International bank account numbers (IBAN)
Two letters and two check digits, followed by the account identifier. Validated by the ISO 13616 mod-97 rule, where the rearranged value must equal 1 modulo 97.
detected GB82WEST12345698765432
not detected GB82WEST12345698765433 checksum mismatch3. Bank routing numbers (ABA)
Nine digits validated by the 3-7-1 weighted checksum.
detected 021000021
not detected 021000022 checksum mismatch4. US social security numbers
Unlike the others, SSNs carry no checksum. These are validated on formatting and the Social Security Administration’s allocation rules.
Detected:
123-45-6789Not detected:
123456789 unformatted, see limitations below
000-45-6789 area 000 is never issued
666-45-6789 area 666 is never issued
900-45-6789 areas 900 to 999 are never issued
123-00-6789 group 00 is never issued
123-45-0000 serial 0000 is never issuedHow the checks work: code, not models
A regular expression finds the shape of the identifier, and a checksum or a set of allocation rules confirms it. Both steps are written in plain Python. No language model is called at any point in the detection path.
This is a deliberate architectural choice, not a performance optimization. Whether a card number satisfies Luhn is arithmetic, and arithmetic has a single correct answer that anyone auditing the system can recompute. Ask a language model the same question and it produces an answer that is usually right, occasionally not, carries no stable threshold, and cannot be independently verified after the fact.
For a control whose value rests on being verifiable, a probabilistic model is the wrong tier. Decisions that reduce to a rule belong in deterministic code.
Where the interception happens
Inbound (Phase Zero). The check runs before the model is called. A user message containing a detected identifier is refused, and the value is never sent to the model to reason about. The user receives an explanation and an invitation to resend without the restricted data.
Outbound (draft evaluation). The same checks run over the model’s draft response before it is delivered. An agent with tool access can retrieve an internal document, find an account number inside it, and reproduce that number in its answer. Nobody typed it and the model produced it on its own, so an inbound-only control would miss it entirely.
The audit record
When a turn is blocked, SAFi writes a governance record describing the decision. That record is retained for years under the organization’s retention policy, and it is hash-chained and tamper-evident.
The detected value is removed from it. It is replaced in full rather than masked, because a partially masked identifier is still an identifier. What remains is the entry showing which check fired:
{
"userPrompt": "when was our last commit: [REDACTED:ssn]",
"willReason": "pii_detected",
"will_stage": "phase_zero"
}The record shows that the turn was refused, at which stage, by which check, and what the user was told. It does not store the identifier.
Configuration
Every check is disabled until an organization turns it on. A deployment that never opens the settings panel sees no change in behaviour.
No custom patterns. Checks are enabled per identifier under Organization settings, in AI Standards. There is no field for entering a regular expression, and there will not be one. A caller-supplied pattern would execute on every turn inside the deterministic path, where there is no timeout. A subtly malformed one matches nothing, failing open while appearing to be enforced.
Organizational floor. What an organization enables acts as a floor. A business unit or policy can add further checks to its agents. It cannot remove one the organization set, because the two sets are combined and combining prohibitions can only tighten them.
User-level scope. The checks follow the acting user’s organization rather than the agent, so they apply on every agent that user interacts with.
Known limitations
Here is what this system does not do.
Unformatted SSNs are not matched. A bare nine-digit run such as 123456789 is ignored. Because SSNs lack a checksum, nothing distinguishes an unformatted one from an invoice number, a part number or a random string. Matching it would block ordinary business messages with no explanation the user could act on.
Routing numbers are the loosest check. The ABA test is a weighted mod-10 sum over nine digits, so roughly one in ten random nine-digit strings passes it by chance. It is accurate on real routing numbers and will produce occasional false positives on unrelated values.
Only these four identifier types are covered. Passport numbers, national IDs outside the US, medical record numbers, driver’s licence numbers and tax identifiers are not detected.
Detection is format-strict. An identifier spelled out in words, split across lines or obfuscated will not be matched. This is a pattern check, not semantic comprehension.
No contextual classification. SAFi identifies numbers that satisfy known formats and checksums. It does not determine whether a number belongs to a real person, or whether the surrounding context makes sharing it legitimate.
The value existed before the check ran. SAFi removes it from the model’s input and from the governance record. The string still arrived over the network, and it will be present in standard web-server logs or database message rows if your deployment retains them.
Check it yourself
In enterprise security you should not have to trust a vendor’s marketing. The behaviour of this loop is directly observable.
- Enable a check under Organization settings, in AI Standards.
- Send a message containing a restricted identifier.
- Watch the turn get refused before the model is called.
- Open the governance record for that turn and look for the value. The decision, the stage and the reason are there. The identifier is not.
SAFi is open source. The detectors, their checksums and their tests are in the repository.

