maisecrets / Why you can trust it
Why you can trust it
High-precision detection of personal data. No language model required. On four labelled datasets, maisecrets 0.8.6 made the fewest wrong masks of four open-source tools, and it had a higher F1 than Preserve on all four. It finds a name after a label, or when you put the name on a term list. Tools with a language model find more names. The charts show both sides.
Personal data: precision, recall and F1 against three tools
Four open-source tools on the same documents of four labelled datasets: maisecrets on 31 October 2026, the other tools on 21 October 2026. No maisecrets rule was written from these documents. The tools work in different ways:
- maisecrets 0.8.6: rules and labels, and the names on your term list. No language model, no name dictionary.
- Preserve: rules plus a name dictionary and word frequencies (default configuration, no model).
- pii-anon: regular expressions only (the
regex-ossengine). - Presidio: rules plus the spaCy language model
en_core_web_lg(NER).
maisecrets made the fewest wrong masks on every dataset. Presidio found more personal data on three of them. Most of that difference is names: 74 to 92 % of the values that maisecrets missed are names. maisecrets ships no language model.
Precision against recall: maisecrets has the highest precision on every dataset; with its opt-in NER, it finds the most and has the best F1
Higher: fewer wrong masks. Further right: more values found. The dashed curves join points of equal F1. Benchmark V3, maisecrets 0.8.6 with and without NER on 31 October 2026, the other tools on 21 October 2026.
What to notice: maisecrets (orange) has the highest precision on every dataset, so its masks are wrong least often. Without NER it is further left than other tools on three datasets, because it finds fewer names.
With the opt-in NER service (blue), maisecrets finds the most and has the best F1 on every dataset. Its precision falls on Mendeley to 86.3 %, below Presidio, and on Privy it ties with pii-anon. The NER point: 31 October 2026, the image mcpgate/ner:v2.0.3573 on the same samples.
The numbers of this chart
| Dataset | Tool | How it finds personal data | Found (recall) | Right when flagged (precision) | False alarms | F1 |
|---|---|---|---|---|---|---|
| Nemotron | Presidio | rules + language model | 72.2 % | 92.8 % | 173 | 0.812 |
| Nemotron | Preserve | rules + name dictionary | 66.0 % | 82.5 % | 356 | 0.733 |
| Nemotron | pii-anon | regex | 76.6 % | 29.7 % | 4,437 | 0.428 |
| Nemotron | maisecrets | rules | 61.7 % | 99.6 % | 7 | 0.762 |
| Nemotron | maisecrets + NER | rules + ner (opt-in) | 85.9 % | 99.3 % | 14 | 0.921 |
| Mendeley | Presidio | rules + language model | 67.7 % | 89.5 % | 397 | 0.771 |
| Mendeley | Preserve | rules + name dictionary | 66.6 % | 81.3 % | 380 | 0.732 |
| Mendeley | pii-anon | regex | 61.0 % | 65.8 % | 1,089 | 0.633 |
| Mendeley | maisecrets | rules | 70.6 % | 98.8 % | 18 | 0.824 |
| Mendeley | maisecrets + NER | rules + ner (opt-in) | 93.7 % | 86.3 % | 315 | 0.899 |
| Gretel | Presidio | rules + language model | 45.3 % | 27.3 % | 2,398 | 0.340 |
| Gretel | Preserve | rules + name dictionary | 41.6 % | 24.6 % | 2,461 | 0.309 |
| Gretel | pii-anon | regex | 42.9 % | 27.7 % | 2,012 | 0.337 |
| Gretel | maisecrets | rules | 31.2 % | 90.4 % | 39 | 0.464 |
| Gretel | maisecrets + NER | rules + ner (opt-in) | 51.3 % | 91.8 % | 64 | 0.658 |
| Privy | Presidio | rules + language model | 47.3 % | 52.7 % | 1,060 | 0.498 |
| Privy | Preserve | rules + name dictionary | 20.5 % | 41.5 % | 350 | 0.275 |
| Privy | pii-anon | regex | 28.6 % | 93.6 % | 22 | 0.438 |
| Privy | maisecrets | rules | 23.7 % | 99.2 % | 2 | 0.383 |
| Privy | maisecrets + NER | rules + ner (opt-in) | 66.4 % | 93.5 % | 51 | 0.776 |
Names and the other personal data, counted apart. The table below splits the same V3 runs after the fact. The documents and the findings of each tool stay the same; only the count of found values is split by kind. It is a post-hoc analysis, not a separate benchmark. It shows two things. First, names explain a large part of the low overall recall of maisecrets: it finds 5 to 31 % of the names. Second, maisecrets also misses other personal data. On Gretel it finds only 56 % of the values that are not names, and most of these misses are postal addresses. Without names, maisecrets has the highest recall on 3 of the four datasets. On Privy, Presidio finds more: 97 % against 76 %.
| Tool | Overall, % | Names, % | Not names, % |
|---|---|---|---|
| Nemotron834 documents, 2,781 values | |||
| Nemotron: PresidioPreservepii-anonmaisecrets+ NER | Presidio: 72.2 Preserve: 66.0 pii-anon: 76.6 maisecrets: 61.7 + NER: 85.9 (highest) | Presidio: 86.0 Preserve: 64.1 pii-anon: 78.7 maisecrets: 31.5 + NER: 89.1 (highest) | Presidio: 62.4 Preserve: 67.4 pii-anon: 75.1 maisecrets: 83.3 + NER: 83.6 (highest) |
| Mendeley362 documents, 2,084 values | |||
| Mendeley: PresidioPreservepii-anonmaisecrets+ NER | Presidio: 67.7 Preserve: 66.6 pii-anon: 61.0 maisecrets: 70.6 + NER: 93.7 (highest) | Presidio: 95.7 Preserve: 45.7 pii-anon: 98.1 (highest) maisecrets: 19.9 + NER: 95.9 | Presidio: 55.4 Preserve: 75.7 pii-anon: 44.8 maisecrets: 92.8 (highest) + NER: 92.8 (highest) |
| Gretel334 documents, 1,141 values | |||
| Gretel: PresidioPreservepii-anonmaisecrets+ NER | Presidio: 45.3 Preserve: 41.6 pii-anon: 42.9 maisecrets: 31.2 + NER: 51.3 (highest) | Presidio: 50.9 (highest) Preserve: 44.0 pii-anon: 45.2 maisecrets: 13.6 + NER: 47.9 | Presidio: 37.4 Preserve: 38.3 pii-anon: 39.7 maisecrets: 56.0 (highest) + NER: 56.0 (highest) |
| Privy2,816 documents, 1,076 values | |||
| Privy: PresidioPreservepii-anonmaisecrets+ NER | Presidio: 47.3 Preserve: 20.5 pii-anon: 28.6 maisecrets: 23.7 + NER: 66.4 (highest) | Presidio: 29.8 Preserve: 18.3 pii-anon: 26.1 maisecrets: 5.2 + NER: 62.8 (highest) | Presidio: 97.1 (highest) Preserve: 26.8 pii-anon: 35.7 maisecrets: 76.4 + NER: 76.4 |
Bold: the highest value of a column within a dataset.
+ NER (blue): maisecrets with the opt-in NER service, the same samples. It finds far more names; the rest stays almost the same.
Overall is the recall of the charts above and below. Names: values labelled as the name of a person. Not names: every other labelled kind, addresses and user names included. Wrong masks cannot be split this way, because the result files do not store the kind of a wrong mask. Precision stays one value for all personal data.
The counts of this table
| Dataset | Tool | Overall: found / labelled | Names: found / labelled | Not names: found / labelled |
|---|---|---|---|---|
| Nemotron | Presidio | 2,009 / 2,781 | 997 / 1,159 | 1,012 / 1,622 |
| Nemotron | Preserve | 1,836 / 2,781 | 743 / 1,159 | 1,093 / 1,622 |
| Nemotron | pii-anon | 2,130 / 2,781 | 912 / 1,159 | 1,218 / 1,622 |
| Nemotron | maisecrets | 1,716 / 2,781 | 365 / 1,159 | 1,351 / 1,622 |
| Nemotron | + NER | 2,389 / 2,781 | 1,033 / 1,159 | 1,356 / 1,622 |
| Mendeley | Presidio | 1,410 / 2,084 | 606 / 633 | 804 / 1,451 |
| Mendeley | Preserve | 1,388 / 2,084 | 289 / 633 | 1,099 / 1,451 |
| Mendeley | pii-anon | 1,271 / 2,084 | 621 / 633 | 650 / 1,451 |
| Mendeley | maisecrets | 1,472 / 2,084 | 126 / 633 | 1,346 / 1,451 |
| Mendeley | + NER | 1,953 / 2,084 | 607 / 633 | 1,346 / 1,451 |
| Gretel | Presidio | 517 / 1,141 | 340 / 668 | 177 / 473 |
| Gretel | Preserve | 475 / 1,141 | 294 / 668 | 181 / 473 |
| Gretel | pii-anon | 490 / 1,141 | 302 / 668 | 188 / 473 |
| Gretel | maisecrets | 356 / 1,141 | 91 / 668 | 265 / 473 |
| Gretel | + NER | 585 / 1,141 | 320 / 668 | 265 / 473 |
| Privy | Presidio | 509 / 1,076 | 237 / 796 | 272 / 280 |
| Privy | Preserve | 221 / 1,076 | 146 / 796 | 75 / 280 |
| Privy | pii-anon | 308 / 1,076 | 208 / 796 | 100 / 280 |
| Privy | maisecrets | 255 / 1,076 | 41 / 796 | 214 / 280 |
| Privy | + NER | 714 / 1,076 | 500 / 796 | 214 / 280 |
The same results as bars, with the count of wrong masks for each tool:
The numbers of this chart
| Group | Tool | Found: labelled values masked whole | Of 100 things flagged, how many were wrong? |
|---|---|---|---|
| Nemotron-PII · 834 documents, 2,781 values | Presidio 2.2.364 (RULES + LANGUAGE MODEL) | 72.2 % | 7.2 (173) |
| Nemotron-PII · 834 documents, 2,781 values | Preserve 0.6.1 (RULES + NAME DICTIONARY) | 66.0 % | 17.5 (356) |
| Nemotron-PII · 834 documents, 2,781 values | pii-anon 1.7.0rc1 (REGEX) | 76.6 % | 70.3 (4,437) |
| Nemotron-PII · 834 documents, 2,781 values | maisecrets 0.8.6 (RULES) | 61.7 % | 0.4 (7) |
| Nemotron-PII · 834 documents, 2,781 values | maisecrets 0.8.6 + NER (RULES + NER (OPT-IN)) | 85.9 % | 0.7 (14) |
| Mendeley financial · 362 documents, 2,084 values | Presidio 2.2.364 (RULES + LANGUAGE MODEL) | 67.7 % | 10.5 (397) |
| Mendeley financial · 362 documents, 2,084 values | Preserve 0.6.1 (RULES + NAME DICTIONARY) | 66.6 % | 18.7 (380) |
| Mendeley financial · 362 documents, 2,084 values | pii-anon 1.7.0rc1 (REGEX) | 61.0 % | 34.2 (1,089) |
| Mendeley financial · 362 documents, 2,084 values | maisecrets 0.8.6 (RULES) | 70.6 % | 1.2 (18) |
| Mendeley financial · 362 documents, 2,084 values | maisecrets 0.8.6 + NER (RULES + NER (OPT-IN)) | 93.7 % | 13.7 (315) |
| Gretel finance · 334 documents, 1,141 values | Presidio 2.2.364 (RULES + LANGUAGE MODEL) | 45.3 % | 72.7 (2,398) |
| Gretel finance · 334 documents, 1,141 values | Preserve 0.6.1 (RULES + NAME DICTIONARY) | 41.6 % | 75.4 (2,461) |
| Gretel finance · 334 documents, 1,141 values | pii-anon 1.7.0rc1 (REGEX) | 42.9 % | 72.3 (2,012) |
| Gretel finance · 334 documents, 1,141 values | maisecrets 0.8.6 (RULES) | 31.2 % | 9.6 (39) |
| Gretel finance · 334 documents, 1,141 values | maisecrets 0.8.6 + NER (RULES + NER (OPT-IN)) | 51.3 % | 8.2 (64) |
| Privy · 2,816 documents, 1,076 values | Presidio 2.2.364 (RULES + LANGUAGE MODEL) | 47.3 % | 47.3 (1,060) |
| Privy · 2,816 documents, 1,076 values | Preserve 0.6.1 (RULES + NAME DICTIONARY) | 20.5 % | 58.5 (350) |
| Privy · 2,816 documents, 1,076 values | pii-anon 1.7.0rc1 (REGEX) | 28.6 % | 6.4 (22) |
| Privy · 2,816 documents, 1,076 values | maisecrets 0.8.6 (RULES) | 23.7 % | 0.8 (2) |
| Privy · 2,816 documents, 1,076 values | maisecrets 0.8.6 + NER (RULES + NER (OPT-IN)) | 66.4 % | 6.5 (51) |
How to read these numbers
Picture texts with 100 sensitive values in them, such as passwords, e-mail addresses and IBANs, and a lot of ordinary text around them. A tool marks what it thinks is sensitive.
- Found (recall): how many of the 100 values the tool masked completely. It masks 80 of them: 80 % found.
- Missed (false negatives): the 20 it did not mask. If they pass through a path that the tool checks, they reach the model as they are.
- Wrong masks (false positives): ordinary text that the tool masked anyway, such as a commit hash or a variable name. Say it masked 20 such things.
- Of 100 things flagged, how many were wrong: the tool flagged 100 things, 80 right and 20 wrong, so 20. This is 100 minus the precision. It does not say how likely it is that any one word of your text is masked.
A second tool masks 60 of the values and 1 ordinary word: it finds 60 %, and 2 of every 100 things it flags are wrong. Neither tool is better in every way. The first finds more; the second gets in the way less.
F1, a secondary summary. F1 joins found and precision into one number from 0 to 1. It is high only when both are high. It treats a miss and a wrong mask as equal, but in an agent they have different results. Read it beside recall and precision, not instead of them.
| Tool | Nemotron | Mendeley | Gretel | Privy |
|---|---|---|---|---|
| PresidioPreservepii-anonmaisecrets+ NER | Presidio: .812 Preserve: .733 pii-anon: .428 maisecrets: .762 + NER: .921 (highest) | Presidio: .771 Preserve: .732 pii-anon: .633 maisecrets: .824 + NER: .899 (highest) | Presidio: .340 Preserve: .309 pii-anon: .337 maisecrets: .464 + NER: .658 (highest) | Presidio: .498 Preserve: .275 pii-anon: .438 maisecrets: .383 + NER: .776 (highest) |
Why precision matters for this tool. maisecrets changes what the AI reads. Every wrong mask puts a placeholder where the agent needed the real text: a broken path, a wrong variable name, a test that fails. People then switch the tool off, and then nothing is masked. Few wrong masks are the condition for leaving it on. On these samples, Presidio made 22 to 530 times as many wrong masks as maisecrets.
F0.5, a summary that weighs precision more. F0.5 joins precision and recall like F1, but precision counts more than recall. For a tool that changes what an AI reads, it is the fairer summary. It still does not replace recall: a missed value is a risk, whatever the score.
| Tool | Nemotron | Mendeley | Gretel | Privy |
|---|---|---|---|---|
| PresidioPreservepii-anonmaisecrets+ NER | Presidio: .878 Preserve: .786 pii-anon: .338 maisecrets: .887 + NER: .963 (highest) | Presidio: .841 Preserve: .778 pii-anon: .648 maisecrets: .915 (highest) + NER: .877 | Presidio: .296 Preserve: .268 pii-anon: .298 maisecrets: .655 + NER: .793 (highest) | Presidio: .515 Preserve: .344 pii-anon: .644 maisecrets: .606 + NER: .864 (highest) |
Why both errors cost something in an AI coding agent. A miss is a risk. If an undetected value passes through a path that maisecrets checks, such as your prompt or a tool result, it enters the model context: the model provider and the session transcript then have it. That is the harm maisecrets exists to prevent, and no precision value makes up for it. A wrong mask replaces text the agent needs with a placeholder: the agent cannot read the commit hash it must check, or your prompt is blocked for no reason. A coding agent reads tool results many times an hour, so a small rate of wrong masks still adds up. Many wrong masks teach a team to switch the tool off, and then nothing is masked. maisecrets is built for wrong masks to be rare.
Masked whole, or in part. The charts count a value as found only when it is masked completely. Some values are masked in part by design: maisecrets masks the password of postgres://user:password@host/db and keeps the host, so the agent can still connect. Those count as not found.
Secrets: real GitHub issues and token formats
A research team labelled the secrets that leaked in GitHub issue reports (MSR 2026 data set (opens in a new tab), CC BY 4.0). We split the set by issue and sealed one half on 25 October 2026, before any case was read. Nobody reads that half, and a run gives totals only (holdout log). Eight scanners ran on it: maisecrets on 26 October 2026, the other scanners on 25 October 2026.
The numbers of this chart
| Tool | Found: labelled secrets masked whole (span level) | Precision: secret findings on a true secret (snippet level) |
|---|---|---|
| maisecrets 0.8.2 | 72.0 % | 71.2 % |
| Betterleaks 1.9.0 | 54.6 % | 63.2 % |
| gitleaks 8.30.1 | 53.2 % | 88.9 % |
| Nosey Parker 0.24.0 | 31.1 % | 59.1 % |
| Kingfisher 1.113.0 | 29.6 % | 78.3 % |
| detect-secrets 1.5.0 | 29.4 % | 48.3 % |
| Kingfisher 2.11.1 | 21.8 % | 78.9 % |
| TruffleHog 3.99.2 | 12.3 % | 66.0 % |
Token formats. The second test uses the built-in examples of six public rule catalogs. These examples are development data for maisecrets: we read them to write rules, and maisecrets uses the Betterleaks rules. A lead on them is expected. The GitHub-issues holdout above is the stronger test.
The numbers of this chart
| Tool | Found: token examples masked whole |
|---|---|
| maisecrets 0.8.2 | 61.7 % |
| Betterleaks 1.9.0 | 46.1 % |
| gitleaks 8.30.1 | 45.1 % |
| Kingfisher 1.113.0 | 27.8 % |
| detect-secrets 1.5.0 | 23.5 % |
| Kingfisher 2.11.1 | 21.9 % |
| TruffleHog 3.99.2 | 12.3 % |
| Nosey Parker 0.24.0 | 9.8 % |
Names: a language model as an opt-in
A name has no fixed shape, so rules find it after a label, or when it is on your term list. A language model (NER) finds names in running text. We measured five such models as an extra layer after the detector, on one dataset (chart below, maisecrets 0.7.3).
maisecrets from 0.8.6 can ask a NER service with the model of our pick, D3. The service is opt-in and runs in the container mcpgate/ner: /maisecrets:ner local starts it on your computer (how to turn it on). maisecrets sends it tool results, with the values that the rules found as filler. maisecrets from 0.8.7 also sends your typed prompts in Claude Code to a local service, and a name there becomes a placeholder; a service on the network never gets a prompt. On the four V3 datasets, it finds names whole as follows:
- Nemotron: 365 without NER, 1,033 with NER, of 1,159
- Mendeley: 126 without NER, 607 with NER, of 633
- Gretel: 91 without NER, 320 with NER, of 668
- Privy: 41 without NER, 500 with NER, of 796
The same samples and scoring as the chart above; result files: bench/results/2026-10-31-v3-0.8.6-ner.
The model D3 (Davlan/distilbert-base-multilingual-cased-ner-hrl, AFL-3.0) and its base model (distilbert-base-multilingual-cased, Apache-2.0) have their notices in the image under /licenses.
The one we recommend is the multilingual DistilBERT (D3, Davlan/distilbert-base-multilingual-cased-ner-hrl): in our measurement, the best trade-off between names found and added false alarms (NER-EXPERIMENT.md). You can also run it on your own files and import the names it finds. maisecrets then masks those names wherever they appear.
The numbers of this chart
| Tool | Names found whole (of 8,121) | Added false alarms |
|---|---|---|
| maisecrets 0.7.3 | 312 · 3.8 % | baseline |
| + spaCy small (NOT SHIPPED) | 1,593 · 19.6 % | +3,676 |
| + spaCy large (NOT SHIPPED) | 2,439 · 30.0 % | +4,744 |
| + DistilBERT (D2) (NOT SHIPPED) | 4,345 · 53.5 % | +283 |
| + multilingual DistilBERT (D3) (OUR PICK · OPT-IN SERVICE) | 5,157 · 63.5 % | +413 |
| + GLiNER, threshold 0.5 (NOT SHIPPED) | 7,812 · 96.2 % | +31,138 |
Why our own detection engine?
Detection is only half the problem. An AI agent still has to work.
maisecrets uses a small, deterministic detection engine, made for agent workflows: precise masks, the same result for the same input on your own computer, and as little disruption to legitimate content as possible.
By default it needs no NER model and no external inference service. It does not guess names in free text. This keeps false alarms low, and it misses some personal data in free text, as the split above shows. The NER service is an opt-in for users who want more names and accept more false alarms.
On four evaluated PII datasets, maisecrets had a higher precision than the three other tools. That does not make it the best detector for every task. It makes it a good choice when a wrong mask costs much.
Detection is only the start. maisecrets adds local placeholders and the vault of your operating system, and it puts the real value back only where a tool needs it: in a command, a file or an MCP call of the same session.
Choose maisecrets when your AI agent must work with sensitive data without showing more of it to the model than necessary, and without breaking the work.
Fast answers
maisecrets reads every prompt and every tool result before the model does. A slow scan would be a wait in every step of the agent. The release gate measures the CPU time of each release against the last one, on one machine: maisecrets 0.8.2 needs 0.642 ms for a short prompt and 5.3 s for 1 MB of tool text (1.07 and 1.14 times the time of 0.8.1). The detector uses the Python standard library only: no model to load, no network call. Source: release-gate.md.
A second measurement compares ten tools on our reference server (AMD EPYC 9645, 8 vCPU, 15.6 GB RAM, Debian 13), one tool after the other: maisecrets 0.8.18 and the other tools on 4 November 2026. Its times differ a little from those of the release gate. The earlier table ran on an Apple M5, which is faster per core, so do not compare the two tables. Four Python tools ran on Python 3.15.0rc3, Presidio on Python 3.11 (README.md). A warm process is already running when the text arrives. A process per run starts the binary for each text, so its time includes the start.
| Tool | Short prompt | 10 KB | 1 MB |
|---|---|---|---|
| Warm process | |||
| Warm process: PresidioPreservepii-anondetect-secretsmaisecrets | Presidio: 18 ms Preserve: 763 ms pii-anon: 2.1 ms detect-secrets: 2.2 ms maisecrets: 0.69 ms | Presidio: 524 ms Preserve: 2.1 s pii-anon: 35 ms detect-secrets: 34 ms maisecrets: 60 ms | Presidio: 184.7 s Preserve: 63.2 s pii-anon: 5.4 s detect-secrets: 3.2 s maisecrets: 9.9 s |
| Process per run | |||
| Process per run: gitleaksBetterleaksNosey ParkerTruffleHogKingfisher 2 | gitleaks: 482 ms Betterleaks: 350 ms Nosey Parker: 595 ms TruffleHog: 2.0 s Kingfisher 2: 5.5 s | gitleaks: 563 ms Betterleaks: 352 ms Nosey Parker: 635 ms TruffleHog: 1.9 s Kingfisher 2: 5.6 s | gitleaks: 774 ms Betterleaks: 636 ms Nosey Parker: 605 ms TruffleHog: 1.9 s Kingfisher 2: 6.0 s |
Bold: the fastest tool of a column. A process per run includes the start of the binary.
The numbers of this chart
| Group | Tool | Short prompt | 10 KB | 1 MB |
|---|---|---|---|---|
| Warm process | Presidio | 18 ms | 524 ms | 184.7 s |
| Warm process | Preserve | 763 ms | 2.1 s | 63.2 s |
| Warm process | pii-anon | 2.1 ms | 35 ms | 5.4 s |
| Warm process | detect-secrets | 2.2 ms | 34 ms | 3.2 s |
| Warm process | maisecrets | 0.69 ms | 60 ms | 9.9 s |
| Process per run | gitleaks | 482 ms | 563 ms | 774 ms |
| Process per run | Betterleaks | 350 ms | 352 ms | 636 ms |
| Process per run | Nosey Parker | 595 ms | 635 ms | 605 ms |
| Process per run | TruffleHog | 2.0 s | 1.9 s | 1.9 s |
| Process per run | Kingfisher 2 | 5.5 s | 5.6 s | 6.0 s |
| Tool | Memory, short prompt | Memory, 1 MB | Install size |
|---|---|---|---|
| Warm process | |||
| Warm process: PresidioPreservepii-anondetect-secretsmaisecrets | Presidio: 767 MB Preserve: 1.9 GB pii-anon: 58 MB detect-secrets: 34 MB maisecrets: 24 MB | Presidio: 4.8 GB Preserve: 2.0 GB pii-anon: 141 MB detect-secrets: 52 MB maisecrets: 330 MB | Presidio: 783 MB Preserve: 302 MB pii-anon: 92 MB detect-secrets: 15 MB maisecrets: 3.7 MB |
| Process per run | |||
| Process per run: gitleaksBetterleaksNosey ParkerTruffleHogKingfisher 2 | gitleaks: 64 MB Betterleaks: 46 MB Nosey Parker: 267 MB TruffleHog: 203 MB Kingfisher 2: 105 MB | gitleaks: 69 MB Betterleaks: 61 MB Nosey Parker: 284 MB TruffleHog: 258 MB Kingfisher 2: 152 MB | gitleaks: 21 MB Betterleaks: 23 MB Nosey Parker: 41 MB TruffleHog: 35 MB Kingfisher 2: 35 MB |
Bold: the lowest value of a column. The install size of Presidio includes a model of 425 MB.
Source: resources.md
The numbers of this chart
| Group | Tool | Memory, short prompt | Memory, 1 MB | Install size |
|---|---|---|---|---|
| Warm process | Presidio | 767 MB | 4.8 GB | 783 MB |
| Warm process | Preserve | 1.9 GB | 2.0 GB | 302 MB |
| Warm process | pii-anon | 58 MB | 141 MB | 92 MB |
| Warm process | detect-secrets | 34 MB | 52 MB | 15 MB |
| Warm process | maisecrets | 24 MB | 330 MB | 3.7 MB |
| Process per run | gitleaks | 64 MB | 69 MB | 21 MB |
| Process per run | Betterleaks | 46 MB | 61 MB | 23 MB |
| Process per run | Nosey Parker | 267 MB | 284 MB | 41 MB |
| Process per run | TruffleHog | 203 MB | 258 MB | 35 MB |
| Process per run | Kingfisher 2 | 105 MB | 152 MB | 35 MB |
With the opt-in NER service. The service adds the time of the model to each tool result, and with a local service also to each typed prompt:
The numbers of this chart
| Group | Tool | Median: half of the documents are faster | p95: 95 of 100 documents are faster |
|---|---|---|---|
| Nemotron | Rules alone | 3.1 ms | 15 ms |
| Nemotron | + NER | 70 ms | 192 ms |
| Mendeley | Rules alone | 1.9 ms | 16 ms |
| Mendeley | + NER | 62 ms | 236 ms |
| Gretel | Rules alone | 4.0 ms | 9.4 ms |
| Gretel | + NER | 121 ms | 166 ms |
| Privy | Rules alone | 0.80 ms | 2.2 ms |
| Privy | + NER | 34 ms | 99 ms |
Typed prompts with NER. maisecrets from 0.8.7 also sends a typed prompt to the local service. A second measurement on 1 November 2026 used texts of prompt length from DialogPII, on an 8-core AMD EPYC 9645, the container limited to 4 CPUs and 1.5 GB, 4 requests at once. No rule was tuned on these texts.
| Tool | 500 characters | 1,000 characters |
|---|---|---|
| Rules alone+ NER | Rules alone: 2.6 % + NER: 56.6 % | Rules alone: 2.6 % + NER: 57.7 % |
Time per prompt with NER on this machine: 500 characters, 54 ms median and 84 ms p95; 1,000 characters, 100 ms median and 164 ms p95. Another CPU gives other times. The run used the timeout of 0.8.7, 300 ms, and every DialogPII request got its answer in time.
Wrong masks of the service: 3.53 and 2.78 per 1,000 words in DialogPII (a lower bound, because a mask on a labelled value of another kind does not count). In developer text from GitHub issues, at most 0.54 per 1,000 words (an upper bound).
Source: bench/results/2026-11-01-v3-0.8.7-prompt-ner
The numbers of this chart
| Tool | 500 characters | 1,000 characters |
|---|---|---|
| Rules alone | 2.6 % | 2.6 % |
| + NER | 56.6 % | 57.7 % |
These documents are longer than a typical tool result. The finds are the same for a service on your network, but the network adds its time. maisecrets waits at most timeout_ms (300 ms by default). After that, the rules mask alone. A slow service drops out; it does not slow the work down. The container needs about 230 to 260 MB of RAM with the int8 model, on a CPU with int8 dot-product instructions (Apple M, AMD Zen 4 and newer, Intel Xeon from Cascade Lake). On other CPUs, it uses the fp32 model and needs about 700 MB. The container selects the model by the CPU. We measured these values on 31 October 2026. The plugin itself needs nothing more.
For a short prompt, maisecrets needed the shortest time of the 10 tools: 0.69 ms. For 1 MB of tool text, 7 of the 9 other tools were faster. For 1 MB, maisecrets held up to 330 MB of memory: Presidio and Preserve needed more, the other 7 tools less. Times are the median of warm runs; memory is the peak of the process tree. Source: resources.md.
A language model for names adds to these numbers. In our NER experiment, spaCy small needed about 12 ms per document, D3 about 33 ms and 0.7 GB more memory, and GLiNER about 152 ms and 3.5 GB more memory (Apple M5, CPU only, one process, 300 documents; D3 in fp32). maisecrets ships none of them.
Where maisecrets stands
Measured, on the benchmarks above (maisecrets 0.8.6): on four labelled datasets of personal data, maisecrets had the highest precision of the four tools. Without names, it had the highest recall on 3 of the four datasets; with names, a tool with a language model finds more. It found the most on one dataset (Mendeley financial). The split by names is a post-hoc analysis of the same runs. As a secondary summary, its F1 was the highest on Mendeley and Gretel, and higher than Preserve's on all four. F0.5, which weighs precision more, was the highest on 3 of the four datasets. For secrets, on the sealed half of real GitHub issues, maisecrets masked the most secrets whole of 8 scanners, and gitleaks had the highest precision.
Where other approaches do better: a language model finds far more names in running text. Presidio has the highest F1 on Nemotron-PII and Privy and finds more personal data on three of the four datasets. pii-anon finds the most on Nemotron-PII, and on Privy its F1 is higher than that of maisecrets. Values that are not names are also missed: on Gretel, maisecrets finds 56 % of them.
What the product does beyond detection (released features, not benchmark results): the value goes into the vault of your operating system; the model sees a placeholder and the real value goes back only into a command, a file or an MCP call of the same session, under a cap per hour; prompts and tool output are checked inside Claude Code and Codex; a machine policy can fix the settings for a company. Every benchmark here can be repeated from the repository.
What we do not claim: that maisecrets is the most accurate detector, that it finds all personal data, or that a benchmark measures the protection of a real agent session.
Where your values stay
/maisecrets:list shows where each stored secret was used.
Our sealed holdout set
Rules are written from development data, so a score on that data is too kind. To check a release on data no rule was written from, we keep a sealed set of 872 synthetic texts with 828 hidden values. We made it ourselves, in a separate agent session that had no access to the code, the results or the misses of maisecrets. Its manifest was committed before the first run, and no rule is changed because of its result. It is our own measurement, not an independent or external evaluation, and no other tool was run on it.
Values maisecrets 0.8.2 masked whole, measured 26 October 2026 maisecrets raised 3 false alarms in all 872 texts.The secrets masked in part. 90 of the 330 secrets are connection strings and URL credentials, and all of them were masked in part, none whole. maisecrets is designed to mask the password of such a string and keep the host, so the agent can still use the connection. Whether the password was masked in each of these 90 cases is not measured yet: the set is sealed, and it does not mark which part of a string is the secret. Of the other 240 secrets, 200 were masked whole. The overall result stays 200 of 330.
The result for each kind of value: What it detects. Since 0.7.4, most releases are measured on this set; the holdout log lists each run.
Full results and method
Benchmark V3 ran maisecrets 0.8.6 and three open-source PII tools on the same documents: the validation split of Nemotron-PII, Mendeley and Gretel, and the Privy test set. The three other tools ran on 21 October 2026. maisecrets runs again on the same documents at each release; the values on this page are from 31 October 2026 (bench/results/2026-10-31-v3-0.8.6).
- Recall (R): the share of the labelled values that a tool masks whole.
- Precision (P): the share of a tool's masks that lie on a labelled value. A mask in a kind of value that the dataset does not label counts neither way.
- False alarms: masks on text that the dataset does not label as sensitive, in a kind it labels.
- F1: 2PR / (P + R), from the unrounded P and R of the result files. The
f1field in a result file uses the precision of Benchmark V2, so this page does not use it. - F0.5: 1.25PR / (0.25P + R), from the same P and R. It weighs precision more than recall.
- Level: every number on this page is at span level, and recall is strict: a value counts as found only when it is masked whole. Precision is P (V3).
- Splits: dev is read to write rules; val is used to decide, not to tune; holdout is never read, and a release sees only its totals. The charts above use samples of the val split (1 in 25) and of Privy (1 in 10). Privy is never read, and its labels are not complete, so its precision is a weak signal.
| Tool | Recall | Precision | F1 | F0.5 | False alarms |
|---|---|---|---|---|---|
| Nemotron-PII834 documents, 2,781 values | |||||
| Presidio | .722 | .928 | .812 | .878 | 173 |
| Preserve | .660 | .825 | .733 | .786 | 356 |
| pii-anon | .766 | .297 | .428 | .338 | 4,437 |
| maisecrets | .617 | .996 | .762 | .887 | 7 |
| + NER | .859 | .993 | .921 | .963 | 14 |
| Mendeley financial362 documents, 2,084 values | |||||
| Presidio | .677 | .895 | .771 | .841 | 397 |
| Preserve | .666 | .813 | .732 | .778 | 380 |
| pii-anon | .610 | .658 | .633 | .648 | 1,089 |
| maisecrets | .706 | .988 | .824 | .915 | 18 |
| + NER | .937 | .863 | .899 | .877 | 315 |
| Gretel finance334 documents, 1,141 values | |||||
| Presidio | .453 | .273 | .340 | .296 | 2,398 |
| Preserve | .416 | .246 | .309 | .268 | 2,461 |
| pii-anon | .429 | .277 | .337 | .298 | 2,012 |
| maisecrets | .312 | .904 | .464 | .655 | 39 |
| + NER | .513 | .918 | .658 | .793 | 64 |
| Privy2,816 documents, 1,076 values | |||||
| Presidio | .473 | .527 | .498 | .515 | 1,060 |
| Preserve | .205 | .415 | .275 | .344 | 350 |
| pii-anon | .286 | .936 | .438 | .644 | 22 |
| maisecrets | .237 | .992 | .383 | .606 | 2 |
| + NER | .664 | .935 | .776 | .864 | 51 |
Bold: the best value of a dataset (the highest, and for false alarms the lowest). Versions: Presidio 2.2.364, Preserve 0.6.1, pii-anon 1.7.0rc1 and maisecrets 0.8.6; + NER is maisecrets + NER with the opt-in NER service.
From 0.8.1 to 0.8.2, on the full splits
The release gate runs maisecrets on every document of each split, not on a sample. The holdout rows are the strongest evidence, because nobody reads that split. maisecrets only, personal data only, span level, strict recall, P (V3). No labelled value that 0.8.1 masked whole is missed by 0.8.2 (release-gate.md).
| Split | Recall | P (V3) | False alarms |
|---|---|---|---|
| Nemotron-PII | |||
| val66,240 values | .609 → .626 | .9953 → .9953 | 180 → 183 |
| holdout68,086 values | .602 → .619 | .9950 → .9950 | 196 → 198 |
| Mendeley financial | |||
| val51,592 values | .687 → .702 | .9906 → .9894 | 338 → 391 |
| holdout52,450 values | .683 → .699 | .9898 → .9887 | 371 → 421 |
| Gretel finance | |||
| val25,894 values | .281 → .304 | .8960 → .8936 | 855 → 952 |
| holdout26,718 values | .279 → .304 | .9054 → .9040 | 793 → 879 |
| Privy | |||
| test, never read11,247 values | .241 → .250 | .9767 → .9771 | 65 → 66 |
Recall rises on every dataset. Precision falls a little on Mendeley and Gretel. Gretel does not label some values in key: value lines that 0.8.2 now masks, such as Italian tax codes, Swedish personal numbers, names, streets and phone numbers; they count as false alarms. On Mendeley, most new false alarms are address hits in a few sentence templates. Result files: bench/results/2026-10-25-v3-0.8.1 and bench/results/2026-10-26-v3-0.8.2.
Limits. All four PII datasets are synthetic. Nemotron-PII and Gretel leave some real values without a label, so precision there is a lower bound for every tool. The tools have different aims, so the benchmark gives no overall rank. Method: BENCHMARK-V3.md. Result files: bench/results/2026-10-21-v3/run.
Measure it yourself. You can reproduce a release benchmark in about 10 minutes and compare your result with ours case by case: docs/REPRODUCE.md. We tested the steps with v0.8.12: a fresh clone on a laptop gave the same counts as our reference server, for all 28,165 cases.
What it is tested on
The public data sets link to their source. The results link to BENCHMARKS.md in our repository, with the version, the licence and the command to repeat each measurement.
- CredData (opens in a new tab)credentials in public repositories, by Samsung23,667 rows: 10,106 credentials, 13,561 non-secretsTests: secrets found, no alarm on what only looks like oneResults
- Secret leaks in GitHub issue reports (opens in a new tab)MSR 2026, CC BY 4.054,203 rows, split by issue; one half sealedTests: secrets in real issue text, no alarm on candidates that are not a secretResults
- Cloakroom holdout sets (opens in a new tab)values hidden in text1,652 values in 470 samples, 215 clean samplesTests: secrets and personal data in running textResults
- NVIDIA Nemotron-PII (opens in a new tab)labelled personal data100,000 documentsTests: e-mail, phone, IP, card and more by typeResults
- Mendeley financial documents (opens in a new tab)synthetic financial texts45,030 documentsTests: personal data in financial documentsResults
- Gretel synthetic PII, finance (opens in a new tab)in 7 languages41,932 documentsTests: personal data in several languagesResults
- PIILO (opens in a new tab)student essays6,807 essaysTests: names and personal data in proseResults
- Public code and developer textsthe Python standard library, packages, logs, configs, docs19,189 files, 2,464 texts, 1,114 snippets without a secret, 367 texts that only look like personal dataTests: no alarm on ordinary workResults
Our own sessions
The problem comes from our own work. Over 3,254 Claude Code sessions (5,260 transcripts, May to September 2026) we counted 185 real secrets: 148 never passed our MCP gateway, 27 came out of a gateway result, and 10 went into a gateway call. The same sessions held more than 1,000 e-mail addresses of people outside the company. The full count.
At the release of 0.6.17, 771 of our own typed prompts went through the hook, with no new block against 0.6.16. These prompts are not published: the number can be read, but not repeated.