Privacy Engine Architecture¶
Pipeline¶
The privacy pipeline is the center of Privyx:
Privacy Pipeline
│
┌──────────────┼──────────────┐
▼ ▼ ▼
Request Stream Response
│ │ │
▼ ▼ ▼
Detect Transform Restore
│ │ │
▼ ▼ ▼
Pseudonymize Deanonymize Restore
│ │ │
└──────────────┼──────────────┘
▼
Vault
Protocols¶
The engine depends only on protocols, never on concrete classes:
class Detector(Protocol):
async def detect(self, text: str, context: Context) -> Detection: ...
class Policy(Protocol):
async def decide(self, detection: Detection, context: Context) -> Detection: ...
class Operator(Protocol):
async def pseudonymize(...): ...
async def deanonymize(...): ...
class Vault(Protocol):
async def create(self, session: Session) -> None: ...
async def get(self, session_id: str) -> Session | None: ...
async def save(self, session: Session) -> None: ...
async def delete(self, session_id: str) -> None: ...
Operators¶
An operator decides what a detected span becomes and how (or whether) it is restored. All operators share the detect → decide → operate → vault pipeline; they differ in the substitution and its reversibility.
| Operator | Substitution | Reversible | Restored in a stream by |
|---|---|---|---|
pseudonym |
a codec token (<PRIVYX_EMAIL_1>), counter- or anchor-derived |
yes (vault) | codec syntax |
hash |
a codec token whose id is a content hash | yes (vault) | codec syntax |
encrypt |
a codec token whose id is a keyed digest; the vault holds AES-256-GCM ciphertext | yes (vault, decrypted with the key) | codec syntax |
redact |
a fixed string ([REDACTED]) |
no | — |
faker |
a realistic fake of the same kind (fake email, name, …) | yes (vault) | exact substituted values |
Faker¶
faker keeps the shape of the data — a model sees a plausible email or name
rather than a placeholder — which can help it reason. Two properties follow from
a fake value being ordinary text with no delimiter:
- Literal restore. Reversal matches the exact substituted strings (a trie of
this session's values), not the token grammar. Batch and streaming share that
matching, so
stream == batchstill holds. The proxies select the trie recognizer for any operator that declaresstream_restore = "literal"and the codec recognizer otherwise, viaStreamRouter's injectable processor factory. - Possible false positives. A fake value that also occurs naturally in a
response can be restored by coincidence — a risk the token operators (whose
placeholders are syntactically distinctive) do not have. Prefer
pseudonymwhen exact, collision-free reversal matters more than realism.
faker needs the optional extra: pip install privyx[faker] (or
uv sync --all-extras). Selecting it without the package fails at startup with a
ConfigError. Configure the locale and a determinism seed:
operator:
type: faker
locale: en_US # optional; Faker's default when omitted
seed: 1234 # optional; makes the same value fake identically across runs
Session Lifecycle¶
get_or_create_session(session_id)→ looks up the vault; creates + persists if missing.transform(text, session)→ detect → decide → pseudonymize →vault.save.restore(text, session_id)→ load session → deanonymize.
Providers are Transport¶
The provider is just a transport. PrivacyEngine never knows about HTTP,
SSE, or provider SDKs. This is what allows Privyx to grow from "LLM privacy
proxy" to "AI data privacy gateway" without rearchitecting the core.