Skip to main content

Fast local PII pseudonymization for text, JSON and LLM payloads.

Project description

Pseudonymize

Local-first semantic pseudonymization for text, JSON payloads, documents, and LLM applications.

Detect once, transform in different ways.

I am Paolo Mazza and my email is paolo@example.com.
              ↓
I am <PERSON_1> and my email is <EMAIL_1>.

Person detection requires an optional local NER or custom detection backend. The dependency-free core detects structured entities such as the email in this example. Pseudonymize processes data locally by default; remote processing is disabled unless explicitly configured in a future backend.

The 0.1.0a1 API is a prerelease and may change before 0.1.0.

Installation

pip install pseudonymize

The base package has no runtime dependencies and supports Python 3.11 or newer.

Thirty-second example

from pseudonymize import pseudonymize

safe_text = pseudonymize("Email paolo@example.com")

assert safe_text == "Email <EMAIL_1>"

Numbered semantic aliases are the default. Numbering starts from one for every convenience call.

Transformation modes

Mode Example Identity behavior
numbered <PERSON_1> Stable for each normalized entity inside one scope
generic <PERSON> Does not distinguish entities of the same type
deterministic <PERSON_K8M42PX7D3Q> Stable across calls for the same key and namespace
redacted [REDACTED] Removes type and identity distinction
from pseudonymize import Pseudonymizer, TransformationMode

engine = Pseudonymizer(mode=TransformationMode.GENERIC)
assert engine.process("paolo@example.com").text == "<EMAIL>"

deterministic = Pseudonymizer(
    mode=TransformationMode.DETERMINISTIC,
    key=b"a-32-byte-or-longer-secret-key...",
    namespace="customer-42",
)

Typed redaction is available with redact(text, typed=True), producing values such as [REDACTED_EMAIL].

Alias scope

engine.process(...), like the convenience function, uses a fresh alias scope for each call. Use an explicit scope when multiple texts must share identity numbering:

from pseudonymize import Pseudonymizer

scope = Pseudonymizer().new_scope()
first = scope.process("paolo@example.com")
second = scope.process("maria@example.com and paolo@example.com")

assert first.text == "<EMAIL_1>"
assert second.text == "<EMAIL_2> and <EMAIL_1>"

process_batch and each process_data call also use one shared scope across their complete input.

Optional reversible mapping

Mappings are disabled by default and are available only for numbered and deterministic modes:

result = Pseudonymizer().process("paolo@example.com", include_mapping=True)

assert result.mapping == {"<EMAIL_1>": "paolo@example.com"}
assert result.restore("Reply to <EMAIL_1>.") == "Reply to paolo@example.com."

Mappings contain personal or sensitive data. They are excluded from repr(result), never logged or persisted by the package, and must be protected separately by the application.

LLM payloads

from pseudonymize import Policy, Pseudonymizer

engine = Pseudonymizer(policy=Policy.llm())
payload = {
    "messages": [
        {"role": "user", "content": "Email paolo@example.com"},
        {"role": "user", "content": "Use paolo@example.com again"},
    ],
    "temperature": 0.2,
}

safe_payload = engine.process_data(payload)

Both occurrences become <EMAIL_1>. Container structure and non-string values remain unchanged, and the input is not mutated.

Detection backends

The core includes validated rules for email addresses, phone numbers, IPv4 and IPv6 addresses, IBANs, payment cards, URL credentials, sensitive URL query values, and common credential formats.

PERSON, ORGANIZATION, and LOCATION are supported entity types but require an optional backend:

engine = Pseudonymizer(backends=[RulesBackend(), local_ner_backend])

No person detector based on capitalization heuristics is included. Exact normalized matches such as Paolo Mazza and Paolo Mazza share an alias, but ambiguous references such as Paolo and Mr. Mazza are not merged automatically.

Security and limitations

Pseudonymize performs pseudonymization, not guaranteed anonymization. Pseudonymized data can remain personal data. Detection has false positives and false negatives, and contextual information can remain identifying. Logs, traces, retrieved documents, tool inputs, and tool outputs need the same protection as prompts.

Deterministic mode uses HMAC-SHA256 over a versioned, domain-separated input. It requires a key of at least 32 bytes; the package never generates or persists one silently. Different tenants should use different keys or namespaces.

Migration from the initial prototype

  • Numbered mode is now the default. Use mode="deterministic" when providing key and namespace.
  • Generic mode produces typed placeholders such as <EMAIL>.
  • redact(...) now produces [REDACTED]; use typed=True for [REDACTED_EMAIL].
  • Existing <PZ1:TYPE:IDENTIFIER> placeholders remain protected from reprocessing.

Roadmap and contributing

The full local-first document and backend plan is in VISION.md and ROADMAP.md. See CONTRIBUTING.md before submitting changes.

Licence

Apache-2.0. Optional model licences are reviewed and documented separately.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pseudonymize-0.1.0a1.tar.gz (122.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pseudonymize-0.1.0a1-py3-none-any.whl (28.0 kB view details)

Uploaded Python 3

File details

Details for the file pseudonymize-0.1.0a1.tar.gz.

File metadata

  • Download URL: pseudonymize-0.1.0a1.tar.gz
  • Upload date:
  • Size: 122.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pseudonymize-0.1.0a1.tar.gz
Algorithm Hash digest
SHA256 73e26eb153f2d2f10717c9a85d0911b97648f2a2ae363a9ceb7944054d2765be
MD5 292e607000b74f5b00abba0df8a8b574
BLAKE2b-256 0d6e17077d6e22fec44eb665df691aed75308a462cf3200a3980c0ad38e5b935

See more details on using hashes here.

Provenance

The following attestation bundles were made for pseudonymize-0.1.0a1.tar.gz:

Publisher: release.yml on ma2za/pseudonymize

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pseudonymize-0.1.0a1-py3-none-any.whl.

File metadata

  • Download URL: pseudonymize-0.1.0a1-py3-none-any.whl
  • Upload date:
  • Size: 28.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.14

File hashes

Hashes for pseudonymize-0.1.0a1-py3-none-any.whl
Algorithm Hash digest
SHA256 13823ef30bfb6e6998c5a73a238eacd66875d4a5f2ddb0d98319301af224838e
MD5 3d57cee50523fad5fc3232ab0c59394a
BLAKE2b-256 41e30d1998c9bfeeb5f68a9cbcc1ecba30a97c4717cea8e6ed6b54b2cfd448f8

See more details on using hashes here.

Provenance

The following attestation bundles were made for pseudonymize-0.1.0a1-py3-none-any.whl:

Publisher: release.yml on ma2za/pseudonymize

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page