Priivy
A Rust PII detection engine that replaces Presidio at 66x to 1,525x the speed
- Rust
- ONNX
- WebAssembly
- gRPC
- PostgreSQL
- Helm
- Vault

What it is
Priivy is a PII detection and anonymization engine I wrote in Rust as a full replacement for Microsoft Presidio. It detects 236 entity types across more than 20 countries, validates structured identifiers with checksums (Luhn, mod-97, Verhoeff) for near-zero false positives, and anonymizes with replace, redact, mask, hash, or reversible AES-256-GCM encryption. The API is Presidio-compatible over REST and gRPC, and PyO3 bindings make it a drop-in for Python callers.
The workspace is 15 crates, one per bounded context: core types, analyzer, anonymizer, regex, NLP, server, config, CLI, structured data via Arrow, image OCR, and WASM.
Priivy Guard, the browser extension, runs the same engine compiled to WebAssembly entirely in-browser, so no PII leaves the machine for scanning. It intercepts form submits, file uploads, and fetch/XHR/WebSocket bodies, then blocks or nudges based on policy before data is sent.
Why it matters
PII scanning sits in the request path of everything: LLM gateways, upload forms, batch jobs. Presidio's Python + spaCy latency makes inline scanning expensive. Measured on identical inputs, warm cache, release build:
| Workload | Presidio | Priivy | Speedup |
|---|---|---|---|
| Short text (SSN) | 3.05 ms | 2.0 us | 1,525x |
| Multi-PII (5 types) | 5.98 ms | 62 us | 96x |
| 10 KB document | 266 ms | ~4 ms | 66x |
| 50 KB document | 1.59 s | ~14 ms | 114x |
| 100 KB document | 3.76 s | ~28 ms | 134x |
That is 66x at 10 KB and 1,525x on short texts. At these latencies (124 us for the full analyze plus anonymize pipeline), DLP stops being a batch job and becomes an inline filter. The workspace runs about 675 tests.
Architecture decisions
Regex-first, ML optional. The regex-only build compiles to under 5 MB with zero ML dependencies; ONNX models (GLiNER for zero-shot NER, Piiranha for fixed-vocabulary PII) sit behind feature flags for when recall on unstructured names matters, and both can run together through a composite engine. Feature flags keep the minimal Docker image around 15 MB while the full model images run 500-600 MB.
Parallelism by construction. Documents split into overlapping 4 KB chunks processed by rayon, recognizers fan out concurrently within each chunk, and results merge with offset correction and deduplication across overlap zones. Zero-copy text slices and static entity names keep heap allocation out of hot paths; the regex crate's SIMD literal matching does the rest.
Strict lints as policy: no unsafe, no unwrap, expect, or panic, no unchecked indexing, enforced workspace-wide. Encryption-based anonymization is reversible by design, but deanonymize requires an Operator+ role and is audit-logged before decryption runs, with metadata only (text length, entity types, outcome), never content. The extension is WASM rather than a cloud call, because scanning PII by sending it to a server fails the pitch. Production deploys through a Helm chart with Envoy Gateway, Vault secrets, and PostgreSQL 18.