The most constrained computer most people will ever use is the browser tab you are reading this in. Tighter than the phone in your pocket, and nobody budgets for it.
A browser tab gets no installer, no graphics card it can count on, a few hundred megabytes of memory before things get ugly, and a user whose patience for a progress bar is measured in seconds. It's the worst place in modern computing to put a neural network. We put ours there on purpose.
Why the model has to be in the tab
Anonymization-as-a-service is a contradiction. To use it, you upload the confidential document to the company promising to protect it. The exposure does not go away. It moves, and acquires a vendor.
So our detection runs in your browser: the PDF parsing, the character recognition on scans, and the model that works out that Margarida Rodrigues is a person and 1249-300 Lisboa is an address, in seven languages, without being told which one. Which means the model has to fit in a tab. The first version didn't.
The alphabet tax
Our detector starts life as a multilingual BERT fine-tuned to find names, organizations and places. Good model. It also carries a vocabulary of 119,547 tokens spanning 104 languages.
We offer seven: English, French, German, Spanish, Italian, Dutch, Portuguese. All Latin script.
At 768 dimensions that vocabulary table is about 92 MB of a 178.5 MB file: more than half the model. Inside it: Devanagari, Cyrillic, Hangul, Thai, Armenian, Hebrew, Arabic, kana, thirteen thousand CJK ideographs. Downloaded by every user. Used by none.
Call it the alphabet tax. Every visitor pays to carry 104 languages in order to read one. We also built the tool that fixes this, proved it was safe, and then left it as an optional command nobody ran. A build step that is opt-in is a build step that happens nowhere.
Deleting rows, not compressing photos
The standard answer is distillation: train a small student model to imitate a large teacher. It works, and it trades accuracy for size. That is not a trade you get to make quietly in a tool whose whole job is to not miss anything. So we didn't.
What we did instead is closer to deleting unused rows from a spreadsheet. No encoder weight is touched: the twelve layers that do the actual understanding are copied verbatim. The vocabulary table is quantized per-tensor, so a surviving row keeps its exact value whether its neighbours are there or not. And the tokenizer matches the longest piece available, so dropping pieces unreachable in Latin text leaves Latin text splitting identically.
Result: 119,547 tokens down to 68,875. 178.5 MB down to 139.6 MB, about 100 MB over the wire. And we can claim more than "near-identical". We ran both models over 77 real documents in all seven languages: zero differences in how text was split, zero differences across 2,219 detected entities, and a maximum difference in model output of 0.0000000000. The same model, with the dead weight removed.
Getting there cost us one bad week. Token identifiers appear in three places in the tokenizer file; we updated two. Every test passed, text segmented identically, and the model quietly returned different answers on sixty documents. The rule we kept: compare the raw model output, never the entity list. Entity lists agree by luck.
Twice we tried to go further
Vocabulary is still a third of what remains, much of it Latin script for languages we do not offer. Another 30 to 40 megabytes looked available. It turned out to be a cliff.
Keeping 58,000 of the 68,875 Latin tokens, 84% of them, saved 8 MB and lost 13% of all detections. Then the control that ended the argument: a keep-list of only the tokens our test documents actually use (about 22,800 kept, about 46,000 thrown away) produced byte-identical segmentation and 100% agreement. So a small vocabulary costs nothing. A missing token costs everything. There is no curve to tune along, and re-segmentation fails hardest on rare words, which is what proper nouns are.
One number closed it. The Latin characters for languages we do not sell into (Turkish ğ ı, Vietnamese ư ạ ế, Romanian ă ț, Czech ř ů, Hungarian ő, Nordic ø) come to 4,200 pieces. 3.2 MB. That is the alphabet of every foreign name in a European contract: prime personal data, in the documents most likely to hold it, for three megabytes. If our detection degrades on your name because your name is Turkish, we have built the wrong product.
Second attempt: DistilBERT, six layers instead of twelve, about 70 MB over the wire against our 100. Small enough to talk yourself into. Then we scored it against 137 hand-checked entities and detection fell from 77.2% to 73.5%. But the average hides the shape: German fell 13 points, English 9, Spanish 22. And the two models agreed on only 76% of what they found, which means the smaller one returned a different set of entities of the same size. "Fewer" you can manage. "Different" you cannot.
Minuscule
The model we ship is called Minuscule. Carolingian minuscule was the writing system standardized across Charlemagne's empire in the eighth century: one clean Latin script, deliberately narrow, which is why a scribe in Aachen could read a manuscript copied in Tours. Many languages, one alphabet, nothing extra. Also, it is very small.
68,875 tokens. 139.6 MB on disk, roughly 100 MB transferred once and cached for good. Not one encoder weight altered from the model it derives from. It carries its own build record (upstream model, settings, vocabulary sizes, the checksum of its own weights), because a model that cannot tell you where it came from has no business handling the thing you are protecting. It is published on Hugging Face (weights, tokenizer and build record), so the numbers above are yours to check rather than ours to assert.
What a hundred megabytes buys
Right now, somewhere, a paralegal is pasting a client’s contract into a chat window. A nurse is uploading a discharge summary. A caseworker is dropping a family's file into a free translation site. They are not reckless people. They are behind. The approved option needs a ticket, a security review and two weeks, and the deadline is Thursday.
The only version of "protected" that ever wins is the one that is already open. No install, no administrator, no permission, no meeting. Which means a browser tab. Which means the model fits in a browser tab. Which is why grown adults spent a week arguing about 3.2 megabytes of Turkish diacritics.
Unglamorous work, invisible by design. But it ends with a person on a laptop in an office you will never see, doing their job on time, and their client's name never leaving the room. A hundred megabytes, once. After that, it is yours.
Common questions
Does removing vocabulary from a model reduce its accuracy?
Not when the removed vocabulary is unreachable in the languages you support. No encoder weight is modified and every surviving embedding row is copied bit-for-bit, so for Latin-script text the result is numerically identical to the original. We measured zero differences across 2,219 entities in 77 documents. Cutting into vocabulary the model can reach is a different matter, and it degrades sharply.
Why not just use a smaller model instead?
We tried. A six-layer DistilBERT of the same family is about 70 MB on the wire against our 100, but it scored 73.5% against 77.2% on our curated set, and the average hid a 22-point drop in Spanish and 13 in German. It also agreed with the shipped model on only 76% of its findings, which makes it a different set of entities of the same size. Our benchmark against Presidio explains how we measure this.
Can a model this size really run in a browser tab?
Yes, with two caveats. The download happens once and is cached on your device afterwards, and the model still has to be compiled into a session at each launch, which is why there is a loading bar on the first run. Everything after that is local: no page of your document is uploaded. Why we built our own detection engine covers the rest of the pipeline.
Which languages does it cover, and what happens to the rest?
English, French, German, Spanish, Italian, Dutch and Portuguese, plus any other Latin-script language whose characters survive in the vocabulary: Turkish, Vietnamese, Romanian, Czech, Hungarian and Nordic characters were deliberately kept, because foreign names appear constantly in European contracts. A language in a dropped script, such as Japanese, needs its own model pack rather than one shared model that grows with every language added.