redacted + redaction_sidecar.
What gets redacted automatically
Detection patterns for structured identifiers are ported from
Microsoft Presidio (MIT). Contextual
names and street lines are also scored by Rampart
(CC BY 4.0) — a ~15 MB MiniLM NER model that runs entirely in the browser via
WASM. Monetary amounts, dates, BIC/SWIFT codes, and ISO currency codes
are kept so balance-math and structural fraud signals still work. The playground
preview still lets the user drag boxes over anything the detectors missed.
Token semantics
- Same original value → same token everywhere (
1234-5678and1234 5678share[ACCT_1]). - Different values → different indices (
[NAME_1]vs[NAME_2]is a real inconsistency). - The engine is told these rules and will not treat placeholders themselves as fraud.
Coverage tradeoffs
Kept- Content checks on non-redacted text
- Cross-field consistency via tokens
- Font forensics on remaining text
- Structural signals via the sidecar (version count, editors, font hints)
- Incremental-save history: each save of the original is analyzed locally with the same
token registry, so a PII value edited between saves surfaces as a token diff
(
[NAME_2]removed,[NAME_1]added) without shipping old bytes - Incremental-edit content: text spans added by later saves (edited amounts, dates) are diffed locally and shipped with PII tokenized — non-PII edits like value changes stay verbatim, so “the net pay was edited after creation” still reaches the engine
- Editing-tool fingerprints per save, scraped from each save’s appended raw bytes (catches e.g. an Adobe touch on a Chrome-made PDF whose parsed metadata still claims the original producer)
- The playground X-ray viewer: each incremental save is rendered and redacted on-device (same tokens, same main-pass coverage including OCR hits and signatures, same manual boxes) and uploaded alongside the final document, together with a copy of the final pages with changed regions boxed in red — computed with the same last-two-saves cardinality diff the server runs on unredacted uploads — so the version slider and “pages flagged” navigation work identically, without any original bytes leaving the device
- Visual / AI analysis outside replaced regions
- Paste-detection features at replaced regions as numeric pixel forensics (noise / background / sharpness / ELA / baseline deltas)
- Provenance verdicts the engine normally reads off the raw file, which flattening
destroys: C2PA AI-generation markers (PDF info/XMP and DCT-embedded full-page
images), the Photoshop-authored JPEG marker, and the empty-shell revision
structure (see
ai_provenance,embedded_ai_provenance,photoshop_saved,text_layer). These decide the result on their own, so omitting them from the sidecar means an AI-generated or Photoshop-authored document can come back clear. FlateDecode-compressed embedded images are a residual gap — the client cannot inflate them in-browser, so only DCT-embedded JPEGs are scanned.
- Open-ended visual judgment of the replaced patches beyond those features
- Semantic checks on the redacted values themselves
- Edit-history analysis beyond what the sidecar captured
Playground
On the Documents upload modal, enable Redact before upload. Each file is OCR’d and redacted locally; you preview the result (and can drag extra boxes) before anything is sent. For PDFs with incremental saves, the browser also uploads a redacted render of each save plus a changed-region highlight of the final pages (redaction_versions_N /
redaction_highlight_N form fields), which power the X-ray version slider on the result
page. Every covered region from the main pass (text-layer hits, OCR-only hits, signatures,
and manual boxes) is re-projected onto those historical renders so scanned PII and ink
cannot leave the device under the “Previous save” side.
API: redacting on your side
No SDK is required. Redact locally, then submit the redacted bytes with:redacted: true alone is not enough to activate the engine’s redaction-artifact
suppression (the prompt addendum that tells the model to ignore black boxes,
overlays, crisp placeholder labels, etc.). The server only emits that addendum when
extracted content contains [PREFIX_N] placeholders, or the sidecar’s
pixel_forensics inventory lists covered regions with both a token and page.
A bare tokens list (or a bare flag with an empty sidecar) is not verification —
visual fraud cues stay valid.
Recommended open-source tooling for server-side redaction:
- Microsoft Presidio — detection + anonymization to typed placeholders
- A PDF renderer (e.g. pypdfium2) if you need to flatten scanned pages safely
Token format
Placeholders must look like[PREFIX_N] where PREFIX is one of
NAME, ACCT, ROUTING, CARD, SSN, EMAIL, PHONE, IBAN, ADDR, ID, MRZ
(or your own), and N is a stable 1-based index for that prefix. Same original
value → same token — including across saves in pdf.versions, which is what makes
version_diffs meaningful.
Sidecar schema (all fields optional)
new_texts are scrubbed for known registry
values (≥2 characters) before they leave the device; a residual gap remains for PII that
existed only in a historical save and was never detected on the final document (so it
never entered the registry).
Provenance fields
These fields carry verdicts the engine would otherwise read off the raw bytes. Flattening destroys all of them, so if you omit them a document the engine would have rejected outright reaches the model as an ordinary picture and can come back clear.pdf.text_layer only convicts alongside eof_count > 1, every earlier revision in
versions reporting page_count 0 (or failing to parse), final_span_count of at least
20, and 48h or more between creation_date and mod_date.
Response
serialize_check exposes document.redacted: true so consumers know coverage was
reduced. Extracted fields may contain tokens, e.g. "name": "[NAME_1]".
Attribution
- Structured-PII recognizers adapted from Microsoft Presidio (MIT).
- Contextual name/address NER: Rampart by National Design Studio (CC BY 4.0). Model weights are self-hosted under
/static/vendor/rampart/.