Blog··11 min read

Microsoft Presidio alternatives for Node.js and TypeScript (2026)

Godfrey Lebo
Godfrey Lebo
Founder, Sether · Last updated 2026-07-09
Direct answer

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.

TL;DR
  • 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?

PresidioLLM GuardSetherDIY regex
LanguagePythonPythonTypeScript + Python portWhatever you write
Runs in-process in NodeNo (sidecar service)No (sidecar service)Yes (native TS, browser build too)Yes
Detection approachNER (spaCy) + pattern recognizersScanner suite; anonymization is one scanner among manyValidated patterns + label anchoring + secrets pack; multilingual labelsHand-rolled patterns, usually unvalidated
Reply restore round-tripNot built inVault-based, within its Python flowBuilt in: token vault, stream .restore(), in-memory LRU defaultYou build it
Streaming chunk safetyYou buffer and handle boundariesYou handle boundariesBuilt in: hold-back window, 256-byte defaultAlmost always missing
Install footprintHeavy (spaCy models, hundreds of MB)Moderate to heavy (Python + models)~35 KB, 1 runtime dependency (libphonenumber-js)Zero
Free-text NERYes, core strengthYes, via its anonymize scannerLabel-anchored + conversational cues; full NER is an add-on, not coreNo
Beyond redactionAnonymization operators (hash, encrypt, mask)Prompt injection, toxicity, and other guardrailsSecrets detection, SSE helpers, fetch/OpenAI/Anthropic wrappersNothing
LicenseMITOpen source (Protect AI)MITYours

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 situationPickWhy
Python data team, batch anonymization of datasets, logs, documentsPresidioExactly what it was built for; mature operators and many entity types
Free-text NER accuracy is the hard requirement, GPU availablePresidiospaCy-based NER on tuned models is its core strength
Python LLM app that also wants prompt-injection and toxicity guardrailsLLM GuardOne scanner suite for the whole guardrail layer; anonymization included
Node/TypeScript service calling OpenAI or Anthropic, streaming repliesSetherIn-process, streaming-safe, restore built in, no sidecar to run
Python service wanting the same lightweight redact/restore modelSether (PyPI)Same API in Python: redact_sync/restore_sync, async streaming, ASGI/WSGI middleware
Weekend prototype, throwaway internal toolDIY regexFine 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.

The request-path shape in Sether
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.
Try it

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