A customer asks for a payment update. Their message includes an IBAN
and an email address. Bowden-PII lets an application replace those
values with [IBAN_1] and [EMAIL_1] before
sending the request to an AI service for summarisation.
That is the starting point for this project. Run detection locally, keep the original values there, and send the text with placeholders. The first public release, 0.0.1, gives developers a Python API and a command-line tool to try this with their own text.
Why name it after Caspar Bowden?
Caspar Bowden studied who could access data held by cloud services, including through government surveillance. He co-founded the Foundation for Information Policy Research and was Microsoft's chief privacy adviser for Europe, the Middle East and Africa. EDRi's remembrance describes his work and independence.
His work is a reason to ask what an application should send to a cloud service in the first place. Bowden-PII addresses one part of that question by removing supported identifiers before transmission. The name is an independent tribute, without affiliation or endorsement from his estate.
What happens to the text
The rules engine checks Swiss AHV/AVS, UID/CHE and VAT identifiers, IBAN and QR-IBAN, and payment-card numbers. It validates checksums where the format provides one. It also detects email addresses, conservative Swiss phone patterns, URLs, IP addresses and MAC addresses.
from bowden_pii import redact
result = redact("Contact mia@example.ch", policy="strict")
print(result.redacted)
# Contact [EMAIL_1]
If the same normalized value occurs twice, it gets the same
placeholder within that result. A separate map holds the originals.
Keep that map local. The CLI's full --json output also
includes it, so forward result.redacted when you want the
masked text. The audit summary omits the original values.
The policy determines which identifiers the engine replaces.
strict includes every supported rule class.
balanced leaves URLs unchanged.
permissive also leaves IP addresses, MAC addresses and
standalone UID values unchanged.
An early model flagged harmless text
Names, addresses and OCR errors need more than format rules. The experimental MiniLM classifier predicts which parts of a text contain identifiers. The hybrid engine combines those predictions with rule detections and gives validated rules priority when they overlap.
One early model flagged identifiers in 38 of 40 test texts labelled as containing no identifiers. Adding negative examples to training reduced that to zero in the next run on the same held-out set. These were small synthetic sets, but they exposed a problem we needed to fix before training on more data.
The larger minilm-large-v1 run completed three epochs on
12,000 synthetic training rows. The data includes German, French,
Italian and English examples. Evaluation used separate generated
splits and a curated stress set of 120 rows.
The larger run passed the project's recorded acceptance checks. There is still a gap in that evidence. The hybrid report compares counts of identifier labels, so a correct count can hide an incorrect text boundary. We need broader tests on independently labelled real documents before claiming reliable detection across languages and document types.
The rules runtime, 36 focused tests, a small synthetic benchmark and experimental hybrid interfaces. Trained weights and training datasets are not included. The MiniLM adapter currently stops at 256 tokens by default and does not split long inputs into chunks.
Try the rules engine today
Download the source or wheel and install it with Python 3.11 or newer. The default engine needs no model, runtime dependencies or network connection. You can pass your own text to the API or command line. The website only shows precomputed examples.
The next work is better evaluation on public documents and OCR text, wider identifier coverage, and long-text processing. Distributing trained weights will also need a documented evaluation of the model we ship.
If an identifier slips through, or harmless text gets masked, open a GitHub issue with a synthetic example and the output you expected. Keep personal and confidential data out of the report.