Botfilter is a private AI detector: the text you read is scored by a model file inside the extension, on your device, and it is never sent anywhere or saved. Scoring keeps working with Wi-Fi turned off, and there is no account or sign-in. Each of these claims can be checked in Chrome’s DevTools in about five minutes, using the steps near the end of this page.
The launch post introducing Botfilter covers what it does and what each mode looks like. Everything below describes the current Chrome build.
The path your text takes inside the browser
Four parts of the extension handle a piece of text. All four live in your browser, and they talk to each other only through the browser’s own extension messaging.
| Step | Where it runs | What it handles |
|---|---|---|
| 1. Read | Content script, in the page you are viewing | The text of each post, email, or paragraph that is on screen or within about 1,200 pixels of it. It skips anything you are typing: text boxes, form fields, and editable areas, plus code blocks. |
| 2. Route | Service worker, the extension’s background script | Passes the text along and remembers which tab asked. It starts the scorer when a page needs it. |
| 3. Score | A worker inside an offscreen document | Splits the text into tokens with a bundled vocabulary and runs the bundled model on it. Returns one number per text. |
| 4. Mark | Content script, back in the page | Turns the number into a probability and applies your mode: Outline, Collapse, or Hide. |
Firefox has no offscreen documents, so the Firefox build runs the same worker from the extension’s background page.
Before step 2, the content script tidies the text, caps it at 4,000 characters, and drops anything under 25 words, which is too little to judge. It also computes a short hash of the text, a fixed-length fingerprint used to recognize text it has already scored. Page text is never saved. What is saved is that hash, the score, and a few numbers about it, listed in full below. The text itself is never written to disk. It is held in memory while Botfilter works on it, and the scorer also keeps up to 60,000 words it has already split into tokens; that memory is released when the model closes after 10 idle minutes.
Where the model file lives and how it runs
The model is a file named botfilter.int8.onnx, 23,626,443 bytes, in the model folder of the installed extension. Next to it sit vocab.txt (231,508 bytes), which maps words to tokens, and calibration.json (321 bytes), which holds the thresholds for the three sensitivities. They arrive with the extension when you install it. Nothing is downloaded later, so scoring works with no connection.
The model is a fine-tuned MiniLM: 6 layers, 22.7 million parameters, with its weights stored as 8-bit integers to keep the file small. It reads a window of 256 tokens from the start of a text and, when the text is longer, another from the end, and averages the two. How it was trained, on what data, and how well it scores on held-out text are in how the Botfilter detector was trained.
To run it, Botfilter uses ONNX Runtime Web, Microsoft’s engine for running ONNX models in a browser, which is also bundled. Its WebAssembly file sits in the extension’s ort folder, so the model and its runtime add up to about 50 MB of the install. At start, the worker chooses where to run the model:
- WebGPU, your graphics card, when the browser offers a real GPU adapter and a test batch of 8 texts at 128 tokens runs in under 60 ms. If the GPU is slower than that, Botfilter times both options and keeps the faster one.
- WebAssembly on the CPU otherwise, with up to 4 threads (half your cores, capped at 4). The extension turns on the browser’s cross-origin isolation for its own pages so these threads are allowed.
Chrome’s extension service worker cannot start a web worker itself, so Botfilter opens one hidden offscreen document whose only job is to hold the worker. Chrome’s documentation says the only extension API that document can use is chrome.runtime, which Botfilter uses for messaging. When nothing has asked for a score for 10 minutes, Botfilter closes it and the model’s memory is released. It opens again from the same local file on the next page that needs it.
If you want the general picture of how models like this run in a browser, and why a small one can be enough, read what on-device AI means.
Why nothing is sent over the internet
Scoring loads files only from inside the extension and sends nothing over the internet. There is no Botfilter server to send text to. In the extension source, every fetch call loads a file from the extension’s own address, chrome-extension:// followed by its id: the model, the vocabulary, the calibration file, and the WebAssembly runtime. There is no XMLHttpRequest, WebSocket, sendBeacon, or identity API anywhere in it. The built bundles contain one web address, a documentation link inside an ONNX Runtime error message, which is never requested.
The extension’s test suite also checks this in a real browser. It opens a test copy of an X timeline, waits for Botfilter to mark a post, and fails if the page or the extension makes any request the test can see to anywhere other than the extension’s own files or the test page. The test browser answers every other web address with a local stand-in, so no real site is contacted. The test has not yet been shown to record requests made from the hidden offscreen document, which is why step 2 below checks that document by hand.
There is also nothing to sign in to:
- No account and no sign-in. There is no login screen, and the extension does not ask for the browser’s identity permission.
- No analytics or telemetry. Nothing counts your pages, your scores, or how often you open the popup.
- No usage limit. Scoring runs on your own hardware, not on a server, so there is no per-word cost to pass on: no daily word allowance and no credits. Botfilter is free.
The Firefox build declares that it collects no data, using Firefox’s own data-collection field, and Firefox 140 and later show that declaration on the install prompt. Most hosted AI detectors work the other way round: you paste or send text, and it is scored on their servers. AI detectors compared lays out where each tool sends your text, and a free, private alternative to Pangram compares Botfilter with one of them.
What each permission is for
Every extension ships a file called the manifest, which declares what it is allowed to do. This is the permissions part of Botfilter’s Chrome manifest, copied from the built extension:
{
"host_permissions": ["https://*/*", "http://*/*"],
"permissions": ["storage", "offscreen", "scripting", "alarms"],
"content_security_policy": {
"extension_pages": "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
}
}| Permission | What Botfilter uses it for |
|---|---|
https://*/* and http://*/* | Reading and marking text on the sites you visit. This is the broad one, and Chrome warns about it at install. The text it reads goes only to the local model. |
storage | Saving your settings, your “Not AI” choices, and inbox scores, as described in the next section. |
offscreen | Opening the hidden document that holds the model’s worker. |
scripting | The “Scan this page” button, for a tab that was already open before you installed Botfilter, and removing a leftover setting from older versions. |
alarms | A check every 2 minutes that closes the model after 10 idle minutes to free memory. |
Botfilter does not ask for cookies, browsing history, tabs, downloads, or the ability to watch or change network requests. The security policy for its own pages lets them run only scripts packaged with the extension, plus WebAssembly. That policy does not by itself block network requests; the no-network claim rests on the code and the test above.
Does Botfilter store the text you read?
No. It stores hashes, scores, and settings, never page text. Running locally is not the same as storing nothing, so here is everything it keeps, all in your browser profile.
| What | Where | Contents | How long |
|---|---|---|---|
| Settings | Browser extension storage (sync area) | On or off, mode, sensitivity, the badge setting, and the site names you turned off or gave their own mode | Until you change them or remove Botfilter |
| Saved scores | IndexedDB, in the extension’s own storage | Model version, a hash of the text and its length, the score, the word count, a timestamp | 30 days, and at most 60,000 rows |
| “Not AI” choices | Extension local storage | Hashes of texts you unmarked, up to 5,000 | Until you clear them or remove Botfilter |
| Inbox scores | Extension local storage | Your mail site’s own id for a thread, its score, and word count, up to 3,000 | Until you clear them; dropped when the model changes |
// One row in IndexedDB "botfilter" > "scores" (offscreen document, Application panel)
{ "k": "<model version>|<hash>:<length>", "m": 2.41, "w": 63, "t": 1790000000000 }
// k: model version, a hash of the text, and its length in characters
// m: the model's score, w: word count, t: when it was saved- Settings can travel with browser sync. Botfilter saves settings in the browser’s standard sync area. If you have turned on sync in Chrome or Firefox, the browser copies extension settings to your other signed-in browsers, as it does for any extension. That includes the names of sites you turned Botfilter off on. Scores, hashes, and page text are never in that area.
- Hashes are fingerprints, not a lock. A hash cannot be turned back into the text, but someone using your browser profile who already has a text could hash it and see whether it is in the cache. Treat the cache like your browsing history.
To erase the saved scores, “Not AI” choices, and inbox scores, open Botfilter’s settings, wait until the line at the bottom names the model version, and press Clear next to “Saved scores and ‘Not AI’ choices”. That line shows the model is running, and the saved scores can only be cleared while it is. Removing the extension deletes its local storage and its saved scores. Chrome’s storage documentation does not say whether settings already copied by browser sync are removed too.
How to check it yourself
These steps are for Chrome with Botfilter installed, so you can run them once the Chrome Web Store listing is live. Each one takes about a minute.
- Read the permissions. Open
chrome://extensions, find Botfilter, and choose Details. Chrome lists only permissions that carry a warning, so you will see one: that Botfilter can read and change your data on the sites you visit. Nothing about identity, cookies, history, or downloads. The other four permissions raise no warning. To see the whole list, openchrome://version, go to the folder named next to Profile Path, thenExtensionsand Botfilter’s id, and compare itsmanifest.jsonwith the block above. - Watch the model load. Open any article so Botfilter scores something. On
chrome://extensions, turn on Developer mode. Under Botfilter, choose “Inspect views: offscreen.html”. That link appears once the model is running. In the window that opens, choose the Network tab, then press Command+R (Ctrl+R on Windows and Linux) in that window so the model loads again while the panel is recording. Every request listed is achrome-extension://address: the model, the vocabulary, the calibration file, and the runtime. These are reads from the extension’s own folder on your disk, not internet traffic. - Watch while you scroll. Keep that Network tab open and scroll through a long feed. Scoring happens while you scroll, and no new request appears. Do the same with “Inspect views: service worker”.
- Go offline. Open a long article, then turn off Wi-Fi or unplug the network cable. Keep scrolling. Paragraphs further down still get marked, because the model never needed the network.
- Read what is stored. In the offscreen window, open Application, then IndexedDB, then botfilter, then scores. Each row is a hash and a few numbers, never text. For settings and choices, run these two lines in the service worker console:
// In the service worker console (chrome://extensions, then "Inspect views: service worker")
await chrome.storage.sync.get(null); // settings: mode, sensitivity, sites you turned off
await chrome.storage.local.get(null); // "Not AI" choices as hashes, inbox scores by thread idBotfilter’s settings page also shows where the model is running: a line at the bottom names the model version, says whether it runs on the GPU or on the CPU and with how many threads, and says how long it took to load. For the Firefox build, the same checks start from about:debugging, This Firefox, then Inspect next to Botfilter; its requests use moz-extension:// addresses.
What running locally covers, and what it does not
Running on your device decides whether Botfilter sends the text you read anywhere, and it does not. It does not change what the score means. The model is the same whether it runs on your laptop or on a server, and the result is a probability, such as “likely AI-written · 92%”.
Botfilter was trained and tested on English only. It still scores other languages, and how often it flags them is unmeasured. It skips texts under 25 words. Human writing can be flagged, most often work email and older web pages in our tests. A flag is a probability, not proof about the person who wrote it, so use it to filter what you read rather than to judge anyone.
For what those probabilities mean in practice, and how often any detector gets people wrong, read how accurate AI detectors are. The full data policy is on the Botfilter privacy page, and the home page shows what Botfilter does on X, LinkedIn, Gmail, and other sites.
Botfilter is in development, and its Chrome Web Store listing is coming soon. A Firefox build is also in development.