Skip to main content

LLM-Shield-Proxy

Build Status PyPI: llm-shield-proxy PyPI: pii-leak-benchmark License Python Docs & Playground

This repository is two things, and the first one matters more:

  1. pii-leak-benchmark - a neutral harness that measures whether any OpenAI-compatible streaming gateway sends raw personal data to its upstream, and whether it gives the values back to the client.
  2. LLM-Shield-Proxy - a streaming privacy gateway. It is one of the things the benchmark measures, labelled in the table as the reference implementation, and its row carries the same unreplicated caveat as everyone else's.

The first result exposed a biased fixture

All current comparison rows come from the initial project-run measurement set, not independent reproductions. Each row is therefore labelled unreplicated and published with its configuration and raw report for other people to check.

The prompt used to carry three fixed values, chosen to be safe to publish: person@example.invalid, 123-45-6789, 4532-1234-5678-9012. Every one is a value a validating detector is built to reject: .invalid has no public suffix, that SSN is a blacklisted sequence, and the card fails its Luhn checksum (sum 68). Measured against a pinned Presidio at score_threshold: 0.0, stock Presidio returned no EMAIL_ADDRESS, no US_SSN and no CREDIT_CARD for any of them.

The reference implementation used regexes without those validation checks, so it caught all three. The fixture therefore favored shape matching over validated detection. A run against LiteLLM + Presidio reported leaked: ["SSN"] on the shipped fixture and leaked: [] on the same run with valid specimens. That row was withheld rather than published, the fixture was replaced, and every row was re-run. Full measurement: fixture threat model.

The benchmark also exposed two defects in this proxy's streaming hot path: an OpenTelemetry span opened per SSE delta even with export disabled, and the data line and its terminating blank line yielded as two separate ASGI writes. Both are fixed and pinned by tests/test_streaming_write_efficiency.py. No speed multiplier is published for that fix: the original runner and its raw samples were not retained, so there is no auditable evidence to cite. See the record.

Known limitation: a roughly 35-line str.replace shim with no general detector can pass all five checks. The formats remain fixed because broader format randomization produced false leak findings in two of six variants against a correctly redacting gateway. Values now vary within those formats, and submitted CI reports can carry detached provenance over the finished JSON bytes.

Run it yourself, in about a minute

pip install pii-leak-benchmark

# The negative control: no gateway at all, raw pass-through. MUST report outcome=fail.
pii-leak-benchmark \
  --target-base-url capture://self \
  --target-name raw-pass-through-negative-control --target-version 1 \
  --redaction-claimed claimed \
  --redaction-claim-citation https://github.com/ninadphalak/LLM-Shield-Proxy/blob/main/website/docs/conformance/reproducing.md \
  --redaction-enabled \
  --redaction-config-reference "synthetic control: declared redaction intentionally absent"

# Your gateway, already configured to send upstream traffic to http://127.0.0.1:8765/v1
pii-leak-benchmark --target-base-url http://127.0.0.1:4000/v1 --target-name your-gateway

Standard library plus httpx - you should not have to install one gateway to measure another. The harness stands a capture server in front of the gateway's configured upstream and inspects every channel it could arrive through: request line, method, headers, chunk extensions, trailers and the decoded JSON body. Anything it cannot inspect fails closed. Ten adversarial rounds are recorded in the conformance docs; the rule that survived them is enumerate the channel, not the encoding.

A measurement is not a verdict. fail means one thing only: protected data reached the capture. A gateway that never claimed to redact anything, or that anonymizes one-way and leaks nothing, gets a non-verdict outcome instead. This avoids calling a product a privacy failure for a capability it never claimed to provide.

Results

Target Outcome Runs / distinct submitters
Raw capture endpoint (control) fail - three literal matches 1 / 1 - control, not a product
LLM-Shield-Proxy (reference implementation) pass - 5/5 1 / 1 - unreplicated
LiteLLM 1.99.0, default redaction-not-enabled 1 / 1 - unreplicated
LiteLLM 1.99.0 + Presidio no-leak-profile-not-met (no leak) 1 / 1 - unreplicated
Portkey OSS 1.15.2, default redaction-not-enabled 1 / 1 - unreplicated
Portkey OSS 1.15.2 + regexReplace no-leak-profile-not-met (no leak) 1 / 1 - unreplicated

Every row is unreplicated. A gateway needs 3 runs from 3 distinct submitters before its result is treated as replicated. The current rows are project-run measurements, not independent reproductions. Each one includes the pinned configuration and raw artifact so others can verify it or report a different result. Full table, method and evidence · submit a run.

The reference implementation

pip install llm-shield-proxy
llm-shield-proxy --host 0.0.0.0 --port 8000
curl http://localhost:8000/healthz

For the container path:

docker compose up -d
curl http://localhost:8000/healthz
python examples/demo.py
Terminal demonstration of LLM-Shield-Proxy masking and streaming rehydration

LLM-Shield-Proxy is a self-hosted privacy gateway for OpenAI-compatible streaming APIs. It applies configured PII, PHI, PCI and secret transformations before the upstream, then rehydrates the masked values incrementally as SSE events arrive. Point an existing client at it by changing base_url:

from openai import OpenAI

client = OpenAI(api_key="your-shield-virtual-key", base_url="http://localhost:8000/v1")
stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Contact Sarah at sarah@example.com."}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")

A real completion needs an upstream key and a client-auth configuration. Copy .env.example and follow the deployment guide rather than treating the health check as a production validation.

How the reference implementation works

Inbound, the proxy detects configured sensitive values and replaces them before the request crosses the upstream boundary. Outbound, a bounded prefix-aware buffer reconstructs placeholders split across SSE chunks and restores registered values as the stream continues. Structured JSON payloads take a separate syntax-preserving mutation path and still require provider/tool schema testing.

LLM privacy proxy dual-pipeline redaction architecture

The maintained component map and deployment diagrams live in the architecture guide, architecture whitepaper, and deployment guide.

Area What is implemented Where the evidence stops
Detection 10 native Tier 1 patterns, Tier 2 Shannon entropy, optional Tier 3 ONNX NER, BYOR rules Supported types · no recall guarantee on unlabeled traffic
Streaming privacy Sliding-window SSE rehydration, bounded streaming JSON lexer Architecture · conformance method
Masking Synthetic, structural-tag, scrub, operator-keyed stateless crypto Masking guide · plaintext still exists in process memory
Security controls SSRF/DNS-rebinding egress checks, request policy, rate and blast-radius limits, canary tripwires Security · not a substitute for network policy
Evidence plane Hash-linked audit records, Ed25519 receipts, OSCAL output, compliance packs Compliance overview · tamper-evident, not WORM without immutable retention
MCP governance Scoped JSON-RPC subset with RBAC and egress policy Research-scoped; MCP guide · not a complete MCP transport

Deployment choices

Standard mode keeps detection, masking, policy and rehydration inside the operator-controlled gateway, then sends only the transformed request to the selected external provider:

Standard LLM privacy gateway deployment

Air-gapped mode instead sends transformed traffic to an operator-controlled internal model gateway. Network policy must still prevent bypass, telemetry and other unintended egress:

Air-gapped LLM egress gateway deployment

See deployment topologies, air-gapped egress, and the Kubernetes/Helm deployment guide.

Every catalogued feature carries a Supported / Beta / Experimental / Research badge naming its verification boundary: feature catalog · stability policy · limitations.

It supports SOC 2, HIPAA, GDPR, EU AI Act and NIST/ISO evidence programs by supplying technical controls and artifacts. It does not certify a deployment, guarantee complete detection, or make network policy optional.

Verifying this repository

git clone https://github.com/ninadphalak/LLM-Shield-Proxy.git
cd LLM-Shield-Proxy
python -m pip install -e ./pii-leak-benchmark -e ".[dev]"
python -m pytest

The benchmark is a separate distribution in this repo, so it installs first; nothing in it imports the proxy and a test fails if that ever changes. CI provisions real Redis, an HTTP/2 ALPN server, a checksum-pinned ONNX export, Docker, Helm and promtool. A missing dependency fails those jobs rather than skipping them, so a green build cannot mean "nothing ran".

Documentation

Contributing, license, and citation

Contributions are welcome through issues, discussions and CONTRIBUTING.md. The most valuable contribution is an independent benchmark run against a gateway you operate, whether it matches or differs from a row above.

Source code is Apache 2.0; documentation and diagrams may carry CC BY 4.0 terms. See LICENSE.

The author identifies U.S. application numbers 64/126,730 and 64/139,263 as pending filings related to streaming transformation and structured stateless masking. Pending applications are not issued patents; verify status with counsel and official records before relying on them.

If you reference the architecture or benchmark methodology, use CITATION.cff or:

Phalak, N. (2026). Quantifying Latency and Token Overhead in Real-Time LLM Stream Sanitization: A Tiered Detection Approach. https://doi.org/10.5281/zenodo.21955770

Release files for llm-shield-proxy 1.3.5

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

Source distribution (sdist)

Source distribution for llm-shield-proxy 1.3.5
File Size Uploaded
llm_shield_proxy-1.3.5.tar.gz 241.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for llm-shield-proxy 1.3.5
File Interpreter ABI Platform
llm_shield_proxy-1.3.5-py3-none-any.whl Python 3 none any Details

Total release size:398.3 kB

Release files / llm_shield_proxy-1.3.5.tar.gz

Download URL llm_shield_proxy-1.3.5.tar.gz
Size 241.9 kB
Tags Source
SHA-256 checksum
How to use checksums
d17f30d2cc7ff4fcf0bd6eab9b54767c978cbda1b2ed680b4fbde6d4f8f5235c
BLAKE2b-256 checksum
How to use checksums
590fc017e93145c013a65d5ccffda4713d0be76921e35975c2a968c8bda78aff
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release files / llm_shield_proxy-1.3.5-py3-none-any.whl

Download URL llm_shield_proxy-1.3.5-py3-none-any.whl
Size 156.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8033a27ff7cbacf535d34f9bdec9fa3349749893740ee5cea09ba6fce529a1ed
BLAKE2b-256 checksum
How to use checksums
871ee52e93166b41da5d6c381c735d0def235a66ea6abaade4d852847c13ace6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.7

Release history Release notifications | RSS feed

1.6.6

2 release files

1.6.5

2 release files

1.6.4

2 release files

1.6.3

2 release files

1.6.2

2 release files

1.6.1

2 release files

1.6.0

2 release files

1.5.2

2 release files

1.5.1

2 release files

1.4.0

2 release files

1.3.7

2 release files

1.3.6

2 release files

This release

1.3.5 This release

2 release files

1.3.4

2 release files

1.3.3

2 release files

1.3.2

2 release files

1.3.1

2 release files

1.3.0

2 release files

1.2.14

2 release files

1.2.13

2 release files

1.2.12

2 release files

1.2.11

2 release files

1.2.9

2 release files

1.2.7

2 release files

1.2.5

2 release files

1.0.20

2 release files

1.0.19

2 release files

1.0.18

2 release files

1.0.17

2 release files

1.0.16

2 release files

1.0.15

2 release files

1.0.14

2 release files

1.0.12

2 release files

1.0.10

2 release files

1.0.9

2 release files

1.0.8

2 release files

1.0.7

2 release files

1.0.6

2 release files

1.0.5

2 release files

1.0.4

2 release files

1.0.3

2 release files

1.0.2

2 release files

1.0.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