PrivAiTe
Your coding agent reads your .env files, logs and command output, and
everything it reads goes to the LLM provider.
PrivAiTe is a local proxy that sits between your agent or app and the model provider. It replaces the secrets and personal data in every request before it leaves your machine, then puts the personal data back in the reply. It reads inside tool-call arguments and tool results, not only the chat text. Nothing phones home.
| See it work | Get started | See the benchmark | GitHub |
# your agent reads and sends:
OPENAI_API_KEY=sk-demo-fake000
DB_PASSWORD=demo-pass-4821
ADMIN_EMAIL=marie@example.com
# the provider receives:
OPENAI_API_KEY=[SECRET]
DB_PASSWORD=[SECRET]
ADMIN_EMAIL=<EMAIL_ADDRESS_1>
That is real engine output with the shipped config, not a mock-up. In the reply,
<EMAIL_ADDRESS_1> becomes the real address again, streaming included.
[SECRET] does not come back: the shipped configs redact secrets and mask card
numbers for good
(entity overrides).
Detection runs locally and is best-effort, not a guarantee: the
threat model says what it does not cover.
See it work in one command, no API key
docker run --rm \
ghcr.io/crp4222/privaite:0.8.0 \
python -m privaite verify
It starts a throwaway provider on 127.0.0.1, sends the same agent-shaped request straight to it and then through PrivAiTe, and prints which planted values each request body contained. About 20 seconds on a laptop CPU once the image is pulled, and it exits non-zero if a value leaks. What it prints and how to read it.
The problem
People paste real customer names, emails, contracts, and API keys into chatbots and coding agents every day. In this project’s own agent benchmark, a coding agent asked to read a small repository put all 24 planted PII values and secrets on the provider’s wire. Once that text reaches a third-party model, you no longer control where it is stored, logged, or used.
Most existing tools only scrub the visible chat message. Modern traffic is not just chat. It is agents calling tools with your data in the arguments, documents and images attached to a prompt, and CLIs that read files and environment variables where secrets live. That is the gap PrivAiTe was built for.
How it works
- You point your OpenAI-compatible client at PrivAiTe instead of the provider.
- On the way out, it detects personal data and replaces each item with a
stand-in. A placeholder like
<PERSON_1>, or a realistic fake, your choice. The mapping stays in memory on your machine for the length of the request. - On the way back, it restores the real values in the response, including inside tool-call arguments, so your application receives what it expected.
The model only ever sees the stand-ins. You can prove it on your own machine in 90 seconds: a runnable demo starts a fake provider that records what it receives, then sends the same agent request without and with PrivAiTe. The engine is open source, so you can also read exactly what it sends.
What other tools miss
- Tool-call arguments. When an agent calls a function, the PII sits in the JSON arguments, not the chat text. Flat-text scrubbers leave it untouched. PrivAiTe scrubs it and restores it.
- Multimodal content. Text parts inside a multimodal message are handled too.
- Agent and CLI egress. The same engine protects the whole prompt path, not only a chat box.
- Zero telemetry. Detection runs on your machine. PrivAiTe does not call home, and there is no hosted dashboard collecting your traffic.
- Reversible by design, with two deliberate exceptions. You get the real values back, so the output stays usable. The exceptions are in both shipped configs: card numbers are masked and secrets are redacted, which is irreversible on purpose, so those two are never restored. It is a per-type setting you can change (entity overrides).
- Drop-in. Swap one base URL. It works with any OpenAI-compatible client.
Proof, not promises
We built a comparison on real documents, not toy examples. The corpus is 120 real documents whose personal data was labeled independently, then cross-checked. Each solution runs on the same data.
The headline: on the same personal data placed inside a tool-call argument, the text-only guardrails measured (LiteLLM’s built-in Presidio guardrail, LLM Guard) leave 100 percent of it exposed, because they never look there, even for values they detect and scrub in plain text. PrivAiTe removes everything it detects from the tool call; end to end its tool-call leak equals its detection misses (about 15 percent on this corpus), the same misses plain text has. On plain-text recall, the full PrivAiTe preset leads the field.
The benchmark is public and reproducible. Run it yourself.
Ways to run it
- Standalone proxy. Run it next to your app, point your client at it. Best when you want one place to protect every model and client.
- Open WebUI filter. Drop the filter into Open WebUI and it runs in process, no separate service. It covers the chat message, but Open WebUI sends its title, tag and follow-up calls without running filters: with a cloud model, use the standalone proxy, which sees those calls too.
- LiteLLM guardrail. A custom guardrail for teams already running the LiteLLM proxy: it runs the engine in process, no separate service.
- Agent CLI gateway. Point Claude Code at PrivAiTe: the traffic to the provider is scrubbed and the reply restored, tool-call arguments included. Codex is supported in beta. Opt-in, off by default. Setup and limits.
Presets
- onnx (default). The full suite. Detects classic personal data plus secrets and passwords. Downloads a small model on first run (baked into the Docker image).
- light. Presidio with PrivAiTe’s recognizers; 0.5.0 also
covers common structured-secret formats. No Privacy Filter model is loaded;
spaCy language models are still required. It misses names the default finds:
privaite verify --preset lightreports the name in its own demo as leaked. - minilm and ultrafast (opt-in). PrivAiTe’s own multilingual model in place
of the Privacy Filter:
minilmon CPU anywhere,ultrafaston the Apple Neural Engine with Presidio running without spaCy, for agent CLIs on a Mac. Nothing changes unless you set one. Details
Install
Docker, model baked in, runs offline:
docker run -d -p 8400:8400 \
-e PRIVAITE_API_KEYS=change-me \
-e OPENAI_API_KEY=sk-... \
ghcr.io/crp4222/privaite
Also on Docker Hub as crp4222/privaite.
Or pip:
pip install privaite
python -m spacy download en_core_web_lg
Then point your client at http://localhost:8400/v1 with the key you chose. The
full quickstart, config reference, and threat model are in the
README.
Docs
- How to redact PII before sending prompts to an LLM: where PII hides in a request, the three ways to remove it, and how to check the result
- How PrivAiTe detects PII locally: the two engines, what each catches, known limitations
- Configuration reference: providers, presets, anonymization methods,
block_entities, custom patterns - Your policy, your types: the three mechanisms above as one deterministic, dry-runnable policy
- API reference: endpoints and the exact scanned/unscanned request surface
- See exactly what your provider receives: audit the proxy on your own data
- Threat model: what PrivAiTe protects against, what it does not, and what the optional detection cache keeps in memory
- Agent CLI gateway: Claude Code setup, Codex setup (beta), scanned surface, honest limits
- What a coding agent sends to its provider: wire-level measurement of real Claude Code and Codex sessions, 24/24 unprotected, 0/24 and 2/24 through the gateway, with the miss mechanism
- PrivAiTe vs Presidio, LLM Guard, LiteLLM: feature and benchmark comparison, plus what the gateways built for agent traffic state in their own docs
What PrivAiTe is not
Honesty is part of the pitch, so here is the fine print in plain words.
- It performs pseudonymization, not anonymization. The mapping is reversible by design, so the data it protects remains personal data under the law. Use it to reduce exposure and minimize what you send, not as a guarantee.
- Detection is best-effort. It is statistical and never catches 100 percent. A name it does not recognize can still reach the provider. Treat the output as best-effort.
- You stay the data controller. PrivAiTe is a tool you run. It does not take responsibility for your compliance program, and it makes no compliance claim.
- The software is provided as is, under an open-source license.
FAQ
Does it send my data anywhere? No. Detection runs locally and there is no telemetry. The only outbound request is the one you make to your chosen model provider, carrying stand-ins instead of real values. Outside Docker, a first run also downloads the detection model. Some dependencies contact their vendor by default and PrivAiTe turns each of them off: outbound connections lists them.
Does this make me GDPR or HIPAA compliant? No tool can do that for you. It helps reduce the personal data you expose to a third party, which can support your own compliance work. You remain responsible.
Which models does it work with? Anything with an OpenAI-compatible API, plus local models through Ollama and others.
What about latency? The light preset adds very little. The onnx preset adds more because it runs a model. You choose per use case.
Is it really open source? Yes. Read the engine, run the benchmark, open an issue.