Local DLP reverse proxy for LLM traffic
Project description
VeilLLM
A local DLP reverse proxy for LLM traffic. Mask PII, API keys, and secrets before they leave your machine — restore them on the way back.
https://github.com/user-attachments/assets/dec6fb5d-64d2-4731-a4b3-40260ff8f461
Quickstart
# Install (pipx recommended — isolated env, global CLI)
pipx install veil-llm
# Start the gateway
veil-llm serve --port 4000
# Show resolved settings
veil-llm config
pip install veil-llmalso works if you don't use pipx.
On first run, VeilLLM creates a ./config.toml with a random salt and default settings. The spaCy model (en_core_web_lg, ~500MB) downloads automatically if missing.
CLI reference
veil-llm serve [options]
veil-llm config # show resolved settings
veil-llm --help # full help
| Flag | Env var | Default | Description |
|---|---|---|---|
--port |
GATEWAY_PORT |
4000 |
Listen port |
--host |
— | 127.0.0.1 |
Listen host |
--upstream-url |
GATEWAY_UPSTREAM_URL |
https://openrouter.ai/api/v1 |
Upstream API base URL |
--model-name |
GATEWAY_MODEL_NAME |
en_core_web_lg |
spaCy model for PII detection |
--debug |
GATEWAY_DEBUG=1 |
false |
Log placeholder→original map to stderr (⚠️ leaks secrets) |
--high-entropy |
GATEWAY_HIGH_ENTROPY=1 |
false |
Enable high-entropy string detection |
--entropy-threshold |
GATEWAY_ENTROPY_THRESHOLD |
4.5 |
Shannon entropy threshold for high-entropy detection |
--enabled-entities |
GATEWAY_ENABLED_ENTITIES |
PERSON,EMAIL_ADDRESS,… |
Comma-separated list of PII entity types to detect |
Priority: config.toml defaults < config.toml values < env vars < CLI flags.
Available spaCy models
| Model | Size | Vectors | Accuracy | Best for |
|---|---|---|---|---|
en_core_web_sm |
12 MB | ❌ | Low | Minimal footprint, quick tests |
en_core_web_md |
40 MB | ✅ | Medium | Balanced — smaller than lg, better NER than sm |
en_core_web_lg |
560 MB | ✅ | High | Best CPU accuracy (default) |
en_core_web_trf |
460 MB | ✅ | Highest | Transformer-based; slowest, needs GPU for practical use |
Use --model-name or config to switch:
veil-llm serve --model-name en_core_web_md
Usage with any agentic harness
VeilLLM speaks the OpenAI-compatible API. Any harness that can point to a custom base URL — Hermes, Cline, Continue, Aider, OpenHands, or your own scripts — works with one config change:
# Before: harness hits provider directly
base_url: https://api.openai.com/v1
# After: harness hits VeilLLM, which masks then forwards
base_url: http://127.0.0.1:4000/v1
Your Authorization header is forwarded through unchanged. VeilLLM masks sensitive data in your prompts and restores it in responses transparently — the harness never knows it's there.
Important: VeilLLM v0.1 does not support streaming (
stream: true). Configure your harness for non-streaming mode.
Configuration
VeilLLM auto-creates ./config.toml on first run with sensible defaults:
[gateway]
upstream_url = "https://openrouter.ai/api/v1"
port = 4000
salt = "<auto-generated>"
model_name = "en_core_web_lg"
[detection]
enabled_entities = [
"PERSON",
"EMAIL_ADDRESS",
"PHONE_NUMBER",
"CREDIT_CARD",
"IBAN_CODE",
"IP_ADDRESS",
"LOCATION",
"US_SSN",
]
[recognizers]
high_entropy_enabled = false
entropy_threshold = 4.5
Every setting can be overridden via environment variable or CLI flag (see table above). Run veil-llm config to see the resolved settings your gateway will use.
What gets masked
| Category | Examples |
|---|---|
| PII (Presidio) | Names, emails, phone numbers, credit cards, IBANs, IPs, locations, SSNs |
| LLM API keys | sk-..., sk-ant-..., sk-or-... |
| AWS keys | AKIA... access keys and secret keys |
| GitHub tokens | ghp_..., gho_..., github_pat_... |
| Google API keys | AIza... |
| JWTs | eyJ... three-segment tokens |
| Private keys | PEM -----BEGIN ... PRIVATE KEY----- blocks |
| High-entropy strings | Opt-in: strings with Shannon entropy > threshold |
Gradio playground
For a hands-on feel, launch the Gradio UI to see masking live — no upstream API key needed for the detection tab:
uv run --with gradio --with python-dotenv python playground.py
Open http://127.0.0.1:7860. The PII Detection tab lets you paste text and see exactly what gets masked. The Chat tab sends masked prompts to an LLM and shows a transparency log of every entity detected, masked, and restored — the same thing the proxy does silently.
playground.py is a self-contained demo; read it alongside this README to understand the full flow.
How it works
VeilLLM replaces detected values with deterministic placeholders:
"Jane" → <PERSON_a3f2>
"jane@example.com" → <EMAIL_9c1d4e>
"sk-abc123..." → <API_KEY_77b0af>
The same value always maps to the same placeholder (salted hash), so agent conversation history stays coherent across turns. The salt is a random value in ./config.toml — never sent over the network.
On the response, placeholders are restored using tolerant regex matching (handles LLM-mangled casing, spaces, and punctuation).
Endpoints
| Endpoint | Behavior |
|---|---|
POST /v1/chat/completions |
Mask → forward → unmask |
GET /v1/models |
Pass-through |
GET /healthz |
Liveness check |
Any other /v1/* |
Pass-through (no masking, warning logged) |
Telemetry
Per-request log line (stderr):
[req a3f2bc8d] masked 3 entities: API_KEY×1, EMAIL_ADDRESS×1, PERSON×1 | detect 35ms | upstream 1850ms | unmask 2ms
Set GATEWAY_DEBUG=1 to see the placeholder→original map (with a prominent warning).
Docker
docker build -t veilllm .
docker run -p 4000:4000 -v ./config.toml:/app/config.toml veilllm
Limitations (v0.1)
- No streaming (SSE) — returns 400 if
stream: true - English-only PII detection
- Single upstream URL (defaults to OpenRouter; any OpenAI-compatible API works via
GATEWAY_UPSTREAM_URLor config.toml) - Not a full proxy — only the
/v1/*paths are forwarded
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file veil_llm-0.1.3.tar.gz.
File metadata
- Download URL: veil_llm-0.1.3.tar.gz
- Upload date:
- Size: 4.7 MB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
bc59c2ecec7f96734bbf4dbca34faaac779d0fabd6fff8d1a0b2684aee7ae204
|
|
| MD5 |
b86e0d43c750f53ae5de87bf73ed7572
|
|
| BLAKE2b-256 |
b0c1991cc45bb9ac8bfcf2d84cdf62e63d8771235e85732820976dc817a9b6cd
|
Provenance
The following attestation bundles were made for veil_llm-0.1.3.tar.gz:
Publisher:
publish.yml on h-f-fares/VeilLLM
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
veil_llm-0.1.3.tar.gz -
Subject digest:
bc59c2ecec7f96734bbf4dbca34faaac779d0fabd6fff8d1a0b2684aee7ae204 - Sigstore transparency entry: 2314238309
- Sigstore integration time:
-
Permalink:
h-f-fares/VeilLLM@6c2c213d02b81215f48e001782375bf000747c57 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/h-f-fares
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6c2c213d02b81215f48e001782375bf000747c57 -
Trigger Event:
push
-
Statement type:
File details
Details for the file veil_llm-0.1.3-py3-none-any.whl.
File metadata
- Download URL: veil_llm-0.1.3-py3-none-any.whl
- Upload date:
- Size: 18.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e034b5b6d80f3a5d246bece1debdc9ce8eadc44256a21f36dda1b23421c81e2b
|
|
| MD5 |
c6ee853e3c65f55bec61ff503c45c41e
|
|
| BLAKE2b-256 |
1284d190a618fb37a3ab28416ae78032a4c163c109fd3853ec044d9cb9f28d77
|
Provenance
The following attestation bundles were made for veil_llm-0.1.3-py3-none-any.whl:
Publisher:
publish.yml on h-f-fares/VeilLLM
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
veil_llm-0.1.3-py3-none-any.whl -
Subject digest:
e034b5b6d80f3a5d246bece1debdc9ce8eadc44256a21f36dda1b23421c81e2b - Sigstore transparency entry: 2314238337
- Sigstore integration time:
-
Permalink:
h-f-fares/VeilLLM@6c2c213d02b81215f48e001782375bf000747c57 -
Branch / Tag:
refs/tags/v0.1.3 - Owner: https://github.com/h-f-fares
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@6c2c213d02b81215f48e001782375bf000747c57 -
Trigger Event:
push
-
Statement type: