Skip to main content

Deterministic, offline Swiss/EU (FADP/GDPR) PII gateway: tokenize personal data before a cloud LLM sees it, restore it on the way back.

Project description

Sovereign Shield

Use any cloud LLM. Keep the personal data in Switzerland.

A deterministic, offline gateway for Swiss/EU (FADP / GDPR) personal data. It tokenizes structured identifiers locally — turning 756.1234.5678.97 into [AHV_1] — so a prompt can go to Gemini, Claude, or any model without a real identifier ever crossing the border, then restores the real values in the reply on the way back. Detection is regex + checksum, not ML: zero dependencies, zero latency, and it cannot be talked out of a match.

⚠️ Disclaimer. Sovereign Shield is an engineering utility that aids programmatic privacy mitigation. It is not an automated guarantee of regulatory compliance under the Swiss Federal Act on Data Protection (FADP) or the EU GDPR, and it is not legal advice. Context-dependent leak vectors (free-text names, encoded data, semantics) can still slip past a structural, deterministic layer. Use it alongside a DPIA where required, audit logs, and human review — as the outer, deliberately-dumb layer of a defence-in-depth stack.

See it live → shield.ars.md · deterministic, in-browser, no API key.

Install

pip install sovereign-shield-ch              # core: stdlib-only, zero dependencies
pip install "sovereign-shield-ch[gateway]"   # + the optional LangChain proxy

Requires Python 3.12+.

Quickstart

from sovereign_shield import SovereignShield

shield = SovereignShield()

raw = ("Guten Tag. Meine AHV-Nummer ist 756.1234.5678.97. Bitte die Praemie auf "
       "IBAN CH9300762011623852957 zurueckerstatten. Erreichbar unter "
       "+41 79 214 88 03 oder hans.muster@bluewin.ch.")

# 1. De-identify locally. `safe` is all that crosses the border.
safe, ctx = shield.sanitize(raw)
#   safe -> "Guten Tag. Meine AHV-Nummer ist [AHV_1]. Bitte die Praemie auf
#            IBAN [IBAN_1] zurueckerstatten. Erreichbar unter [PHONE_1] oder [EMAIL_1]."
print(ctx.audit())   # {'ch_ahv': 1, 'iban': 1, 'ch_phone': 1, 'email': 1}

# 2. Call any cloud LLM on the placeholders (it never sees a real value).
answer = call_your_llm(safe)

# 3. Restore the real values locally before serving the user.
result = shield.rehydrate(answer, ctx)
print(result.text)       # real AHV / IBAN / phone / email swapped back in
print(result.clean)      # True if the model didn't mangle a placeholder

sanitize is fail-closed: if any structured identifier would survive into safe, it raises DataLeakError instead of leaking. rehydrate is strict and deterministic, and reports any placeholder the model mangled or invented (result.leftover) so you never ship a broken [AHV_1 to a user.

Transparent LangChain proxy

With the [gateway] extra, wrap any LangChain chat model and call it as usual — sanitize-out and rehydrate-in happen under the hood:

from langchain_google_genai import ChatGoogleGenerativeAI
from sovereign_shield.gateway import ShieldedChatModel

llm = ShieldedChatModel(ChatGoogleGenerativeAI(model="gemini-2.5-flash", temperature=0.3))

reply = llm.invoke("Refund AHV 756.1234.5678.97 to IBAN CH9300762011623852957.")
print(reply.content)                                   # real values restored
print(reply.additional_kwargs["sovereign_shield"])     # {'kept_on_shore': 2, 'leftover': []}

Run it as a drop-in proxy

No code changes: run a stateless, OpenAI-compatible reverse proxy and point any OpenAI-compatible client at it. It sanitizes the prompt, forwards it to the real provider, and rehydrates the reply — your API key flows straight through and nothing is stored.

pip install "sovereign-shield-ch[proxy]"
sovereign-shield-proxy   # serves on :8000, forwards to https://api.openai.com/v1

Point your client's base URL at it (the key still goes to the real provider):

from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1")

Front a different provider, or run it as a container sidecar:

SOVEREIGN_UPSTREAM_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai sovereign-shield-proxy
docker build -t sovereign-shield-proxy . && docker run -p 8000:8000 sovereign-shield-proxy

Stateless (the token↔value map lives only for the request) and keyless (your Authorization header is forwarded upstream). Streaming ("stream": true) is supported — placeholders are rehydrated across SSE chunk boundaries, so a token split over two chunks ([AH + V_1]) is still restored correctly.

What it detects

Deterministic shape regex + checksum — the checksum rejects look-alikes so the guard never trips on a random 13-digit string. Separators are stripped first, so 756.1234.5678.97 and 756 1234 5678 97 validate identically.

Category Identifier Validation
ch_ahv Swiss AHV / AVS number EAN-13 check digit
iban CH / LI IBAN ISO-7064 mod-97
credit_card Card PAN Luhn
ch_phone Swiss phone shape only
email Email shape only
dob Date of birth off by default (bare dates false-positive)

Scope: structured identifiers only. Person names and street addresses are not detected — they need an NER model, which would forfeit the deterministic, zero-dependency guarantee. Plug your own via SovereignShield(extra_detectors=[...]) (see SpanDetector); overlapping spans are dropped fail-closed.

Not encoding-robust. A model that base64s or ciphers an identifier defeats the regex. Separator/whitespace reformatting is handled; encoding is not.

How it works

The thesis, proven in the K.E.V.I.N. red-team research this is extracted from: you can't close a data leak from inside the model — a jailbreak, a pretext, or a forced output schema will make it disclose. So you put a deterministic, offline boundary around the model instead. Sovereign Shield is that boundary, as a library: detect → tokenize → (model) → restore.

The browser demo ships a TypeScript port of the exact same detectors, kept byte-for-byte in parity with this Python source by a generated vector suite — so redaction on the client and on the server can never silently drift.

Development

pip install -e ".[dev]"
pytest                                        # unit + round-trip suite
ruff check . && ruff format --check . && mypy
python scripts/gen_shield_vectors.py --check  # Python parity vectors current
cd web && npm install && npm run parity       # TS shield reproduces them exactly

The web/ directory is the live demo (Next.js). See web/README.md.

Credits & license

Extracted from the K.E.V.I.N. adversarial-testing project; background in the FADP AI-gateway write-up. Licensed under Apache-2.0.

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

sovereign_shield_ch-0.3.0.tar.gz (35.8 kB view details)

Uploaded Source

Built Distribution

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

sovereign_shield_ch-0.3.0-py3-none-any.whl (27.6 kB view details)

Uploaded Python 3

File details

Details for the file sovereign_shield_ch-0.3.0.tar.gz.

File metadata

  • Download URL: sovereign_shield_ch-0.3.0.tar.gz
  • Upload date:
  • Size: 35.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.13

File hashes

Hashes for sovereign_shield_ch-0.3.0.tar.gz
Algorithm Hash digest
SHA256 d69a7cb7e72425c05073990c558d4579e7197cc856408a0c6b7b679643a89cc9
MD5 ae468f00d3f8d6c1970a3f5836f5779f
BLAKE2b-256 45328c9b5ee60fbd76038ab4158a8740d6bb78cc0424f7bfe4f2f3d93fe74456

See more details on using hashes here.

Provenance

The following attestation bundles were made for sovereign_shield_ch-0.3.0.tar.gz:

Publisher: release.yml on acoseac/sovereign-shield

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

File details

Details for the file sovereign_shield_ch-0.3.0-py3-none-any.whl.

File metadata

File hashes

Hashes for sovereign_shield_ch-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 0b79818d77748792da58b518ee3ac3813ee98b068946696a89ece8c6a6203138
MD5 6e87929b5ac16e8ea98ec6e2b33f56ca
BLAKE2b-256 eb88c417edeb0ab7d8062032173705f4345e211bb6c2485fadbfe9d84c3733e3

See more details on using hashes here.

Provenance

The following attestation bundles were made for sovereign_shield_ch-0.3.0-py3-none-any.whl:

Publisher: release.yml on acoseac/sovereign-shield

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