swapback
Your data stays yours. A local proxy that swaps names, emails, phones and keys for placeholders before a prompt reaches an LLM provider, and puts them back in the reply.
Quickstart
pip install swapback
swapback demo
swapback init && swapback-proxy
Python 3.11 or newer. swapback demo runs a fake model with made-up data: no key, no network. The proxy listens on
127.0.0.1:8787; point your client at it and change nothing else:
from openai import OpenAI
client = OpenAI(
base_url="http://127.0.0.1:8787/v1")
Set it up for a real provider
swapback init writes swapback.yaml and known.yaml. Put the names, emails, phones and IDs that must never reach a
provider in known.yaml (or build it from a contacts export with swapback known init --vcf contacts.vcf), set
upstream: in swapback.yaml, run swapback check-config, then start swapback-proxy. The full steps, including
OpenRouter and Anthropic base URLs, are in the guide.
Works with
- OpenClaw: tested end to end (2026.10.7) with one provider block; its
exec,web_fetch,web_search,browserandmessagetools keep placeholders by default. Config. - Any OpenAI-compatible client: OpenAI Chat Completions and Responses (the official SDKs, OpenRouter, LiteLLM clients). Set the base URL, change nothing else.
- Anthropic Messages: the Anthropic SDK, plain and streamed, including
tool_use. - Tool-policy recipes for Claude Code and Codex CLI: framework recipes.
How it works
flowchart LR
A["Your app or agent"] -- "Email Jane Q Doe" --> S["swapback (127.0.0.1:8787)"]
S -- "Email «PERSON_1»" --> P["LLM provider"]
P -- "Hi «PERSON_1» ..." --> S
S -- "Hi Jane Q Doe ..." --> A
- Swap. Values from your own list are matched in their common spellings (case, separators, escapes, nested JSON); detectors catch phones, emails, cards, IBANs and API keys you did not list. Each becomes a typed placeholder.
- Check. An egress re-check scans the outgoing request again. If anything listed is still there, or the scrub
fails, the request is blocked (
503). It fails closed. - Restore. Placeholders in the reply are put back byte-exact, in plain and streamed replies and in tool-call
arguments. Network, shell and send-style tool calls keep the placeholder unless you allow the tool, so a
prompt-injected
web_fetchcannot carry your email out.
No model to download and nothing probabilistic: exact matching plus patterns, about 5,900 lines of Python, 3
dependencies. Re-run the leak test yourself with swapback selftest (made-up data, 7 request paths, about 10 s).
Documentation
| Topic | Where |
|---|---|
| Why swapback, setup, agents, tool calls, configuration, hooks | docs/guide.md |
| What it does not catch, and security notes | docs/limits.md |
| Leak test, performance, detection score | docs/testing.md |
| Comparison with PasteGuard, Maskit, Veil, piighost and Kiji | docs/comparison.md |
| Changes per release | CHANGELOG.md |
| Reporting a vulnerability | SECURITY.md |
Project facts: Python 3.11+, about 5,900 lines (302 KB), 3 dependencies (PyYAML, phonenumbers, pyahocorasick), stdlib HTTP server, MIT.
Development
pip install -e ".[test]"
pytest -q # the full suite, including the holdout score as a regression floor
python evals/run_holdout.py # the detection score table in docs/testing.md (--misses lists the cases not caught)
python examples/smoke.py # end-to-end: mock upstream + proxy, plain and streamed
The vendored rule sets live
in swapback/vendor (see THIRD_PARTY_NOTICES.md); tools/build_vendor.py regenerates the Presidio patterns.
See CONTRIBUTING.md and the changelog.
Roadmap
Restore Anthropic Messages streams.Done in 0.1.1.Catch separator and case variants of listed values (NBSP,Done in 0.1.1..,_,-,%20, all caps) under one placeholder.Restore OpenAI Responses API streams.Done in 0.3.0.Keep placeholders in tool calls that could send data out.Done in 0.3.0.Build the known-values file from a contacts export.Done in 0.3.0 (swapback known init).- Optional, pluggable NER for names you never listed, off by default.
- More national ID and address patterns, with tests from synthetic data.
Release on PyPI.Done:pip install swapback.Hooks for spend caps, local fallback and usage accounting.Done in 0.4.0.A public, re-runnable leak test.Done in 0.4.2 (swapback selftest).
Ideas and pull requests are welcome; open an issue first for anything large.
License
MIT, see LICENSE. Bundled rule data: gitleaks (MIT) and Microsoft Presidio (MIT), see THIRD_PARTY_NOTICES.md.
Metadata
Release files for swapback 0.4.16
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| swapback-0.4.16.tar.gz | 399.5 kB | With provenance | Details for swapback-0.4.16.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| swapback-0.4.16-py3-none-any.whl | Python 3 | none | any | Details for swapback-0.4.16-py3-none-any.whl |
Total release size: 704.2 kB
Release files / swapback-0.4.16.tar.gz
| Download URL | swapback-0.4.16.tar.gz |
|---|---|
| Size | 399.5 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
12930fc1e53389efbda4bec4edf6798ea5aeb56dce9e3138c1853c5d55bf7c14
|
|
BLAKE2b-256 checksum How to use checksums |
43a0babe953a9fc106870dc1be8249153783fe35505d16546a97bfa0288ac0ea
|
| 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 Nov 11, 2026.
Transparency logRelease files / swapback-0.4.16-py3-none-any.whl
| Download URL | swapback-0.4.16-py3-none-any.whl |
|---|---|
| Size | 304.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
30b171d3ae83c8fd33b77534268ee1740157f0e36c941f83987761db4f0afff2
|
|
BLAKE2b-256 checksum How to use checksums |
4bfa06e55959408d1841ac056fe59ff4b91230beb1c51735397f846d18127c3e
|
| 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 Nov 11, 2026.
Transparency logSecurity for swapback 0.4.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.
Full provenance
Both files in this release have been uploaded with provenance metadata.
Attestation for swapback-0.4.16.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 Nov 11, 2026.
Transparency logA 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.