Skip to main content

PII Guard

PII Guard, a privacy gateway for AI agents: personal data becomes reversible placeholders before it leaves your network and comes back in the answer. You send 'Email Jane Placeholder: jane@example.net', the model sees 'Email [[PERSON_7f3a]]: [[EMAIL_20c2]]', you get back 'Done: emailed Jane Placeholder.' Built on Meta's open models, runs on your machine, Apache-2.0.

Try it live Demo Video CI Model on Hugging Face License: Apache-2.0

  • What it does. An OpenAI-compatible gateway for AI agents. Change one base URL, and the names, addresses, IDs and faces it detects become reversible placeholders ([[PERSON_7f3a]]) before the request leaves. The real values come back in the answer, mid-stream and inside tool calls. Detection is rules plus a small NER that runs on CPU: XLM-RoBERTa base, distilled from Meta's Muse Glimmer 30B.
  • How well. On 990 fictional agent-traffic documents (de, en, hu), 0.2% of personal values reached the model and 0.7% of masks hit text that was not personal data. The lowest-leak alternative, PrivAiTe, leaked 2.4% and over-masked 35.2% on the same documents.
  • How fast. The NER takes 43 ms per message on a laptop CPU (p50; p95 50 ms, measurement). No LLM call and no API cost in fast mode.
  • Where your data goes. In fast mode, the default (rules plus the on-device NER, no LLM call), the original text is processed only on your machine. The hosted demo receives what you type, so use fictional data there. The optional audit and SAM 3.1 endpoints receive the original text or pixels. Anything the detectors miss passes through: pseudonymization, not anonymization or a compliance guarantee.
Leak rate against over-masking rate on 990 fictional agent-traffic documents; lower-left is better. PII Guard fast 0.2% leak, 0.7% over-masking; PrivAiTe 2.4% and 35.2%; Presidio 16.6% and 33.4%; LLM Guard 20.2% and 16.9%; PII Guard rules only 38.8% and 0.0%.

990 fictional documents (de, en, hu), drawn by pii-guard eval report from the run records. All sets · Shareable PNGs

Built for the Meta Global AI Developer Hackathon (Oct 26 – Nov 4, 2026) · 3-minute video walkthrough. Released on PyPI as wattmind-pii-guard. Known gaps.

Quickstart

Walkthrough: watch the 3-minute demo video on YouTube.

No install: the live demo runs as a guest with no sign-up (fictional data only; capped sponsored allowance).

On your machine (needs uv): the web console, the API and the gateway on http://127.0.0.1:8787, with a mock model that echoes what it receives:

uvx --python 3.12 --from wattmind-pii-guard pii-guard up

Launch a coding agent directly (OpenCode, Codex, Claude Code, pi, Muse Code) behind an in-process local gateway with Meta's muse-spark-1.3:

export META_API_KEY=<your-key>
uvx --python 3.12 --from wattmind-pii-guard \
  pii-guard run opencode --upstream meta --model muse-spark-1.3
  • The package is wattmind-pii-guard: pii-guard on PyPI is an unrelated project. The latest develop builds from git (--from 'wattmind-pii-guard @ git+https://github.com/watt-mind/pii-guard@develop') without the web console; everything else is the same.
  • First run: downloads PyTorch and the NER once (the NER is 1.1 GB); /healthz reports "ner": "ready" when detection is live.
  • A real model: --upstream meta with META_API_KEY; openai, fireworks, openrouter or any --upstream-url work the same way (pii-guard up options, coding agent integrations).
  • No auth by default, bound to 127.0.0.1: set PII_GUARD_API_TOKEN before exposing it (how to expose it safely).

Results

Results table, leak · over-masking per fixture set for PII Guard fast, PrivAiTe, Presidio and LLM Guard; lower is better and the lowest per metric in each row is bold. PII Guard fast leaks 0.0% on the hand-written set, 0.2% on agent traffic, 0.6% on Nemotron-PII, 0.8% on CEE-PII and 19.1% on Gretel finance (Presidio 18.8%). Every number is in the text table below the image.
Table as text
Leak · over-masking PII Guard fast PrivAiTe Presidio LLM Guard
Hand-written · 62 docs (de+en+hu+pl) 0.0% · 1.3% 13.0% · 25.7% 25.5% · 26.1% 46.3% · 33.7%
Agent traffic · 990 docs (de+en+hu) 0.2% · 0.7% 2.4% · 35.2% 16.6% · 33.4% 20.2% · 16.9%
Nemotron-PII · 300 docs (en) 0.6% · 1.5% 4.0% · 5.6% 12.8% · 32.0% 28.8% · 0.6%
CEE-PII · 451 docs (en+hu) 0.8% · 7.3% 4.4% · 59.2% 36.5% · 52.5% 75.8% · 66.4%
Gretel finance · 400 docs (de+en) 19.1% · 24.4% 27.7% · 46.5% 18.8% · 75.4% 59.3% · 17.0%

Leak: labeled personal values left fully readable in what the model receives. A value with only part left readable (a surname, the tail of a number) is a partial leak, counted separately: PII Guard fast 0.0% on agent traffic, 1.4% on the hand-written set. Over-masking: masks on text that was not personal data. Lower is better for both; in each row, the lowest leak and the lowest over-masking are bold.

Caveats

  • The sets are small (tens to hundreds of documents per language): a point or two of difference is noise. Agent traffic is written by Muse models from fictional seeds, which can flatter a detector taught by Muse labels; that is why the hand-written and public sets sit next to it.
  • Finance text is the weak spot. On Gretel finance's own labels PII Guard leaks 19.1%, about level with Presidio's 18.8%; on corrected labels (why) 2.4% against 14.8%. Over-masking is high on Gretel finance (24.4%) and CEE-PII (7.3%), where reference numbers without a cue word come out as IDs.
  • NER v4.2 leaks more than the previous v2.0 on CEE-PII (0.8% against 0.4%), the Polish pl-pii-bench (4.2% against 3.5%) and TAB court text (5.1% against 4.0%, scored locally only), and less on the hand-written sets, agent traffic and the EU multilingual set (model notes).
How this is measured

Every tool ran on the same documents (byte-identical files) and was scored by the same code; comparison tools use their documented defaults (configurations). All data is fictional. PII Guard fast is rules plus the published NER v4.2 (model card). The hand-written set pools de, en, hu, pl; the full tables, per-language numbers, Ai4Privacy (in-distribution for the NER, so not a win) and the corrected finance labels are in the benchmark report.

NER model time per message on CPU (the model alone, not the whole gateway): p95 50 ms (122 messages of about 382 characters, arm64 macOS 26.5, 8 threads; measurement).

Records behind the PII Guard fast column: Hand-written (1 · 2), Agent traffic, Nemotron-PII, CEE-PII, Gretel finance; every other cell is in the run log.

Per-language results, the open NER models compared behind the same rules, and latency are in the benchmark report.

Which Meta models do what

Muse Glimmer 30B was the best zero-shot detector we measured (F1 99.3, 0.0% leak on the 38 Hungarian and English hand-written documents of that run, record) and far too slow for a request path at 9.4 s per document (p50). So it labeled the training text instead, and XLM-RoBERTa base learned from it: 43 ms on CPU, published as watt-mind/pii-guard-ner-xlmr-base v4.2.

In the default fast mode, every model that sees original personal data runs on your machine. Audit mode (Muse Spark Open) and SAM 3.1 are opt-in and receive originals at the endpoint you configure.

Job Meta model
Fast-mode NER (CPU) XLM-RoBERTa base (FacebookAI, MIT), fine-tuned: model card, training
Teacher: labels training text Muse Glimmer 30B (open weights, Apache-2.0): distillation
Benchmark writer; demo model Muse Spark (Meta Model API): which model does what
Audit mode, red team Muse Spark Open (open weights): audit runs, red team
Fictional ID specimens Muse Image: painted the portraits, signatures and stamps (generation record)
Signatures in images SAM 3.1 (Meta Model API, decoded with Meta's meta_sam_parser); faces stay on the device (YuNet): specimen results

Inside the gateway

How it works: your app or agent sends a request to PII Guard, which runs on your side. On the way out PII Guard does 1. extract text, tool calls and image regions; 2. detect personal data with rules, then NER, then an optional audit LLM; 3. apply policies and pseudonymize with keyed placeholders; 4. mask image regions (faces, SAM 3.1, OCR). Only the masked request, with placeholders such as [[PERSON_968e29]] in place of personal data, crosses to the LLM provider. The response streams back to PII Guard, which does 5. restore placeholders chunk by chunk, and your app or agent gets the answer with the real values. The placeholder-to-value mapping never leaves PII Guard.

The gateway speaks the OpenAI Chat Completions and Responses APIs. It masks messages, tool definitions, tool-call arguments, tool results and images, restores placeholders in streams, keeps one placeholder per person per session, and fails closed on anything it can't mask; the mapping never leaves PII Guard. Every document in the hand-written set unmasks back to its original exactly.

Mode Detection Leaves your machine
fast (default): everyday traffic Rules + on-device NER; no LLM call Only the masked request, to your LLM provider
fast with PII_GUARD_DETECTORS=rules: offline runs, CI, structured data Rules only; free-text names only as known values your app registers Only the masked request
audit (opt-in, X-PII-Guard-Mode: audit): measuring the upper bound, training labels Rules + NER + an LLM detector; fails closed if that call fails The original text, to the audit endpoint you configure

Grammar-aware. In Hungarian a name changes with every case; the ending stays outside the placeholder:

Hungarian (real output), with an English gloss
You send Kérem, írjon Próba Zsuzsannának a zsuzsa.proba@example.com címre.
Please write to Zsuzsanna Próba at …
The model sees Kérem, írjon [[PERSON_968e29]]nak a [[EMAIL_e7038b]] címre.
… to [[PERSON]] …
You get back Megírtam a levelet Próba Zsuzsannának.
I wrote to Zsuzsanna Próba.

Language packs. Hungarian, English, German and Polish each ship validated identifier rules, cue words and case handling; a new language is one package (how to add a language).

What it detects: people, addresses, emails, phone numbers, dates of birth, national and document IDs (including Hungarian tax and TAJ numbers), bank accounts and payment cards, vehicle plates, parcel numbers and secrets such as API keys, plus faces, ID photos and signatures in images (full entity list).

Real agents behind it. On 2026-10-29 four unmodified coding agents (pi, OpenCode, Codex, Muse Code) worked a fictional customer file through the gateway: 115 requests, none of the file's 9 personal values reached the model, and the answers and the files on disk came back with the real values. No turn was refused; in an earlier run on 2026-10-28, 7 of 15 were, before two gateway fixes (a system note saying the placeholders are intentional, and the NER no longer masking file names). Two Codex turns stopped on gateway bugs; ten turns is a sample, not a benchmark (harness matrix).

Architecture · sessions and modes · threat model · GDPR context

Thirteen differentiators, each with evidence.

Use it from code

Round trip

Mask, then unmask with the session PII Guard keeps. With pii-guard up running, this script takes the session id and the person placeholder from the first response (output from fast mode):

R=$(curl -sS http://127.0.0.1:8787/v1/mask \
  -H 'Content-Type: application/json' \
  -d '{"text": "Email Jane Placeholder, jane.placeholder@example.net, about the refund.", "language": "en"}')
echo "$R"
# {"masked_text":"Email [[PERSON_604260]], [[EMAIL_84bb25]], about the refund.",
#  "session_id":"s_a925963eb8819a782a68c56f41265424","entities":[...],"kept":[]}
SID=$(echo "$R" | python3 -c 'import json,sys; print(json.load(sys.stdin)["session_id"])')
P=$(echo "$R" | grep -o '\[\[PERSON_[0-9a-f]*\]\]' | head -1)
curl -sS http://127.0.0.1:8787/v1/unmask \
  -H 'Content-Type: application/json' \
  -d "{\"text\": \"Done: I emailed $P about the refund.\", \"session_id\": \"$SID\"}"
# {"text":"Done: I emailed Jane Placeholder about the refund.","unresolved":[]}

The session id and the placeholders differ on every run: each new session gets its own.

Python client

Put it in front of an existing client; only the base URL changes:

# pip install openai
from openai import OpenAI

client = OpenAI(base_url="http://127.0.0.1:8787/v1", api_key="unused")  # mock upstream
stream = client.chat.completions.create(
    model="mock",
    stream=True,
    messages=[
        {"role": "user", "content": "Refund Jane Placeholder, jane.placeholder@example.net."}
    ],
)
for chunk in stream:  # the mock echoes the masked text it got; the gateway restores the values
    print(chunk.choices[0].delta.content or "", end="")
# Echo: Refund Jane Placeholder, jane.placeholder@example.net.

What the model actually received: curl -sS 'http://127.0.0.1:8787/v1/traffic?limit=1' shows the last gateway request as sent upstream, with placeholders (pii-guard up keeps a masked-only traffic log). API guide · agents and harnesses.

Console tour

Console tour in the dark theme, recorded in fast mode (rules + NER) with fictional English data: Overview, a support email masked in the Playground with names replaced by placeholders, the same session in Chat, Logs and Connect

Console tour, recorded in fast mode (rules + NER) with fictional data. More screens · Video walkthrough.

Run the console locally: make demo from a checkout.

Contributing, security, license

The contributing guide has the rule that matters most: never commit real personal data, not even in a test.

Security: found a way to make personal data leak past PII Guard? Report it privately: SECURITY.md.

Licensed Apache-2.0.

Metadata

Release files for wattmind-pii-guard 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wattmind-pii-guard 0.1.1
File Size Uploaded Provenance Actions
wattmind_pii_guard-0.1.1.tar.gz 19.7 MB Without provenance Details for wattmind_pii_guard-0.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for wattmind-pii-guard 0.1.1
File Interpreter ABI Platform Actions
wattmind_pii_guard-0.1.1-py3-none-any.whl Python 3 none any Details for wattmind_pii_guard-0.1.1-py3-none-any.whl

Total release size: 23.6 MB

Release files / wattmind_pii_guard-0.1.1.tar.gz

Download URL wattmind_pii_guard-0.1.1.tar.gz
Size 19.7 MB
Tags Source
SHA-256 checksum
How to use checksums
a44e785a1c36c7dcf234dd33be8e161b427db2ef94fb7652a2634213445d0bf4
BLAKE2b-256 checksum
How to use checksums
a0e65cc21373fd20684c9a6b89162adff634a9bf0d77b029478f8fc8d89f2b6a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / wattmind_pii_guard-0.1.1-py3-none-any.whl

Download URL wattmind_pii_guard-0.1.1-py3-none-any.whl
Size 3.8 MB
Tags Python 3
SHA-256 checksum
How to use checksums
197be68e40ce243ab8483c4f9efd3b85fc0abec6b175d1e0d921a8f7c5fd7772
BLAKE2b-256 checksum
How to use checksums
4c5b083ff736a8b00783f6547747c89cf8ddb98e33bcab545e7d7545225da1da
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via uv/0.13.6 {"installer":{"name":"uv","version":"0.13.6","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Security for wattmind-pii-guard 0.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page