Open-source PII firewall for LLM apps. Detect, anonymize and rehydrate sensitive data before it reaches OpenAI, Anthropic or any LLM provider.
10
stars
45
commits
Python
primary language
Aug 29, 2026
updated
Stop leaking PII into LLM APIs without breaking conversation context.
PII Firewall intercepts sensitive data before it reaches OpenAI, Anthropic, or any model provider, then restores it transparently in the response.
User text ──► [ PII Firewall ] ──► Sanitized prompt ──► LLM
│ │
Secure vault Model response
│ │
└────────── Re-hydrated reply ◄─────────┘
Standard redaction breaks context — replacing "John" with [REDACTED] means the model can no longer refer to the person by name. PII Firewall uses a stateful Detect → Anonymize → Rehydrate flow so the LLM sees pseudonyms while the user gets real answers.
pip install pii-firewall
from privacy_firewall import create_firewall
firewall = create_firewall("healthcare", detector_backend="regex")
result = firewall.secure_call(
text="Patient John Doe, SSN 123-45-6789, diagnosed with hypertension.",
context={
"tenant_id": "hospital-001",
"case_id": "patient-123",
"thread_id": "consultation-1",
"actor_id": "doctor-456",
},
llm_client=lambda prompt: f"Acknowledged for {prompt[:30]}..."
)
print(result.sanitized_text) # Patient PERSON_1, SSN_REDACTED, diagnosed with hypertension.
print(result.final_text) # Patient John Doe, SSN 123-45-6789, diagnosed with hypertension.
Note: The package is published as
pii-firewallon PyPI; the import namespace isprivacy_firewall.
| Problem | How PII Firewall handles it |
|---|---|
[REDACTED] breaks LLM context | Reversible pseudonyms (PERSON_1, EMAIL_1) keep context intact |
| Different PII needs different treatment | 6 disposition actions: pseudonymize, mask, hash, generalize, redact, keep |
| Spanish/French/German/etc. IDs | Locale-specific patterns for 55+ languages |
| Hard to audit what left your system | Vault + GDPR right-to-forget + TTL |
| Streaming chat needs real-time protection | Rehydrates tokens on the fly |
pip install "pii-firewall[presidio,langdetect]"
python -m spacy download en_core_web_sm
from privacy_firewall import create_firewall
firewall = create_firewall("finance", detector_backend="presidio")
result = firewall.secure_call(
text="Card ending 4242 belongs to john@example.com.",
context={"tenant_id": "acme", "case_id": "c1", "thread_id": "t1", "actor_id": "u1"},
llm_client=lambda prompt: f"Processed: {prompt}"
)
print(result.sanitized_text) # Card ending MASKED_4242 belongs to EMAIL_1.
print(result.final_text) # Card ending 4242 belongs to john@example.com.
| Profile | Behaviour |
|---|---|
healthcare | Pseudonymizes patient identifiers; keeps diagnoses, medications, procedures |
finance | Masks card numbers; pseudonymizes account numbers and IBANs; keeps amounts |
legal | High anonymity; pseudonymizes party names; generalizes dates to month/year |
generic | Balanced defaults for any use case |
A patient asks: "I'm John Doe, born 1985-03-12, my SSN is 123-45-6789."
The LLM receives: "I'm PERSON_1, born YEAR_1980_1989, my SSN is REDACTED."
The response is rehydrated before the user sees it.
A support ticket contains IBANs, card numbers, and email addresses. The LLM summarizes the issue without ever seeing real account data.
Party names, dates, and locations are pseudonymized so teams can run contract analysis through LLMs without exposing client data.
| Backend | Install extra | Best for | Latency |
|---|---|---|---|
regex | (none) | Structured IDs, emails, phones — zero dependencies | < 1 ms |
presidio | [presidio,langdetect] | Named entities — recommended default | 50–200 ms |
hybrid | [presidio,langdetect] | Regex + Presidio for maximum coverage | 50–250 ms |
gliner | [gliner] | Zero-shot NER, no fine-tuning needed | 100–400 ms |
transformers | [transformers] | Domain-specific models (biomedical, legal) | 100–500 ms |
opf | [opf] | OpenAI Privacy Filter — token-level classifier | 50–200 ms |
nemotron | [opf] | NVIDIA Nemotron fine-tune on OPF | 100–300 ms |
Apache 2.0. See LICENSE.
43 commits
2 commits
Python
74.6%
HTML
13.1%
TypeScript
10.8%
Open-source PII firewall for LLM apps. Detect, anonymize and rehydrate sensitive data before it reaches OpenAI, Anthropic or any LLM provider.
10
stars
45
commits
Python
primary language
Aug 29, 2026
updated
Stop leaking PII into LLM APIs without breaking conversation context.
PII Firewall intercepts sensitive data before it reaches OpenAI, Anthropic, or any model provider, then restores it transparently in the response.
User text ──► [ PII Firewall ] ──► Sanitized prompt ──► LLM
│ │
Secure vault Model response
│ │
└────────── Re-hydrated reply ◄─────────┘
Standard redaction breaks context — replacing "John" with [REDACTED] means the model can no longer refer to the person by name. PII Firewall uses a stateful Detect → Anonymize → Rehydrate flow so the LLM sees pseudonyms while the user gets real answers.
pip install pii-firewall
from privacy_firewall import create_firewall
firewall = create_firewall("healthcare", detector_backend="regex")
result = firewall.secure_call(
text="Patient John Doe, SSN 123-45-6789, diagnosed with hypertension.",
context={
"tenant_id": "hospital-001",
"case_id": "patient-123",
"thread_id": "consultation-1",
"actor_id": "doctor-456",
},
llm_client=lambda prompt: f"Acknowledged for {prompt[:30]}..."
)
print(result.sanitized_text) # Patient PERSON_1, SSN_REDACTED, diagnosed with hypertension.
print(result.final_text) # Patient John Doe, SSN 123-45-6789, diagnosed with hypertension.
Note: The package is published as
pii-firewallon PyPI; the import namespace isprivacy_firewall.
| Problem | How PII Firewall handles it |
|---|---|
[REDACTED] breaks LLM context | Reversible pseudonyms (PERSON_1, EMAIL_1) keep context intact |
| Different PII needs different treatment | 6 disposition actions: pseudonymize, mask, hash, generalize, redact, keep |
| Spanish/French/German/etc. IDs | Locale-specific patterns for 55+ languages |
| Hard to audit what left your system | Vault + GDPR right-to-forget + TTL |
| Streaming chat needs real-time protection | Rehydrates tokens on the fly |
pip install "pii-firewall[presidio,langdetect]"
python -m spacy download en_core_web_sm
from privacy_firewall import create_firewall
firewall = create_firewall("finance", detector_backend="presidio")
result = firewall.secure_call(
text="Card ending 4242 belongs to john@example.com.",
context={"tenant_id": "acme", "case_id": "c1", "thread_id": "t1", "actor_id": "u1"},
llm_client=lambda prompt: f"Processed: {prompt}"
)
print(result.sanitized_text) # Card ending MASKED_4242 belongs to EMAIL_1.
print(result.final_text) # Card ending 4242 belongs to john@example.com.
| Profile | Behaviour |
|---|---|
healthcare | Pseudonymizes patient identifiers; keeps diagnoses, medications, procedures |
finance | Masks card numbers; pseudonymizes account numbers and IBANs; keeps amounts |
legal | High anonymity; pseudonymizes party names; generalizes dates to month/year |
generic | Balanced defaults for any use case |
A patient asks: "I'm John Doe, born 1985-03-12, my SSN is 123-45-6789."
The LLM receives: "I'm PERSON_1, born YEAR_1980_1989, my SSN is REDACTED."
The response is rehydrated before the user sees it.
A support ticket contains IBANs, card numbers, and email addresses. The LLM summarizes the issue without ever seeing real account data.
Party names, dates, and locations are pseudonymized so teams can run contract analysis through LLMs without exposing client data.
| Backend | Install extra | Best for | Latency |
|---|---|---|---|
regex | (none) | Structured IDs, emails, phones — zero dependencies | < 1 ms |
presidio | [presidio,langdetect] | Named entities — recommended default | 50–200 ms |
hybrid | [presidio,langdetect] | Regex + Presidio for maximum coverage | 50–250 ms |
gliner | [gliner] | Zero-shot NER, no fine-tuning needed | 100–400 ms |
transformers | [transformers] | Domain-specific models (biomedical, legal) | 100–500 ms |
opf | [opf] | OpenAI Privacy Filter — token-level classifier | 50–200 ms |
nemotron | [opf] | NVIDIA Nemotron fine-tune on OPF | 100–300 ms |
Apache 2.0. See LICENSE.
43 commits
2 commits
Python
74.6%
HTML
13.1%
TypeScript
10.8%