Microsoft Presidio alternatives for Node.js and TypeScript (2026)
Microsoft Presidio is a Python NER-plus-patterns toolkit built for data pipelines; it is excellent there. For a Node.js or TypeScript service that redacts LLM prompts on the request path, the main alternatives are Sether (native TypeScript, streaming redact and restore, ~35 KB), LLM Guard (Python guardrail suite), or DIY regex, which usually fails on streaming.
- Presidio is battle-tested and flexible. If you run Python data pipelines and need batch anonymization with many entity types, use it and stop reading.
- For TS/Node product teams the friction is real: a separate Python service to deploy, no built-in restore round-trip for LLM replies, and a heavy install for a request-path concern.
- Sether is a native TypeScript library (~35 KB, 1 runtime dependency, MIT) built for the LLM request path: streaming redact and restore with a token vault.
- LLM Guard is a Python guardrail suite where anonymization is one scanner among many; pick it when you want prompt-injection and toxicity checks in the same layer.
- DIY regex is fine for a prototype and usually falls to the streaming chunk-boundary trap in production.
What is Presidio, and what is it great at?
Microsoft Presidio is an open-source PII detection and anonymization toolkit from Microsoft, written in Python. It splits the job into an analyzer (NER via spaCy models plus a large set of pattern recognizers) and an anonymizer (configurable operators: replace, mask, hash, encrypt).
It deserves its reputation. It is battle-tested, very flexible, supports a wide range of entity types, and was designed for data pipelines: anonymizing datasets, scrubbing logs at rest, de-identifying documents in batch. If that is your workload, especially with GPU budget and a Python team, Presidio is the mature choice and you should use it.
This post is not "Presidio bad". It is about a specific mismatch: TypeScript and Node.js product teams who need redaction on the LLM request path, which is a different job than batch anonymization.
Where is the friction for Node and TypeScript teams?
- It is a Python service you now operate. There is no native Node library, so a TS team runs Presidio as a sidecar or microservice: another deployment, another thing to monitor, another network hop that itself carries the PII.
- No reply-restore round trip. Presidio anonymizes text going in. LLM apps also need the reply mapped back: if the prompt said
<PERSON_1>, the answer mentioning<PERSON_1>should reach the user with the real name. Presidio has no built-in vault-backed restore for this; you build and maintain that state yourself. - Footprint on the request path. spaCy models and their dependencies are hundreds of megabytes and take real memory. Justified for a pipeline node; heavy for a check that runs inside every chat request.
- Streaming is your problem. LLM responses stream, and values split across chunk boundaries defeat per-chunk scanning. Presidio was not designed around token streams, so buffering and boundary handling land on you. We wrote up how subtle that gets in the chunk-boundary bug post.
None of these are design flaws. They are the natural shape of a tool built for data pipelines being pressed into a latency-sensitive request path in a different language.
How do the alternatives compare?
| Presidio | LLM Guard | Sether | DIY regex | |
|---|---|---|---|---|
| Language | Python | Python | TypeScript + Python port | Whatever you write |
| Runs in-process in Node | No (sidecar service) | No (sidecar service) | Yes (native TS, browser build too) | Yes |
| Detection approach | NER (spaCy) + pattern recognizers | Scanner suite; anonymization is one scanner among many | Validated patterns + label anchoring + secrets pack; multilingual labels | Hand-rolled patterns, usually unvalidated |
| Reply restore round-trip | Not built in | Vault-based, within its Python flow | Built in: token vault, stream .restore(), in-memory LRU default | You build it |
| Streaming chunk safety | You buffer and handle boundaries | You handle boundaries | Built in: hold-back window, 256-byte default | Almost always missing |
| Install footprint | Heavy (spaCy models, hundreds of MB) | Moderate to heavy (Python + models) | ~35 KB, 1 runtime dependency (libphonenumber-js) | Zero |
| Free-text NER | Yes, core strength | Yes, via its anonymize scanner | Label-anchored + conversational cues; full NER is an add-on, not core | No |
| Beyond redaction | Anonymization operators (hash, encrypt, mask) | Prompt injection, toxicity, and other guardrails | Secrets detection, SSE helpers, fetch/OpenAI/Anthropic wrappers | Nothing |
| License | MIT | Open source (Protect AI) | MIT | Yours |
One honest note on that table: Sether's free-text name detection is weaker than Presidio's NER. Sether's identity pack is label-anchored ("name: Sarah Chen" in Latin, CJK, Cyrillic, and Arabic scripts), which is precise but misses names dropped mid-sentence with no cue. When free-text NER is the core requirement, that column favors Presidio, and we say so. The trade-off is explored in Regex vs NER for PII detection.
Which one should you pick for your scenario?
| Your situation | Pick | Why |
|---|---|---|
| Python data team, batch anonymization of datasets, logs, documents | Presidio | Exactly what it was built for; mature operators and many entity types |
| Free-text NER accuracy is the hard requirement, GPU available | Presidio | spaCy-based NER on tuned models is its core strength |
| Python LLM app that also wants prompt-injection and toxicity guardrails | LLM Guard | One scanner suite for the whole guardrail layer; anonymization included |
| Node/TypeScript service calling OpenAI or Anthropic, streaming replies | Sether | In-process, streaming-safe, restore built in, no sidecar to run |
| Python service wanting the same lightweight redact/restore model | Sether (PyPI) | Same API in Python: redact_sync/restore_sync, async streaming, ASGI/WSGI middleware |
| Weekend prototype, throwaway internal tool | DIY regex | Fine at that scale; just do not let it quietly graduate to production |
What does a Presidio-to-Sether migration map to?
Conceptually the pieces line up, so a migration is mostly renaming ideas rather than rebuilding them. Presidio's analyzer (recognizers for EMAIL_ADDRESS, PHONE_NUMBER, CREDIT_CARD and so on) maps to Sether's detector packs: the basic pack covers email, phone, card with Luhn, SSN, IPs, and IBAN with mod-97 validation.
Presidio's anonymizer with a replace operator maps to Sether's tokenization, which emits stable tokens like <EMAIL_uuid>. The piece with no Presidio equivalent is the vault-backed restore: Sether keeps the token-to-value mapping (in-memory LRU by default, 10,000 entries with a 1-hour TTL, pluggable if you want your own store) and .restore() maps the LLM reply back.
import { Sether } from '@raeven-co/sether';
const sether = new Sether();
// Presidio analyzer + anonymizer, in one sync call:
const { text: safe } = sether.redactSync(userMessage);
// The part Presidio does not do: restore in the reply
const reply = await callOpenAI(safe);
const { text: restored } = sether.restoreSync(reply);For streaming, Node streams pipe through sether.redact() and sether.restore(), and there are SSE helpers plus wrappers (wrapOpenAI, wrapAnthropic, wrapFetch) if you would rather not touch the call sites at all. The full setup is in Redact PII before OpenAI in TypeScript.
What you give up: Presidio's breadth of entity types and its NER-first detection. What you gain: no sidecar, a ~35 KB in-process dependency, and streaming restore you do not have to write. Whether that trade is good depends entirely on which column of the decision table you live in.
Why does "lightweight" matter on the request path?
A redaction layer on the request path runs on every single LLM call your product makes. Its latency is added to every response, its memory is resident in every instance, and its dependency tree is attack surface on your most sensitive data flow.
A sidecar adds a network hop that itself carries the raw PII, which means TLS, auth, and availability questions for one more internal service. An in-process library with one runtime dependency keeps the data in the process that already had it. Supply-chain weight matters here too, which is why we documented getting Sether to zero open audit alerts.
The broader picture, including where gateways and browser extensions fit alongside libraries, is in the complete guide to PII redaction for LLM apps.
Frequently asked questions
- Is there a native Node.js port of Presidio?
- No. Presidio is Python, and Node teams run it as a separate service behind HTTP. That works, but it adds a deployment, a network hop carrying the raw PII, and latency on every request, which is the main reason native TypeScript alternatives exist.
- Is Sether as accurate as Presidio?
- On structured PII (emails, phones, cards, IBANs), validated patterns put them in the same range, and validation like Luhn and mod-97 keeps false positives near zero. On free-text names with no label or cue, Presidio's spaCy NER is stronger than Sether's label-anchored detection. Pick based on which class of text you actually process.
- Can I use Sether from Python, or is it TypeScript only?
- There is a Python port:
pip install sether(v0.1.1, Python 3.9+). It has the same redact/restore model with sync and async streaming, wrappers for OpenAI, Anthropic, and httpx, and ASGI/WSGI middleware built in. - What does Sether cost compared to Presidio?
- Both are free and open source under MIT. Neither charges for the library. The real cost difference is operational: Presidio typically means running and monitoring a Python service; Sether is an in-process dependency.
Sether is free and MIT-licensed: npm i @raeven-co/sether · pip install sether. Or paste some text into the live sandbox and watch the redaction happen.
Keep reading
- PII redaction for LLM applications: the complete guide (2026)How to keep names, emails, cards, and secrets out of OpenAI, Anthropic, and other LLM providers: detection methods, reversible tokenization, streaming pitfalls, and tool comparison.
- How to redact PII before sending prompts to OpenAI (TypeScript)A hands-on TypeScript tutorial: detect and redact emails, phones, cards, and API keys before they reach OpenAI, then restore them in the reply. Streaming included.
- Regex vs NER for PII detection: when patterns aren't enoughWhere validated patterns win, where label anchoring helps, and where only NER catches PII. Trade-offs, local ONNX inference, and a practical recommendation ladder.
- The chunk-boundary bug: why streaming PII redaction is hardHow a property-based test caught emails slipping through Sether's streaming redactor when values split across chunks, and the hold-back window that fixed it.