Skip to content

Limitations

Privyx reduces what an AI provider sees of your data. It does not make a prompt safe by itself, and it is honest work to know where it stops. This page lists the limits in one place; the threat model explains the reasoning behind them.

Detection

Only what a detector finds is masked. Everything else in a request reaches the provider as you wrote it.

Limit What to do
Names, organizations, and project terms are not found without configuration. The built-in patterns recognize values by their shape, and a name has none. List them as word lists, or add the Presidio or LLM detector.
No detector is complete. A pattern can miss a format it was not written for; a language model can overlook a name. Test with privyx detect on real samples of what you send. The test suite scores the built-in patterns on a labeled corpus; see Testing.
The built-in patterns are not country-specific, apart from the US social security number. National ID numbers, tax numbers, and local formats are not covered. Add a pattern, or a plugin when the format needs a checksum.
A value that is encoded or split is not recognized: base64, URL-encoded, spelled out, or spread over several fields. Mask before you encode, with privyx mask.
Meaning is not masked. "The patient in room 4 who was admitted on Monday" names nobody and still identifies someone. Keep such text out of the prompt; no placeholder can stand in for it.

Coverage

Only chat requests on routed paths are masked, and within them only the parts that carry conversation content.

Limit What to do
A path without a route is forwarded as the client sent it: embeddings, the legacy completions API, batch and file uploads, Gemini's native API, reads of stored objects. Set proxy.passthrough_unknown: false to refuse those paths, or mask the data first with privyx mask.
Images, audio, PDFs, and other binary or base64 content are not inspected. Do not rely on Privyx for files; extract and mask the text yourself.
Request settings are left as sent: model, metadata, user, tool names, parameter names, enum values, and a response_format schema. Keep sensitive values out of those fields. Tool and parameter descriptions are masked.
URLs and ids inside messages are skipped: an image URL with a name in its query string goes out unchanged. Put such values in the text of a message instead.
JSON keys are never rewritten, and a chat message's participant name is kept. Put the value, not the key, where the sensitive text is.
Request headers are not masked, with one exception for Codex's turn metadata. Do not send sensitive values in custom headers.
A WebSocket connection is refused with 426, since Privyx could not mask its frames. Clients fall back to HTTP; Codex does so by itself.
Privyx sees only the traffic a tool sends to its provider's base URL. Telemetry or other requests to different hosts do not pass through it, and a tool that ignores its base URL setting bypasses Privyx entirely. privyx run warns when no request reached it; see Check that it works.

Restoring

Limit What to do
A token the model rewrote is not restored: translated, split by a space, or wrapped in other characters. Two departures are tolerated, a token without its outer delimiters and a long id on its own. Pick a token format your model reproduces reliably.
logprobs token strings are not restored, so a client that asks for them can see fragments of a token. Do not request logprobs through Privyx.
The redact operator cannot be restored at all, and faker restores by matching its fake values, which the model may also write by coincidence. Use pseudonym when exact reversal matters.
A tool that the provider runs for the model, such as web search or code execution, receives tokens. A search for a masked name searches for the token. Leave unmasked what such a tool must see, with the strict policy.
The model cannot reason about what it cannot see. It can pass a masked email address along; it cannot tell you its domain. Mask less with the strict policy, or use the faker operator so the model sees a plausible stand-in.

What the provider still learns

Masking hides a value, not everything about it:

  • That it exists, and what kind it is. <PRIVYX_EMAIL_1> says that an email address was there.
  • When two values are equal. The same value gets the same token within a session; with an anchor or the hash or encrypt operator, in every session.
  • Everything that was not detected, and the shape of the conversation around it.

A short unkeyed hash token can be matched by anyone who guesses the value. Prefer pseudonym with an anchor secret when that matters.

Security

Limit What to do
The proxy has no authentication and no notion of users. Anyone who can reach its port can send requests through it, and can name any session in the x-privyx-session header. Keep it on 127.0.0.1, or put an authenticating reverse proxy in front; see Deployment. Set session ids in your backend.
The vault and --map files hold original values in plain text, unless you use the encrypt operator. Protect them like the data they mirror, set a vault.ttl, or use encrypt.
The llm detector sends unmasked text to the model it asks. Use a provider you already trust with that text, or a model you host.
Privyx does not defend against prompt injection. Tool-call arguments are restored before your tool runs, so if injected instructions make the model call a tool that sends data elsewhere, real values are sent. Limit what your tools may do, as you would without Privyx.
Your side still has everything. The client's own transcripts, logs, and terminal hold original values. Protect them as before; Privyx changes only what leaves for the provider.
A plugin runs inside the proxy and sees unmasked text. Load only code you trust.
Privyx is a technical control, not a compliance certificate. Masking can support a privacy program; it does not replace a data processing agreement or a legal assessment. Treat it as one measure among several.

Maturity

Privyx is in its 0.1.x series. Defaults and internals can change between releases, and the changelog records each change. A native Gemini route is not implemented yet; see Google Gemini.

If you find a case where a detected value reaches the provider, or a token is restored wrongly, please report it privately; see the security policy.