Skip to main content

pii-airlock

Keep real personal data out of AI prompts in minutes — local, reversible, and provider-agnostic.

CI PyPI Python Platforms Ruff License: MIT

2-min quickstart · Install · Gateway · CLI · doctor · Troubleshooting · All agents →

pii-airlock is a local privacy layer for AI tools. It replaces personal data with placeholders before requests leave your machine, then restores originals in responses.

Example:

John Smith (john@acme.com) → <PERSON_482913> (<EMAIL_ADDRESS_093117>)

Runs on macOS, Linux, Windows (Python ≥ 3.10). Built on Microsoft Presidio.


Why people use pii-airlock

Problem What pii-airlock gives you
You use multiple AI clients/providers One local gateway URL for all of them
You need reversible anonymization Stable placeholders + local mapping file
You use Claude Code tools/files Native hooks for prompt + tool-data checks
You want to avoid vendor lock-in Provider adapters: OpenAI-style, Anthropic, Gemini

Detection happens locally. Gateway traffic is forwarded only after PII is replaced.


2-minute quickstart

If you just want the safest default for most users:

pipx install "pii-airlock[proxy]"
pii-airlock init
pii-airlock proxy

Then set your client base URL:

export OPENAI_BASE_URL=http://127.0.0.1:8745/openai

PowerShell:

$env:OPENAI_BASE_URL = "http://127.0.0.1:8745/openai"

Now prompts are scrubbed before provider calls, and responses are restored automatically.


Install

Requires Python ≥ 3.10 and pipx (recommended) or pip. Works the same on macOS, Linux and Windows.

pipx install "pii-airlock[proxy]"   # recommended (gateway included)
pii-airlock init                    # guided setup (models + env hints)

On macOS or Linux with Homebrew (installs the CLI, hooks and gateway in their own virtualenv; the optional gliner, docx and pdf extras need pipx):

brew install matinfo/tap/pii-airlock
pii-airlock download-models         # spaCy models, downloaded once

Latest from source (before a release lands on PyPI):

pipx install git+https://github.com/matinfo/pii-airlock

Optional format support (plain text, CSV and JSON work out of the box):

pipx inject pii-airlock 'pii-airlock[docx]'   # Word .docx
pipx inject pii-airlock 'pii-airlock[pdf]'    # PDF (text extraction)
pipx inject pii-airlock 'pii-airlock[all]'    # everything

If you installed without gateway support and need it later:

pipx inject pii-airlock 'pii-airlock[proxy]'

If download-models reports that your interpreter has no pip (common in pipx), run the printed pipx inject pii-airlock "<model-wheel-url>" commands. The command now prints exact model wheel URLs for you.

doctor health checks

pii-airlock doctor

This checks proxy dependencies, model installation, and mapping roundtrip in one command.

Quick verification

echo "Contact John Smith at john@example.com" \
  | pii-airlock scrub --map /tmp/test.pii-map.json -o /tmp/scrubbed.txt
cat /tmp/scrubbed.txt   # Contact <PERSON_482913> at <EMAIL_ADDRESS_093117>
pii-airlock restore /tmp/scrubbed.txt --map /tmp/test.pii-map.json

Universal gateway (any provider)

The gateway is the easiest mode for non-technical users: point your AI client at localhost, and pii-airlock handles scrub/restore automatically.

  your app ──http──▶ pii-airlock gateway ──https──▶ provider API
           ◀───────────  (restore)  ◀───────────  (scrub)
# already included if you installed with "pii-airlock[proxy]"
# pipx inject pii-airlock 'pii-airlock[proxy]'
pii-airlock proxy            # listens on http://127.0.0.1:8745

Then set the base URL in whatever client you use:

# OpenAI SDK / Codex CLI / most OpenAI-compatible tools
export OPENAI_BASE_URL=http://127.0.0.1:8745/openai

# Anthropic SDK
export ANTHROPIC_BASE_URL=http://127.0.0.1:8745/anthropic

# Google Gemini — use base path
#   https://generativelanguage.googleapis.com  ->  http://127.0.0.1:8745/gemini

Why this is practical: configure once, keep existing clients, and avoid changing prompt habits.

  • No TLS interception. Your client talks plain HTTP to localhost; the proxy makes the real HTTPS call upstream. No certificates to install. Bind stays on 127.0.0.1 by default.
  • Auth passes through untouched and pii-airlock never logs request headers or bodies. (uvicorn runs at log level warning, so request lines aren't logged either.)
  • A fresh in-memory mapping per request — the gateway writes no PII to disk (only the stable-token key, see Tokens).
  • Concurrency-safe: detection is serialized with a lock, so the engine is shared safely across simultaneous requests.

Provider coverage — three wire formats, each with an adapter in pii_scrub/payload.py:

Route Wire format Covers Streaming
/openai Chat Completions, Responses, Completions, Embeddings, Moderations OpenAI, Codex, Cursor, Continue, Ollama, LiteLLM, vLLM, … SSE ✅
/anthropic Messages (incl. count_tokens, batches) Claude SDKs, Claude-compatible tools SSE ✅
/gemini generateContent, countTokens, embedContent Google Gemini SSE ✅ · array-stream buffered

Tool traffic is covered too: tool results / function outputs (where file contents and command output live) and tool-call arguments are scrubbed on the way out; tool-call arguments the model emits are restored on the way back.

Fail closed. A request body the gateway doesn't know how to scrub — an unknown endpoint (file uploads, audio, …) or a body that isn't JSON — is refused (403/400) instead of being forwarded. Opt specific paths out with passthrough_paths if you know they carry no personal data.

Need setup for a specific client? Use AGENTS.md (Claude Code, Codex, Cursor, Continue, Aider, Gemini).


CLI usage

Reversible scrub → restore pipe

# 1. Scrub — replaces PII with tokens, saves a mapping file
echo "Contacte Jean Dupont à jean@acme.fr" \
  | pii-airlock scrub --map /tmp/m.pii-map.json
# → Contacte <PERSON_558644> à <EMAIL_ADDRESS_094087>

# 2. Send the scrubbed text to your LLM …

# 3. Restore — swap tokens back in the model's response (use your own tokens)
echo "J'ai répondu à <PERSON_558644> via <EMAIL_ADDRESS_094087>." \
  | pii-airlock restore --map /tmp/m.pii-map.json
# → J'ai répondu à Jean Dupont via jean@acme.fr.

Tokens

Same value, same token, so the model still sees coreference. By default tokens are stable: <PERSON_558644> is a keyed hash (HMAC-SHA256) of the value, so it is the same in every request, file and process — across turns, subagents, trimmed or compacted histories — and prompt caches keep hitting. The key is created on first use in ~/.local/state/pii-airlock/token.key (owner-only) and never leaves your machine; without it a token reveals nothing about its value.

  • token_rotation: day (default) | week | never — how long a value keeps its token. Tokens cannot be linked across periods, so a provider cannot follow one person through all your conversations. Use never to scrub a dataset in several runs.
  • token_style: surrogate — realistic fakes instead of tokens, keyed the same way: Jean Dupont → Jasper Iverson, jean.dupont@acme.fr → jasper.iverson@example.com; phones, cards (Luhn-valid), IBANs (valid checksum) keep their shape, IPs come from the documentation ranges. Text reads naturally, which some models and tools handle better than <TOKENS>. A surname alone gets the surname of the full fake, and restores alone too. Other entities (IDs, secrets, places) keep stable tokens. Trade-off: a fake the model writes in another sense (a fake first name alone, for someone else) is restored as well.
  • token_style: sequential — <PERSON_1>, <PERSON_2> in reading order, per request or file (the previous default).
  • Delete token.key to rotate the key immediately.

Detect without changing text

pii-airlock detect notes.txt
# PERSON           0.85  [9:21]    'Jean Dupont'
# EMAIL_ADDRESS    0.99  [24:38]   'jean@acme.fr'

File formats

pii-airlock scrub report.csv  -o report.scrubbed.csv
pii-airlock scrub data.json   -o data.scrubbed.json
pii-airlock scrub contract.docx -o contract.scrubbed.docx   # requires [docx]
pii-airlock scrub scan.pdf                                   # requires [pdf] → text on stdout

Other options

pii-airlock scrub prompt.txt --no-map          # irreversible one-way scrub
pii-airlock scrub --lang fr,en                 # explicit language list
pii-airlock scrub --threshold 0.7              # raise confidence cutoff
pii-airlock scrub input.txt -o out.txt --map secrets.pii-map.json

Claude Code hooks

As a plugin (one step)

/plugin marketplace add matinfo/pii-airlock
/plugin install pii-airlock@pii-airlock

Installs the same hooks (PreToolUse, UserPromptSubmit) plus a /pii-airlock:setup skill that walks Claude through checking the install, downloading models and setting a policy. The plugin cannot install the Python CLI, so install that first (brew install matinfo/tap/pii-airlock or pipx install "pii-airlock[proxy]"). Fails closed: while the CLI is missing, every prompt and tool call is blocked (a SessionStart message says why); PII_AIRLOCK_PLUGIN_FAIL_OPEN=1 lets them through. Use the plugin or install-hook, not both, or each call is scanned twice. The launcher is a POSIX shell script: on Windows use install-hook.

With install-hook

Register guardrails that intercept PII before it reaches the model:

pii-airlock install-hook                  # both events, project .claude/settings.json
pii-airlock install-hook --scope user     # ~/.claude/settings.json (all projects)
pii-airlock install-hook --event tool     # PreToolUse only
pii-airlock install-hook --event prompt   # UserPromptSubmit only
Leak vector Covered by Default action
PII you type in a prompt UserPromptSubmit block, shows a scrubbed version to send instead
PII in a text file Claude reads (the hook scans the file content) PreToolUse (Read) ask
PII in a shell command, file write/edit, search pattern, URL or web query PreToolUse ask
PII in MCP tool arguments PreToolUse (mcp__*) ask

The hooks detect and block/ask — they don't silently rewrite payloads (the hook API doesn't support in-place rewriting). For a silent, reversible rewrite, use the CLI pipe above.

  • hook_decision: deny blocks tool calls instead of asking.
  • prompt_decision: warn sends prompts containing PII anyway, with a warning (Claude Code has no "ask" for prompts: it's block or send).
  • If detection can't run (e.g. missing spaCy model), the hooks fail closed: the input is treated as containing PII. Set hook_on_error: open to skip checks instead.

Speed: the hook daemon. Claude Code starts a new process for every hook call, and loading the spaCy models costs 0.5–3 s each time. With the default hook_daemon: auto, the first hook call starts a small background daemon that keeps the models loaded; later calls answer in ~60 ms. It listens on a Unix socket (mode 0600, in a directory only you can write to), reloads your project policy on every call, and exits after hook_daemon_idle_minutes (30) idle. Nothing leaves your machine.

pii-airlock daemon status     # also shown by `pii-airlock doctor`
pii-airlock daemon start      # warm it up now (e.g. from your shell profile)
pii-airlock daemon stop

While no daemon answers, hooks analyze in-process (hook_fallback: inprocess). With hook_fallback: regex they use patterns only, in milliseconds: emails, phones, IBANs, cards, IPs, FR/CH IDs, secrets and your deny_list — names and places are not detected in that mode. hook_daemon: off disables the daemon. Windows: no daemon (hooks analyze in-process).

Not covered by hooks: the output of a command or tool (e.g. cat in Bash, Grep matches, a fetched web page) reaches the model unchecked, as do binary files (images, PDFs). For full coverage, route the traffic through the gateway.


Configuration

Override chain (lowest → highest priority):

bundled defaults → ~/.config/pii-airlock/config.yaml → ~/.claude/skills/pii-airlock/SKILL.md
  → ./.pii-airlock.yaml → ./.claude/skills/pii-airlock/SKILL.md → CLI flags

allow_list, deny_list and custom_patterns accumulate across layers (your personal allow list + the project's); every other key is overridden.

Default config (config.default.yaml):

languages: [en, fr]
models:
  en: en_core_web_lg
  fr: fr_core_news_lg
score_threshold: 0.5
entities: []          # empty = all entities Presidio recognizes, minus exclude_entities
entity_actions: {}    # per type: tokenize | redact | allow | block (see Per-project policy)
exclude_entities: [DATE_TIME, URL, NRP, US_DRIVER_LICENSE, US_BANK_NUMBER,
                   US_PASSPORT, IN_VEHICLE_REGISTRATION]  # noisy in code; [] keeps all
score_thresholds: {}  # per-entity minimum, e.g. {PERSON: 0.7}
builtin_recognizers: true  # FR/CH IDs, FR/CH phone formats, secrets (see below)
analysis_cache_size: 2048  # texts remembered per language (digest + offsets, never the text); 0 = off
hook_daemon: auto          # keep models loaded between hook calls (off: never)
hook_fallback: inprocess   # no daemon answer: load models (inprocess) or patterns only (regex)
hook_decision: ask    # tool calls: ask (surface + confirm) | deny (block)
prompt_decision: block  # prompts: block (show scrubbed version) | warn (send anyway)
hook_on_error: closed   # detection failure: closed (treat as PII) | open (skip)
hook_max_file_bytes: 2000000  # larger files read by Claude count as a detection failure
passthrough_paths: []   # gateway: path fragments whose bodies may be forwarded unscrubbed

# Advanced (proxy runtime):
mapping_backend: memory   # memory (default) | file
# mapping_dir: /var/lib/pii-airlock/maps

For higher-volume/self-hosted setups, mapping_backend: file can be useful for debugging or operational inspection. memory stays the safest default for local use because mappings are ephemeral.

Mapping files hold the real values, so the file backend encrypts them at rest (AES-256-GCM, key derived from token.key in the state directory). A copied or backed-up maps/ directory is then useless without that key: keep token.key out of backups, and note that losing it makes pending mappings unreadable (they only live for one request). Plain files from older versions still load. Set mapping_encryption: false to write readable JSON for debugging; this is ignored in an untrusted project's config. Files from pii-airlock scrub --map stay plain JSON, as you restore them by hand.

Built-in recognizers

On top of Presidio, pii-airlock ships checksum-validated recognizers that work in every language and need no model:

Entity What Validation
FR_NIR French social security number mod-97 key (incl. Corsica 2A/2B)
CH_AHV Swiss AHV/AVS number 756.xxxx.xxxx.xx EAN-13 check digit
FR_SIRET / FR_SIREN French business IDs (sole traders are people) Luhn; SIREN needs the keyword
DE_TAX_ID German tax ID (Steuer-IdNr.), needs the keyword (Steuer-ID, IdNr) ISO 7064 MOD 11,10
DE_TAX_NUMBER German Steuernummer (21/815/08150), needs the keyword format
IT_VAT_CODE Italian Partita IVA, with the IT prefix or a P.IVA keyword Luhn
PHONE_NUMBER International +CC … in any language; FR/CH/DE/IT national formats (06 12 34 56 78, 079 123 45 67, 030 12345678, 333 1234567) format
SECRET AWS/GitHub/Slack/OpenAI/Anthropic/Google/Stripe keys, JWTs, private keys, URL passwords, password=… values format

Exclude any of them with exclude_entities, or all with builtin_recognizers: false.

Per-project policy (Claude Code skill)

The project policy lives in .claude/skills/pii-airlock/SKILL.md. Claude Code loads it as a skill (rules for handling personal data, what to do when a hook blocks), and pii-airlock reads its fenced yaml pii-airlock block as config.

pii-airlock policy init                  # create the skill for this project
pii-airlock allow "Acme SA"              # never PII here (your company, products)
pii-airlock deny "Project Phoenix" --as CODENAME   # always sensitive here
pii-airlock allow "Jane Roe" --scope user          # personal, all projects
pii-airlock policy show                  # effective policy and where it comes from
allow_list: [Acme SA]
deny_list:
  CODENAME: [Project Phoenix]
custom_patterns:
  - {entity: CONTRACT_ID, regex: 'CT-\d{6}'}

Hooks apply the policy of the project Claude Code is working in. Every detection message shows the one-command fix for a false positive.

Trust. A project's policy comes with the repo, and a policy can loosen protection (allow_list, hook_on_error: open, entity_actions: {PERSON: allow}...). So for a project you have not trusted, only settings that tighten apply (deny-list terms, custom patterns, block / redact, deny / block / closed decisions). Your own allow, deny, action and policy init edits keep a trusted project trusted; after a git pull that changes the policy, review it and run pii-airlock trust. pii-airlock policy show says what is being ignored. User-level config is always trusted.

What happens to each entity type

By default every detected entity is tokenized (reversible). Choose per type:

Action Effect
tokenize token or fake, restored in the response (default)
redact irreversible [IBAN_CODE]; the value is never kept, not even in a mapping file
allow left as is and not flagged (what exclude_entities does for its list)
block the gateway answers 403 and sends nothing, scrub writes nothing, hooks deny (even with hook_decision: ask) and block prompts (even with prompt_decision: warn); messages name the type and count, never the value
pii-airlock action IBAN_CODE redact          # this project
pii-airlock action CREDIT_CARD block --scope user
entity_actions:
  IBAN_CODE: redact
  SECRET: block        # API keys and passwords never leave the machine
  LOCATION: allow

Layers accumulate (user policy + project policy); an explicit action beats exclude_entities. Names found elsewhere in the text keep following their person: with EMAIL_ADDRESS: allow, marie@acme.fr stays but a bare marie that matches a detected "Marie Dubois" is still tokenized.

Adding a language

python -m spacy download de_core_news_lg
# .pii-airlock.yaml
languages: [en, fr, de]
models:
  de: de_core_news_lg

One multilingual model (GLiNER)

Instead of a spaCy model per language, GLiNER (urchade/gliner_multi_pii-v1) finds people, organizations and places in any language with one model. Emails, phones, IDs and secrets are still found by patterns.

pipx inject pii-airlock 'pii-airlock[gliner]'   # or: pip install 'pii-airlock[gliner]' (PyTorch)
pii-airlock download-models                     # with ner_backend: gliner, fetches the ~1.1 GB model
ner_backend: gliner

On the bundled EN+FR eval corpus (python -m tests.eval.run_eval --backend gliner):

spaCy _sm models GLiNER
PERSON recall / precision 0.96 / 0.68 1.00 / 0.95
All entities, precision 0.67 0.98
Time per message (CPU) ~6 ms ~40 ms

Fewer false positives means fewer needless hook prompts. Long texts are scanned in overlapping windows (GLiNER itself truncates past a few hundred words). Organizations and places must be capitalized to count, which drops common nouns GLiNER tags ("la société", "client"); names are kept in any case. With the hook daemon the model is loaded once.


Guarantees & limitations

What pii-airlock guarantees

  • Reversibility is exact. Any value the engine tokenized is restored byte-for-byte via the mapping. restore(scrub(text)) round-trips for tokenized spans.
  • Determinism. The same value gets the same token within a mapping (and, with stable tokens, across requests), so coreference is preserved for the model.
  • Found once, scrubbed everywhere. A value detected anywhere in a request or file — and the parts of a person's name — is scrubbed at every other mention, earlier ones included, even where the model missed it.
  • Tokens are opaque & safe. Restored values are re-inserted through proper JSON encoding; values containing quotes, newlines or <…> won't corrupt payloads.
  • Local-only detection. Detection never makes a network call. Only the gateway forwards (already-scrubbed) traffic onward.

What it does not guarantee — read this

  • Detection is best-effort, not complete. Presidio + spaCy are statistical; they miss and mis-tag entities (more so with _sm models, or for phone numbers with odd spacing). pii-airlock reduces exposure — it is not a guarantee that every piece of PII is removed. Review sensitive material; raise score_threshold or add custom recognizers as needed.
  • Gateway scope. Text in generation, embedding and token-count requests is scrubbed, including tool results and tool-call arguments. Images, audio and file attachments inside a request are forwarded as-is (not inspected). Other endpoints with a body are refused unless listed in passthrough_paths. Anthropic thinking blocks are signed, so they are neither scrubbed nor restored.
  • All configured languages are analyzed for every text and the results merged, so mixed EN/FR text is covered — at the cost of one NER pass per language.
  • Gemini array streaming (without alt=sse) is buffered, then restored as one response rather than streamed live.
  • .docx scrubbing rewrites changed paragraphs into a single run, so inline formatting within those paragraphs is not preserved. PDF is extract-only → scrubbed text out (no PDF re-render).

Platform support

Tested in CI on Linux, macOS and Windows (Python 3.10–3.14 on Linux; 3.12–3.13 on macOS/Windows). One platform nuance:

  • Mapping files are created with mode 0600 on POSIX (macOS/Linux). On Windows chmod is a no-op; the file inherits your account's default ACLs — typically already user-private in a home directory. Treat mapping files as secrets regardless (below).

Troubleshooting

Run this first for a quick diagnosis:

pii-airlock doctor

.venv/bin/pytest: bad interpreter .../pii-scrub/...

Your venv points to an old repo path. Recreate it:

rm -rf .venv
python3 -m venv .venv
.venv/bin/python -m pip install -U pip
.venv/bin/python -m pip install -e '.[dev]'

No module named spacy when running python -m spacy ...

You are likely using system Python instead of the pii-airlock environment. Use:

pii-airlock download-models

download-models says this interpreter has no pip

This is normal in many pipx environments. Run the exact pipx inject command printed by download-models for each model wheel URL.

pii-airlock proxy fails with missing httpx / starlette / uvicorn

Install gateway dependencies:

pipx inject pii-airlock 'pii-airlock[proxy]'

OSError: [E050] Can't find model ... after download

Install the model wheel directly into the same environment where pii-airlock runs (the command prints the exact wheel URL in this case).


⚠ Security: the mapping file holds real PII

*.pii-map.json contains the original personal data in plain text.

  • Created owner-only (0600 on POSIX; default user ACLs on Windows — see above).
  • The bundled .gitignore excludes *.pii-map.json and *.pii-map.*.
  • Never commit mapping files.
  • Delete them when you no longer need to restore.
  • Use --no-map when reversibility isn't required.
  • scrub --encrypt-map writes the file encrypted (AES-256-GCM, key derived from token.key in your state directory); restore --map opens it with the same key automatically. Opt-in because such a file is only restorable on this machine, and only while token.key exists: keep token.key out of backups, never in the repo.
  • The gateway keeps its mapping in memory only and discards it after each response.

Development

git clone https://github.com/matinfo/pii-airlock
cd pii-airlock
pip install -e ".[dev]"
ruff check .
pytest -q

Unit tests stub out Presidio/spaCy and run entirely offline. Detection quality is measured separately on a labeled EN+FR+DE+IT corpus (fake data) with the real small models: python -m tests.eval.run_eval -v prints per-entity recall/precision, --gate fails below tests/eval/thresholds.json (also pytest -m eval). Add a case to tests/eval/corpus.jsonl when you fix a miss.

For a live end-to-end check, run pii-airlock download-models first, then:

echo "Call John Smith at john@example.com" | pii-airlock scrub --map /tmp/test.pii-map.json

Community

If pii-airlock helps you keep PII out of your AI tools, a ⭐ helps others find it.


License

MIT © pii-airlock contributors

Metadata

Release files for pii-airlock 0.5.0

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

Source distribution (sdist)

Source distribution for pii-airlock 0.5.0
File Size Uploaded Provenance Actions
pii_airlock-0.5.0.tar.gz 135.4 kB With provenance Details for pii_airlock-0.5.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for pii-airlock 0.5.0
File Interpreter ABI Platform Actions
pii_airlock-0.5.0-py3-none-any.whl Python 3 none any Details for pii_airlock-0.5.0-py3-none-any.whl

Total release size: 223.3 kB

Release files / pii_airlock-0.5.0.tar.gz

Download URL pii_airlock-0.5.0.tar.gz
Size 135.4 kB
Tags Source
SHA-256 checksum
How to use checksums
1783a630c82207ff3c40091d8bf9e00adf6440c8c72784fe268ee9fe1007c77a
BLAKE2b-256 checksum
How to use checksums
ed8cebcba5c2886f1baef2785d23e2b5fbc86001f1b82fc4941729b05a12741e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Release files / pii_airlock-0.5.0-py3-none-any.whl

Download URL pii_airlock-0.5.0-py3-none-any.whl
Size 87.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
9d1c0dcdae821bc8113527e70b53263c0537afb87a3e791c80a89de62361acf6
BLAKE2b-256 checksum
How to use checksums
2a4f154bebf2fc45029496bde0a683516d0e1c60111dead27db41f28b440c848
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

Security for pii-airlock 0.5.0

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.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for pii_airlock-0.5.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 31, 2026.

Transparency log

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