Skip to main content

Universal PII Firewall (UPF)

PyPI version Python versions CI License

A privacy firewall that strips PII from text and documents before they reach any LLM or external API — running entirely on your machine, with no network calls, no API keys, and no cloud dependencies.

When you send a document to an LLM, you risk leaking names, ID numbers, financial data, and contact details. Traditional regex tools miss multilingual PII, OCR noise, and content embedded in images. UPF sits between your data and any downstream service, intercepts sensitive content locally, and hands back a clean payload.

The Problem It Solves

Developers integrating LLMs into document workflows face a hard compliance problem: the moment a user's data leaves the system boundary, GDPR, HIPAA, and similar obligations apply to the receiving service. Most LLM providers process inputs on remote infrastructure. UPF is the gate that ensures PII never reaches that boundary in the first place.

  • Intercepts names, IDs, financial data, and contact info before any API call
  • Works on raw text, OCR output, and scanned image files
  • Handles multilingual input (EN, ES, PL, PT, PT-BR, FR, DE, NL, IT) and OCR noise
  • No LLM or cloud API required — all detection runs locally with deterministic rules and optional local ML

How It Works

[Your document / image]
        ↓
  OCR (optional, Tesseract — only if no text provided)
        ↓
  Multi-layer PII detection
  ├── Deterministic  — IBAN, credit card, national ID checksums
  ├── Regex + context — emails, phones, addresses, secrets, URLs
  ├── Multilingual keywords — name/address cues in 9 languages
  └── Optional ML NER — spaCy (local model, no API)
        ↓
  Redaction engine
  ├── Text  — label / mask / partial / pseudonym / skeleton
  └── Image — black-box overlay, face blur, signature blur
        ↓
  [Safe payload — ready for LLM, storage, or downstream service]

No data leaves your system at any stage. The core package has zero required dependencies.

Install

Core package:

pip install universal-pii-firewall

Optional extras:

# OCR/image text extraction + image redaction
pip install "universal-pii-firewall[image]"

# Optional face blur for image pipeline
pip install "universal-pii-firewall[face]"

# Optional ML NER layer
pip install "universal-pii-firewall[ml]"

# Dev and release tooling
pip install "universal-pii-firewall[dev]"

Quick Start

Text sanitization

from upf import sanitize_text

text = "Alice Smith paid with 4111-1111-1111-1111 and emailed alice@example.com"
print(sanitize_text(text))
# -> [REDACTED:NAME] paid with [REDACTED:CREDIT_CARD] and emailed [REDACTED:EMAIL]

For a long-form, realistic before/after example (with risk score and detected entities), run examples/text_example.py --mode detailed.

OCR text sanitization

from upf import sanitize_image

ocr_text = "John Doe IBAN DE89370400440532013000"
print(sanitize_image(ocr_text))

Image bytes sanitization

from upf import sanitize_image_bytes

with open("examples/inputs/1.png", "rb") as f:
    image_bytes = f.read()

result = sanitize_image_bytes(
    image_bytes,
    ocr_text="John Doe paid with 4111 1111 1111 1111 and email john@example.com",
)
print(result.sanitized_text)
print(result.risk_score, result.risk_level)

Optional face blur

from upf import UPFConfig, sanitize_image_bytes

cfg = UPFConfig(blur_faces=True, face_blur_strength=31)
with open("examples/inputs/2.png", "rb") as f:
    result = sanitize_image_bytes(
        f.read(),
        ocr_text="Alice Smith alice@example.com",
        config=cfg,
    )

Optional signature blur

Detects handwritten signature regions via contour heuristics and blurs them. Requires the face extra (OpenCV).

from upf import UPFConfig, sanitize_image_bytes

cfg = UPFConfig(blur_signatures=True, signature_blur_strength=31)
with open("examples/inputs/1.png", "rb") as f:
    result = sanitize_image_bytes(
        f.read(),
        ocr_text="Signed by John Doe on 2026-03-06",
        config=cfg,
    )

Both face and signature blur can be combined:

cfg = UPFConfig(
    blur_faces=True,
    face_blur_strength=51,
    blur_signatures=True,
    signature_blur_strength=31,
)

Enable via environment variables in image_example.py --mode detailed:

UPF_BLUR_FACES=true UPF_BLUR_SIGNATURES=true uv run python examples/image_example.py --mode detailed

If you omit ocr_text, install the image extra and ensure Tesseract OCR is available on your system.

Configuration Knobs

All options with their defaults:

from upf import UPFConfig, RedactionMode

cfg = UPFConfig(
    # Detection toggles
    redact_names=True,
    redact_emails=True,
    redact_phones=True,
    redact_ibans=True,
    redact_credit_cards=True,
    redact_secrets=True,
    redact_addresses=True,
    redact_urls=True,
    redact_numeric_ids=True,
    redact_national_ids=True,   # NATIONAL_ID, PT_NISS, PT_NIF, BR_CPF
    redact_organizations=True,

    # Detector layers
    use_ml_ner=True,
    use_multilingual=True,
    use_relationship_detector=True,

    # Image behavior (requires [face] extra)
    blur_faces=False,
    face_blur_strength=51,
    blur_signatures=False,
    signature_blur_strength=23,

    # Redaction output format: label | mask | partial | pseudonym | skeleton
    redaction_mode=RedactionMode.LABEL,

    # Risk policy
    risk_mode="legacy",         # "legacy" | "threshold"
    low_threshold=0.40,
    high_threshold=0.75,
    block_high_risk=False,
)

Public API Reference

Stable exported interface from upf:

  • sanitize_text
  • sanitize_text_with_details
  • sanitize_image
  • sanitize_image_bytes
  • sanitize_image_base64
  • secure_llm_call
  • SecureLLMResult
  • UPFConfig
  • RedactionMode
  • PseudonymSession
  • HighRiskBlockedError
  • ImageSanitizeResult
  • TextSanitizeResult

Benchmark Methodology and Results

Text benchmark metrics below are from the included dataset and scripts:

  • Command: uv run python benchmarks/run_tests.py
  • Command: uv run python benchmarks/run_tests_strict.py
  • Interpreter: Python 3.11.14
  • Measurement date: March 6, 2026
  • Dataset size: 74 labeled text cases (text, multilingual, edge_cases)

Measured results:

Metric Value
Cases 74
Precision 0.9733
Recall 1.0000
Avg latency (ms) 0.2495
P95 latency (ms) 0.3505
Strict F1 0.9865

Language coverage in this dataset: EN, ES, PL, PT, PT-BR.

Image precision/recall benchmark is not published yet because labeled image sidecar cases are currently absent (benchmarks/run_image_tests.py reports 0 cases).

Showcase Gallery

Sample assets are included under examples/inputs/ and examples/outputs/.

Case 1

Input Redacted Results Panel
Input 1 Redacted 1 Results 1

Case 2

Input Redacted Results Panel
Input 2 Redacted 2 Results 2

Case 3

Input Redacted Results Panel
Input 3 Redacted 3 Results 3

Running Local Examples

From repository root:

uv run python examples/text_example.py --mode quick
uv run python examples/text_example.py --mode detailed
uv run python examples/image_example.py --mode quick
uv run python examples/image_example.py --mode detailed

image_example.py --mode detailed requires the image extra and image inputs in examples/inputs/.

Limitations

  • Image benchmark precision/recall is not yet formalized due to missing labeled sidecar cases.
  • OCR quality directly affects image-text extraction quality.
  • Optional ML detector ([ml]) depends on external model/runtime availability.

License

Apache License 2.0.

Metadata

Release files for universal-pii-firewall 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for universal-pii-firewall 0.2.0
File Size Uploaded
universal_pii_firewall-0.2.0.tar.gz 51.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for universal-pii-firewall 0.2.0
File Interpreter ABI Platform
universal_pii_firewall-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 114.6 kB

Release files / universal_pii_firewall-0.2.0.tar.gz

Download URL universal_pii_firewall-0.2.0.tar.gz
Size 51.4 kB
Tags Source
SHA-256 checksum
How to use checksums
ebccd0792e393e9f508213a27f9c5fa53df7cf05bea482c70505b18178bf2a0b
BLAKE2b-256 checksum
How to use checksums
89d6bab522c09cd3a7c4cec34ad153e0ee66edfc578828f476ee548f98455f65
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.14

Release files / universal_pii_firewall-0.2.0-py3-none-any.whl

Download URL universal_pii_firewall-0.2.0-py3-none-any.whl
Size 63.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fce014dd594f846054fe05c37848062568970b6470188dbb5cf342f72367ba1b
BLAKE2b-256 checksum
How to use checksums
b506a9473bf7713a913c42e7318dd85863f197ba47de091921b644de3b5d414e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.11.14

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page