Configuration
All settings are environment variables prefixed PALENA_PSEUDONYMIZER_. The service validates them on startup and refuses to boot on invalid config. The full list is in the Environment variables reference; this page covers the choices that matter most.
Entities
PALENA_PSEUDONYMIZER_PRESIDIO_ENTITIES=PERSON,ORGANIZATION # defaultLOCATION is off by default. Masking a city (Paris → Riverside) replaces one real place with another real-sounding place, and the model then reasons from the wrong geography — wrong weather, wrong currency, "which Riverside do you mean?". Person and organization names are pure identity and safe to swap; place names carry semantics the model needs. Add LOCATION back only if your threat model requires geography masking.
You can enable any entity type Presidio detects — add structured PII such as credit cards, national IDs, emails or phone numbers:
PALENA_PSEUDONYMIZER_PRESIDIO_ENTITIES=PERSON,ORGANIZATION,CREDIT_CARD,US_SSN,EMAIL_ADDRESSHow each type is substituted depends on its strategy.
Substitution strategy
Not all PII should be masked the same way. A fake name for a credit card is meaningless, and the model rarely needs the real digits — it just needs to know a value was there. So there are two strategies:
| Strategy | Output | Right for |
|---|---|---|
pool | a realistic fictional value (Alice Johnson → Jordan Avery) | nominal identities the model reasons about: PERSON, ORGANIZATION, LOCATION |
token | a consistent reversible placeholder (4111… → <CREDIT_CARD_1>) | structured PII: CREDIT_CARD, US_SSN, IBAN_CODE, EMAIL_ADDRESS, phone numbers, insurance / ID numbers, custom types |
# Default strategy for enabled types that aren't nominal identities:
PALENA_PSEUDONYMIZER_ENTITY_STRATEGY_DEFAULT=token # token | pool
# Per-type overrides (comma-separated TYPE:strategy):
PALENA_PSEUDONYMIZER_ENTITY_STRATEGY=PHONE_NUMBER:pool,CREDIT_CARD:tokenResolution order for a given entity type:
- an explicit
ENTITY_STRATEGYoverride, else poolif the type isPERSON/ORGANIZATION/LOCATION, elseENTITY_STRATEGY_DEFAULT(token).
So simply adding CREDIT_CARD to PRESIDIO_ENTITIES tokenizes it automatically — no strategy config needed for the common case. Tokens are <TYPE_N> where N is a per-type, per-session counter, so the same value maps to the same token across a conversation and reverses back to the exact original (case and all).
Detecting structured / country-specific PII
Presidio recognises many types out of the box (CREDIT_CARD, US_SSN, IBAN_CODE, EMAIL_ADDRESS, PHONE_NUMBER, US_PASSPORT, UK_NHS, …). Country-specific IDs (national identity cards, insurance numbers) usually need a custom recognizer — add a regex or deny-list recognizer the same way you add the organization deny-list.
Allow-list
Terms that should never be pseudonymized, even when Presidio detects them:
PALENA_PSEUDONYMIZER_ALLOW_LIST=Palena,Acme Rockets,Ada LovelaceMatched case-insensitively against the detected span. Use it for:
- your own brand / product names — you don't want "Palena" swapped for a fake org in every prompt;
- public figures or well-known entities that carry no client PII;
- words Presidio over-tags — e.g. the generic NER tagging "SSN" as an
ORGANIZATION(a false positive you saw in testing).
It's the symmetric counterpart to the organization deny-list: the deny-list adds detections, the allow-list suppresses them. Allow-listed terms are also excluded from first/last-name decomposition, so an allow-listed surname never leaks a sub-mapping.
Deterministic pseudonyms
By default a real name gets the next free pool pseudonym per session, so the same person may map to different pseudonyms in different conversations. Set a secret to make pool assignment deterministic — the same real value maps to the same pool pseudonym across every session:
PALENA_PSEUDONYMIZER_DETERMINISTIC_SECRET=<random-secret>The pseudonym is chosen by a keyed HMAC-SHA256(secret, name) over the pool, probing forward on a within-session collision. This gives stable, portable pseudonyms — useful when you correlate pseudonymized data across sessions or want reproducibility after a Redis flush. Trade-offs:
- Reversal still uses the session store (HMAC is one-way) — this changes which pseudonym is picked, not whether state is stored.
- Cross-session determinism means a fixed name↔pseudonym relationship, i.e. less per-session unlinkability. Only enable it if that's what you want.
- Applies to the pool strategy only; token entities keep their per-session counter. Treat the secret like any other credential.
Pseudonym pools
Each entity type draws pseudonyms from a pool:
PALENA_PSEUDONYMIZER_POOL_PERSON=Jordan Avery,Taylor Morgan,Alex Rivera,...
PALENA_PSEUDONYMIZER_POOL_ORGANIZATION=Acme Corp,Birch Industries,...The default PERSON pool is gender-neutral on purpose. A pseudonym carries an implied gender the model reasons from — map a female name to "Thomas" and the model writes "he", leaking a wrong attribute. Unisex names (Jordan, Taylor, Alex, Riley…) give the model no strong prior, so it says "they" or asks. This is pure pool curation; there is no gender logic in the service.
Person-name decomposition
PALENA_PSEUDONYMIZER_DECOMPOSE_PERSON_NAMES=true # defaultWhen on, mapping "Alice Johnson → Jordan Avery" also records "Alice → Jordan" and "Johnson → Avery", so a model that refers to just the first name still reverses correctly and later turns stay consistent. A real surname that is also a common noun (e.g. "Baker") can over-match; set this to false if your inputs contain many such names.
Images
PALENA_PSEUDONYMIZER_NON_TEXT_PII_ACTION=redact # redact | block | passthrough- redact (default) — forward a redacted copy of the image.
- block — reject the request with a user-facing message.
- passthrough — forward the original untouched (not recommended).
Failure behaviour
The guardrail is fail-closed on the way to the model: if Presidio or Redis is unreachable during the pre-call phase, the request is blocked so real names cannot leak. On the post-call phase it is best-effort — a reversal failure never turns a successful LLM response into an error.
Operators control the final policy with LiteLLM's unreachable_fallback (fail_closed recommended).
Authentication
PALENA_PSEUDONYMIZER_API_KEY=<shared-secret>When set, clients (i.e. the LiteLLM proxy) must send it as an x-api-key header. When empty, the service accepts any request — fine for a private in-cluster deployment, not for an exposed one.
Session erasure
Mappings expire on their own via the Redis TTL, but you can erase one on demand — e.g. to honour a GDPR right-to-erasure request or tear down a finished conversation:
curl -X DELETE http://pseudonymizer:8080/sessions/<session_id> \
-H "x-api-key: $PALENA_PSEUDONYMIZER_API_KEY"Returns 200 {"deleted": true|false} — deleted reports whether a mapping existed (the call is idempotent). The endpoint is guarded by the same API_KEY shared secret as the guardrail endpoint; leave the key unset only in a trusted, in-cluster deployment.