pii redaction · architecture · llm security · tokenization · concepts

Reversible PII redaction: how redact-then-restore works

How typed tokens and a retained vault support reply restoration, with practical checks for expired mappings, JSON output and tool authorization.

Godfrey Lebo Founder, Sether · updated · 8 min read
In this article
Conceptual Sether illustration: A stand-in goes out. Originals return. Sether™ / Field notes pii redaction A stand-in goes out. Originals return. Keep the mapping under application control.
Conceptual illustration with fictional data.
Direct answer

Reversible PII redaction replaces matched values with typed tokens before a provider request and keeps the token-to-original mapping in a vault you operate. When a reply contains recognised tokens, restoration puts those originals back. Unmatched text still passes through, and successful restoration depends on the matching vault remaining available.

TL;DR
  • One-way masks are useful when originals are not needed again. Reversible tokens suit workflows where a reply or final document needs those values.
  • Typed tokens preserve field hints, not a promise of unchanged model behavior. Keep redacted history and test whether the model preserves the replacements.
  • The default vault is in-process; a shared store is possible. Your storage configuration determines who can access mappings and how long they remain.
  • Redact and restore need access to the same retained mapping. One instance is the simplest setup; separate instances can share a vault.
  • If a destination has no justified need to recover originals, avoid keeping a restoration path merely because it is available.

Why does one-way masking break chat apps?

Classic redaction is one-way: find the email, replace it with [REDACTED], done. That is correct for logs and documents, where nobody needs the value back.

SetherThe idea, illustrated Your map. Your side.
Original → replacement → restored reply.

Replace detected details before sending. Restore recognized replacements using the matching retained map.

Fictional example. Detection can miss values; review the edited text. Follow one prompt step by step

A placeholder such as [REDACTED] removes detail, which is often the right goal. But a draft addressed to a customer may need an unambiguous reference to the value that your app will insert later. Typed, distinct tokens let the model refer to those fields without requiring the original strings.

Think of a coat check: the ticket refers to something held elsewhere. The analogy has a limit. A story about a distinctive coat may reveal its owner even without the ticket mapping. Likewise, tokenizing an email does not anonymize the rest of a customer’s message.

What makes a good redaction token?

Sether tokens look like <EMAIL_uuid>: a type prefix plus a unique identifier. Both parts do work.

  • The type prefix tells the model what kind of value sits there. It can still say "I will send the invoice to <EMAIL_uuid>" and reason about it as an email address.
  • The identifier is not an encoding of the original string. Resolving it uses the vault entry. Protect both the vault and the surrounding content; context can still reveal identity.
  • A fresh token for every match means repeated appearances of sarah@acme.com can produce different tokens. Each retained entry can restore to the same address. The model cannot infer equality from those token strings alone, although the surrounding text may connect them.

Where do the real values live?

In the vault: a key-value store mapping tokens to original values. Sether ships MemoryVault, an in-process LRU that holds 10,000 entries with a 1-hour TTL.

The vault interface is pluggable. Different processes can redact and restore through a compatible mapping store you implement, provided authorized entries remain available. The TypeScript Vault lookup contract is synchronous: an ordinary asynchronous Redis client is not a drop-in implementation, because restore cannot await per-token lookups. Design the storage boundary explicitly, including isolation, availability and retention.

Why must both directions share the mapping?

The default instance owns a MemoryVault. Redaction writes a mapping there and restoration reads it back. Reusing that instance is the simplest way to keep both directions connected.

A second instance with its own default vault has no access to the first instance’s entries. By default, unresolved tokens stay in the output; strict restore can reject them in supported releases. A second instance configured with the same vault is different: it can resolve mappings that are still available and authorized for that conversation.

How does this survive a multi-turn conversation?

The redacted history carries the conversation. Turn one redacts sarah@acme.com to a token. If you keep the redacted messages as the history you resend, then on turn three, when the user says "actually send it to that same address", the history still holds that exact token, so the model refers back to it and the reply restores to the real address. If you re-redact the plain history on every turn instead, each pass mints new tokens, so one address can appear as several different tokens in the same prompt. Replies still restore correctly, but the model has less to go on.

Keeping redacted history avoids repeatedly sending originals from earlier turns, but new messages and unmatched context still need review. Set the vault lifetime for your actual session behavior. The one-hour default is a configuration choice, not a guarantee that a delayed reply or a resumed conversation will still have its mapping.

What are the security properties?

  • Matched originals are withheld from that edited request. Unmatched values, attachments and identifying context are outside that statement.
  • A token is a reference, not anonymization proof. Its random identifier does not encode the original, but context or other data may identify the person.
  • The vault is sensitive application data. Restrict access, isolate tenants or conversations, and avoid copying mappings into ordinary logs.
  • Retention must cover the intended round trip. Expiry and eviction limit stored mappings but can also prevent restoration; test both outcomes.

To be precise about the claim: this is a technical measure that helps with data minimisation. It shrinks what leaves your boundary; it does not remove the need for processor agreements or a lawful basis, which we walk through in the GDPR post.

Failure to rehearse Expected handling
A mapping expired or was evicted Leave unresolved or raise in strict mode; do not fabricate a replacement.
The model invented or altered a token Reject or review it before a tool action.
A valid token appears in the wrong argument Apply authorization and field validation; a known token is not permission.
The reply is JSON Use JSON-aware restore or restore parsed string values, then validate the schema.

Restoration is part of the full MIT SDK and remains a Free essential in the official tools. Optional Pro workflows do not sell your originals back to you: tool restoration still depends on having the matching session mapping. Pricing explains the separate tool pilot.

When is reversibility the wrong choice?

Reversibility exists to serve a live user who needs the real value back in the reply. If no user is waiting, keeping a path back to the data is a liability, not a feature.

Destination Redaction mode Why
LLM prompt in a chat app Reversible A user is waiting for a reply that must read naturally
Application logs One-way Nobody should ever recover PII from a log line
Analytics and metrics pipelines One-way Aggregates need counts, not identities
Training or fine-tuning data One-way A restore path into a dataset defeats the purpose of redacting it
Data shared with third parties One-way You cannot control their handling of a reversible token stream

Choose retention by destination and purpose. A log scrubber may never need a reverse mapping; a live customer reply may. Do not keep originals in a vault by default if the workflow has no reason to recover them.

The round trip in code

In TypeScript, redact and restore are stream transforms on one instance. The full tutorial, including the drop-in OpenAI wrapper and SSE streaming, is here.

TypeScript: one instance, both directions
import { Sether } from '@raeven-co/sether';
import { Readable } from 'node:stream';

const sether = new Sether(); // holds the vault

// Outgoing: values become tokens
const safeForLLM = Readable.from([userMessage]).pipe(sether.redact());

// Incoming: tokens become values again, same instance
const safeForUser = llmResponse.pipe(sether.restore());

In Python, the same design with sync calls (async and streaming also exist; the Python walkthrough covers them).

Python: same instance, both directions
from sether import Sether

sether = Sether()  # holds the vault

safe = sether.redact_sync(user_message)   # values -> tokens
reply = call_llm(safe)
restored = sether.restore_sync(reply)     # tokens -> values

One subtlety worth knowing before you ship: restoring a streamed reply means tokens can split across chunks, and a naive restore misses them. Sether holds back a small window to stay boundary-safe; the details are in the chunk-boundary bug post.

Frequently asked questions

Is a redaction token the same as encryption?

No. A token identifies a vault entry rather than encrypting the original string. Protect the vault as sensitive data. Tokenization does not prevent someone inferring identity from the surrounding message or other available information.

What happens if a vault entry expires before the reply arrives?

It cannot be restored from that missing entry. Default restore leaves the token unresolved; supported strict modes raise an error instead. Decide whether to show a placeholder, ask the user to retry or stop the operation. Never guess an original value. See the JSON-safe restore release.

Can the LLM provider reverse the tokens?

The random identifier does not encode the original, and Sether does not send the vault to the model provider. That does not make the whole message anonymous: unmatched text and other context may still identify someone.

Should I use reversible redaction for my logs too?

Use reversibility only where a justified workflow needs originals back. For logs, analytics or shared datasets, assess whether retaining any reverse mapping is necessary and restrict both the content and its retention accordingly.

Godfrey Lebo

Written by Godfrey Lebo

Founder of Sether. Ships production software for a living and reads every comment below. More about the team.

Share: X LinkedIn Markdown

Comments

Emails and phone numbers in comments are hidden automatically. We take our own medicine.

No comments yet. Ask a question or share how you handle this. First comment gets our undivided attention.

Join the conversation

Questions, corrections and war stories are all welcome. Be kind, stay on topic.

By posting you agree to our terms. We store a one-way hash of your IP to fight spam, never the IP itself.