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': []}

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.1.0.tar.gz (27.7 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.1.0-py3-none-any.whl (22.6 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: sovereign_shield_ch-0.1.0.tar.gz
  • Upload date:
  • Size: 27.7 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.1.0.tar.gz
Algorithm Hash digest
SHA256 9713a1aaa80495fa50135907df7510120182a404311251f09f5dfcf65a0b2666
MD5 01db4d726fa3fed81bb039b36442d2e1
BLAKE2b-256 84f8306c1953eca7693acf279527982c701729a874b8407c85432daadadded4f

See more details on using hashes here.

Provenance

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

File metadata

File hashes

Hashes for sovereign_shield_ch-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8a66715a13eada045ad4a2084b1fcba60bfb1d1c67deb20ba7bed79864caee22
MD5 e1a520227c58f3e3448cd34808df5c9f
BLAKE2b-256 43afc9e59d0b14f1a5af1a8e8e78c6563fca3d40e6777a88c6cbe8435fc2c890

See more details on using hashes here.

Provenance

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