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). v1 covers non-streaming /v1/chat/completions; streaming (SSE) is rejected with a clear error for now.

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.2.0.tar.gz (33.6 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.2.0-py3-none-any.whl (26.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: sovereign_shield_ch-0.2.0.tar.gz
  • Upload date:
  • Size: 33.6 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.2.0.tar.gz
Algorithm Hash digest
SHA256 a1614504830c16ef978f114ece4d2e61139c4493da3586bc6d2fc69c51a321bf
MD5 6075c303c894830fcf51bf126e72d8f7
BLAKE2b-256 97043bb0013fe2989135feb7cd8cbffe909f54c13cd451aebd38e4b0852d5855

See more details on using hashes here.

Provenance

The following attestation bundles were made for sovereign_shield_ch-0.2.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.2.0-py3-none-any.whl.

File metadata

File hashes

Hashes for sovereign_shield_ch-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 23df890e01e331cd13de78b8d4f1129f320d9aaf4c1d869665900f58a582ab86
MD5 4ac5307947b7fc1744d9a67f09f8c00a
BLAKE2b-256 adca564f1e53dc784821a10901a3bb1e1532e0ec9bd181ac57e8879107d2892c

See more details on using hashes here.

Provenance

The following attestation bundles were made for sovereign_shield_ch-0.2.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