Newshot
Account

Keeping names, emails and phone numbers out of LLM prompts with a local proxy

Original text(via HN AI)

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.

Terminal: pip install swapback, then swapback demo. The app sends a made-up name, email and phone; the model receives PERSON_1, EMAIL_1 and PHONE_1 placeholders; the app gets the original values back. PASS.
Terminal: pip install swapback, then swapback demo. The app sends a made-up name, email and phone; the model receives PERSON_1, EMAIL_1 and PHONE_1 placeholders; the app gets the original values back. PASS.

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, browser and message tools keep placeholders by default. Config.
  • Hermes Agent: runs in front of the maintainer's own Hermes agent every day, on real email and calendar traffic.
  • 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_fetch cannot 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 12 s).

Documentation

TopicWhere
Why swapback, setup, agents, tool calls, configuration, hooksdocs/guide.md
What it does not catch, and security notesdocs/limits.md
Leak test, performance, detection scoredocs/testing.md
Comparison with PasteGuard, Maskit, Veil, piighost and Kijidocs/comparison.md
Changes per releaseCHANGELOG.md
Reporting a vulnerabilitySECURITY.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, ., _, -, %20, all caps) under one placeholder. Done in 0.1.1.
  • 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.

The text above was extracted automatically from the source page and may miss images, tables or code blocks. The original is authoritative.

Published by HN AI
This site only summarises and aggregates; copyright stays with the original author. Headlines and summaries follow the source — where they differ, the original wins.
Read the original ↗