Skip to content
GitHubOpen source
Product
Docs
Solutions
IndustriesAll industries
CompareCompare all AI data-leak tools
Benchmarks
GitHubApache-2.0

Reversible pseudonymisation for LLMs: mask data and restore it in the answer

To pseudonymise LLM prompts reversibly, replace each sensitive value with a substitute before the request leaves, keep the mapping somewhere the provider cannot reach, and translate the substitutes back in the response. Shadow does all three on your machine: it swaps names, emails, IDs and secrets for format-preserving fakes from a persistent persona, stores the pairs in an encrypted local vault, and restores real values in answers, streams and tool calls.

What does reversible pseudonymisation mean for LLM prompts?

Section titled “What does reversible pseudonymisation mean for LLM prompts?”

GDPR Article 4(5) defines pseudonymisation as processing personal data so that it can no longer be attributed to a person without additional information, provided that information is kept separately and protected. Shadow maps onto that definition directly:

GDPR elementIn Shadow
Personal data replacedDetected values become fakes or typed tokens
Additional informationThe real↔fake mapping in vault.bin
Kept separatelyThe vault and its key stay on the machine or pod running Shadow. The provider receives fakes for every detected value, never the mapping
Technical measuresChaCha20-Poly1305 encryption, a 32-byte key in vault.key with mode 0600, atomic writes

The “reversible” part is what makes it practical. Plain redaction (<PERSON>) is safe but destroys meaning, and the answer refers to placeholders you then have to resolve by hand. With reversible pseudonymisation the answer comes back about your real customer.

you ──"Summarise this thread: jane.doe@acme.example asks about the Acme GmbH renewal"────────────────▶ Shadow
Shadow ──"Summarise this thread: xiu.wang@lingxi.cn asks about the Lingxi Technology Co., Ltd. renewal"──▶ provider
provider ──"xiu.wang@lingxi.cn wants renewal terms…"──────────────────────────────────────────▶ Shadow
Shadow ──"jane.doe@acme.example wants renewal terms…"───────────────────────────────────────▶ you

Three properties make the round trip reliable:

  • Format-preserving. A fake email is an email, a fake card passes Luhn, a fake IBAN passes mod-97, a fake UK NINO looks like a NINO. Code, validation logic and the model’s reasoning all keep working.
  • Consistent. The same real value always gets the same fake, and no two real values share one, so restoration is exact. Because fakes come from one persona, they are also consistent with each other: your company maps to the persona’s company, and emails at your domain map to the persona’s domain.
  • Persistent. The vault survives restarts, so a customer gets the same fake tomorrow. Conversation history and long-running agents stay coherent.

Surrogates, tokens or redaction: which treatment?

Section titled “Surrogates, tokens or redaction: which treatment?”

Shadow’s intent-aware policy picks a treatment per value. Three of them are pseudonymisation choices:

TreatmentProvider seesReversibleBest for
surrogate (default)hui.zhao@zhilan.cnYesConversation, analysis, code: the model writes naturally
token[PERSON_0001], [EMAIL_0002]YesReports over many rows: the model treats values as opaque IDs
redact[REDACTED_SECRET]NoValues that must never leave and never need to come back

The fourth treatment, keep, sends the real value. It is used for the recipient when you ask the model to write to or act on someone (faking the recipient would make the model address the wrong person) and for what an action in the provider’s cloud needs; every keep is logged. Set [policy.act] targets = "surrogate" to pseudonymise recipients too, restored in the answer. See intent-aware policy.

Terminal window
curl -fsSL https://raw.githubusercontent.com/Tiny-Bubble-Company/shadow/main/packaging/install.sh | sh
shadow init --org "Acme GmbH"
shadow identity # the fake identity providers will see
Terminal window
echo "Ask Jane Doe (jane.doe@acme.example, +1 415 555 0100) about Acme GmbH" | shadow mask > masked.txt
cat masked.txt
shadow unmask < masked.txt # the original text comes back

Nothing is sent anywhere by these commands. They use the same vault as the proxy.

Terminal window
shadow terms add "org:Globex" "person:Jane Doe" "Project Falcon"

or in shadow.toml:

organization = ["Acme GmbH"]
terms = ["org:Globex", "person:Jane Doe", "Project Falcon"]
internal_domains = ["acme.example"]
[policy.report]
person = "token"
email = "token"
phone = "token"
address = "token"
default = "surrogate"
[policy.always]
secret = "redact" # opt-in; irreversible
Terminal window
shadow run
export OPENAI_BASE_URL=http://127.0.0.1:8877/gw/openai/v1
export ANTHROPIC_BASE_URL=http://127.0.0.1:8877/gw/anthropic

Or cover every tool on the machine that calls a supported provider with proxy mode (shadow install --apply). If you build in Rust, embed shadow-core and call mask / unmask directly.

Terminal window
shadow vault stats # counts by kind
shadow vault export --i-understand # real↔fake pairs in clear text
shadow vault forget "jane.doe@acme.example" # drop one mapping

After forget, the value gets a new fake next time it appears, and older responses that mention the old fake are no longer restored. That is useful for honouring an erasure request against your local mapping. It does not delete anything the provider already holds, which for a detected value is only the fake.

Pseudonymisation is named in GDPR Article 25 (data protection by design) and Article 32 (security of processing), and the EDPB’s Guidelines 01/2025 on pseudonymisation describe it as a way to reduce risk while keeping data useful. Shadow gives you a technical implementation of it for AI traffic: the substitution happens before transfer, and the additional information never leaves your infrastructure. Pseudonymised data is still personal data for whoever holds the key (Recital 26), so your other obligations remain.

Frequently asked questions

What is the difference between pseudonymisation and anonymisation for LLM prompts?

Anonymised data cannot be linked back to a person by anyone. Pseudonymised data can be, but only with additional information kept separately. Shadow pseudonymises: the provider sees fakes, and the mapping back to real values stays in an encrypted vault on your machine.

How does Shadow restore the real values in the model's answer?

Every fake is recorded in the vault next to the real value. When the response comes back, including streamed SSE events and tool-call arguments, Shadow replaces each fake with its real value before your app sees it.

Can the AI provider reverse Shadow's fakes?

No. Fakes are generated randomly from the persona, not derived from the real value, so there is nothing to decrypt or brute-force. The only link is the vault, which never leaves your machine.

Is pseudonymised data still personal data under GDPR?

For you, yes: you hold the key, so Recital 26 treats it as personal data. Pseudonymisation is still a recognised safeguard under Articles 25 and 32. Whether masked text counts as personal data for the recipient is a legal question; ask your DPO.

When should I use tokens or redaction instead of realistic fakes?

Use typed tokens such as [PERSON_0001] for bulk data in reports, so the model does not invent facts about fake people. Use redaction for values that must never leave in any form and never need to come back, such as secrets.