local-pii

Get Started

On-device PII anonymization for Expo, React Native, the browser and Node. Redact locally, send only placeholders to any LLM, rehydrate the reply.

local-pii anonymizes personal data before it leaves the device, sends only placeholders to your LLM, and rehydrates the reply locally. The placeholder → original mapping never leaves the device.

Ontem encontrei João Silva. Meu telefone é +49 151 12345678.
        │  anonymize (on device)

Ontem encontrei [GIVEN_NAME_1] [SURNAME_1]. Meu telefone é [PHONE_1].   ← only this is sent
        │  ↕ your LLM
        ▼  rehydrate (on device)
João Silva / +49 151 12345678 come back — the mapping never left the device.

Install

bun add local-pii   # or npm / pnpm / yarn

The deterministic pipeline (emails, phones, cards, IBAN, SSN, IP, URLs) works with nothing else installed, in React Native, the browser and Node.

Quickstart

import { createAnonymizer, rehydrate } from "local-pii"

const pii = createAnonymizer()

const { redactedText, mapping } = await pii.anonymize(
  "Email me at ana@acme.com or call +49 151 12345678",
)
// redactedText → "Email me at [EMAIL_1] or call [PHONE_1]"

const reply = await callYourLlm(redactedText) // only placeholders leave the device
const answer = rehydrate(reply, mapping) // originals restored locally

mapping is plain data ({ "[EMAIL_1]": "ana@acme.com", … }). Keep it in memory — never log it, send it, or put it in analytics/crash reports.

Add on-device AI

The deterministic detectors don't catch names or addresses — that's the Rampart NER model's job. Pick the backend for your platform:

  • Expo / React Nativerampart() via onnxruntime-react-native.
  • Browser / webrampartWeb() via onnxruntime-web (WASM/WebGPU).
import { createAnonymizer } from "local-pii"
import { rampart } from "local-pii/expo" // or: rampartWeb from "local-pii/web"

const pii = createAnonymizer({
  ner: rampart({ model: require("@local-pii/model-rampart/assets/rampart-q4.onnx") }),
})
// "Ontem encontrei João Silva" → "Ontem encontrei [GIVEN_NAME_1] [SURNAME_1]"

Wrap your LLM calls

Adapters wrap the whole anonymize → call → rehydrate cycle, including tool calls, so you barely change your code:

Where next

On this page