First working build. Not yet submitted to the Chrome Web Store; see
PUBLISHING.md for what is still outstanding.
- Value getter interception.
HTMLInputElement,HTMLTextAreaElementandHTMLSelectElementvaluegetters are wrapped in the MAIN world atdocument_start, before any page script runs. - Stack-trace attribution. Each read is attributed by walking the whole call
stack, not the topmost frame. Frameworks that wrap the value accessor —
React's
_valueTracker— otherwise absorb the blame, and with React bundled first-party that turns into a dropped detection rather than a mislabelled one. FormDatainterception.new FormData(form)serialises every field inside the browser without touching the value getter. It is the idiomatic way to read a form and the shape submit-time harvesting takes, and it was completely invisible until now.- Page scan. Existing and late-injected scripts are matched against the vendor list, catching pixels that arrive via a tag container or after a consent banner is dismissed.
- Tamper detection. Prototype replacement is detected and re-wrapped; element-level shadowing is caught by reading a field and checking whether our getter ran. An unrepairable bypass is reported rather than hidden.
Deliberately distinct, because conflating them is how a privacy tool becomes noise:
- Present — a known form-reading vendor is loaded. Never claims a read.
- Reads your fields — we watched a script read a field that was empty at the time. Nothing taken, but proof it will be.
- Data read — a script read a field with content in it.
A field upgrades from tier two to tier three when a read catches content, and never downgrades.
- In-page panel with a labelled edge launcher, and the browser side panel on
Chrome 114+. The in-page one is primary: Brave and some other Chromium
browsers do not expose
chrome.sidePanel. - Built to be read at a glance — one large number, three colour bands, category glyphs, mono field pills, prose collapsed behind a disclosure.
- Field marking recolours the field's existing border, walking up to the nearest
bordered ancestor when the input itself has none. Falls back to an outline.
Re-asserted via a
MutationObserver, because an inline style does not survive a re-render.
- Field contents never leave
sensor.js. The value is read for a length check and nothing else. - No network requests, no server, no account, nothing persisted.
tools/check-invariant.jsenforces the mechanical parts statically and the build refuses to package on failure.
157 automated checks across five suites, every one negative-tested. Eleven
browser fixtures covering the injection race, React attribution, performance,
the overlay, the page scan, tamper resistance, iframes, client-side routing,
strict CSP, FormData harvesting, and outline durability.