Overview
RedactLocal is a local-first, fully offline desktop app I co-founded and built as technical co-founder. It strips personally identifiable information out of legal documents before a lawyer pastes them into an AI assistant, so client data stays on the machine. The detection stack runs locally with no network egress, because a tool meant to protect confidentiality cannot be the thing that leaks it.
I worked on the product through October 2026.
What it does
- Offline PII detection and pseudonymization. A runtime socket-audit test runs a real scan and asserts zero non-loopback connections.
- Profiles for five jurisdictions and 57 entity types, including names, case and bar numbers, national IDs and checksum-validated financial identifiers such as CUSIP, ISIN, SEDOL and LEI.
- Reversible, position-based pseudonymization, so a document can be redacted, worked on and restored. The mapping sits in an encrypted vault that the detection layer never sees.
- A signed, self-updating desktop client. Release manifests are signed with minisign by CI and checked against a pinned public key.
How it is built
The app is a Tauri (Rust) shell over a React and TypeScript front end, with a Python detection sidecar that runs locally and is never exposed to the network. Detection is layered. Presidio and spaCy with about 35 custom legal recognizers are the shipping default, an optional GLiNER zero-shot model sits on top, and an optional local LLM sits behind that.
I fine-tuned a GLiNER PII model against a hand-verified legal corpus. With that opt-in model, full-pipeline recall went from 72.4% to 83.3%, while full-pipeline F1 on the same run was 44.0%. The model is opt-in, so the default build runs without it.
Playwright cannot drive Tauri's WebView2, so I built the desktop end-to-end harness on WebdriverIO, attaching over the Chrome DevTools Protocol to the real release build.
I also removed an AGPL-3.0 dependency from the installer. A test that walks the built bundle enforces that it stays gone, and a CI gate fails a release if NER F1 drops more than 2 points below baseline.
What I Learned
A passing test suite proves less than it looks like. A finance export once wrote a full Social Security number into a file the app reported as safe. I found it in testing, because the detection results were clean and the exported file was not. After that I checked the file the app actually wrote, not the step that produced it.
Tech Stack
Tauri and Rust for the desktop shell, React and TypeScript for the interface, Python with spaCy, Presidio and GLiNER for the offline detection engine, SQLite for local storage, and WebdriverIO for desktop end-to-end testing.