Architecture

How Qvault keeps documents on your machine, how detection and export work, and what it does not (yet) do. This page describes the current design; the detailed engineering notes are in docs/qvac-integration.md in the repository.

Overview

Qvault is a Tauri v2 desktop application. The interface is React and TypeScript rendered in the operating system’s own webview (WebKit on macOS and Linux, WebView2 on Windows). Everything that touches document content runs in a Rust backend in the same process; the interface talks to it through Tauri’s in-process command bridge, not through HTTP.

The optional AI features (Layer 3 and OCR) run in a second, bundled process: the QVAC sidecar, built on Tether’s QVAC SDK and the Bare JavaScript runtime.

Document data moves only between local processes Your computer Qvault app · one process Interface React and TypeScript in the system webview; PDF.js Rust backend Layers 1–2, entity groups Redaction, export, verification Model downloads and checks SQLite + document files App data folder QVAC sidecar · separate process Bare runtime + Tether’s QVAC SDK OCR · Privacy Filter · language model Fixed list of allowed requests Opens no network sockets Model files Local disk, SHA-256 checked Hugging Face (optional) Only when you click Download Tauri commands JSON lines over stdio loaded from disk HTTPS, pinned revision
The interface and backend share one process; AI and OCR run in a separate sidecar reached only through standard input and output.

Document pipeline

  1. Extraction. PDFs are rendered with PDF.js, which also provides each piece of text with its position on the page. DOCX files are read as the ZIP of XML parts they are: the main document, headers, footers, footnotes, endnotes and comments, with tracked changes resolved so that the scanner and the redactor see exactly the same text.
  2. OCR. Pages without a text layer, and PNG, JPG and TIFF images, are rendered to images and sent to the sidecar for OCR. Pages count as scanned when they have fewer than 32 visible characters or images cover at least half the page; they are rendered with PDF.js at 200 DPI by default (configurable). The recognised words and their boxes are used for detection, the review highlights and placing redaction boxes. They are never written into an exported file: redacted scanned pages are burned in and re-checked with OCR, and no invisible text layer is added.
  3. Detection. Layers 1 and 2 always run; Layer 3 runs if it is enabled and a detector model is installed.
  4. Review. Findings are stored with their category, confidence, source layer and status (pending, approved, rejected). Nothing is applied until you approve it.
  5. Export. Approved redactions are applied and the result is verified before it is kept.

Detection layers

Layer 1: patterns

Compiled regular expressions (Rust’s regex crate, linear-time, no backtracking) for structured data. Matches get a confidence of 0.95. A priority order prevents double matches, such as a phone number inside a URL.

SetExamples
GlobalEmail, phone numbers, credit cards, IBAN, IPv4 addresses, URLs, monetary amounts, dates
United StatesSocial Security numbers, EINs
European UnionVAT identification numbers
BrazilCPF, CNPJ, CEP
GermanySteuernummer

Layer 2: heuristics

Rules for names, which no pattern describes: company names with legal suffixes (LLC, Inc, Corp, GmbH, SA, SARL, LTDA, EIRELI and others), names next to titles and roles (“John Smith, Manager”, “representada por…”), names in ownership tables and entity descriptions, and addresses. Name connectors such as de, da, dos, van, von, del are understood. Lists of stop words and stop phrases (“Section”, “Agreement”, “Limited Liability”…) keep legal vocabulary from being flagged. Confidence ranges from 0.75 to 0.92 depending on the evidence.

Layer 3: local AI

Layer 3 has two detectors. The default is OpenAI Privacy Filter (Apache-2.0), a token classifier built for finding personal data, run through QVAC’s ONNX add-on (@qvac/onnx) on the CPU. Qvault tokenizes each page in Rust, the sidecar returns the model’s raw scores for every token, and Qvault decodes them into spans with a port of OpenAI’s reference Viterbi decoder, so the spans match OpenAI’s own code on the same scores. The model reports a catch-all “account number” label, which Qvault re-types using the Layer 1 validators (CPF, CNPJ, NIF, IBAN and others) and nearby label words; otherwise it stays a bank account at low confidence. Filters drop titles in front of names, parties in case-law citations, statute numbers and legal words, using rules designed from evaluation errors. A page takes about 0.2 seconds and about 2 GB of memory. Privacy Filter does not look for company names, amounts or case numbers; Layers 1–2 cover those.

The second detector is a small language model, used for an optional thorough pass after Privacy Filter (or instead of it). Where the two detectors overlap, the longer span is kept. Every AI finding is labelled in the review list with the detector that found it.

For the language model, the text is split into chunks of about 150 words with 20 words of overlap. For each chunk the model is asked, with a fixed prompt and a JSON schema that constrains its output, to list personal data by category. Qvault then validates every answer (shape, known category, plausibility rules that reject whole sentences), finds every occurrence of each returned string in the chunk on word boundaries, drops anything Layers 1–2 already covered, removes legal vocabulary with a stop-list, and adds the rest for review. Confidence depends on the category and comes from how often the model’s additions were right in the evaluation: 0.80 for names, ID numbers, bank details and dates of birth, down to 0.50 for addresses and case numbers. Generation is deterministic (temperature 0, fixed seed, and the “thinking” mode of Qwen3 turned off), so the same document gives the same suggestions. Results appear page by page as they are found.

The recommended language model is Qwen3-1.7B (Q8_0, about 1.8 GB download, about 2.75 GB of memory). Qwen3 0.6B is offered for computers with little memory, and Qwen3 4B and 8B for workstations; the older Qwen2.5 models remain for existing installs. The Model Manager shows each model’s memory need and checks whether it fits before it can be selected. A chunk that fails is skipped. If Privacy Filter cannot run, the scan uses the language model if one is installed; if the sidecar is unavailable, Layer 3 stops for that scan and the viewer says so. The scan always completes with Layers 1–2.

Entity groups

Detected names, companies, numbers and addresses are collected into entities across all your documents, and spellings of the same entity are grouped. Entities of different categories are never compared.

Two different people written identically (for example “J. Santos”) cannot be told apart and end up in one group; nicknames such as “Bob” and “Robert” are not linked.

The QVAC sidecar

QVAC’s inference packages run on Bare, a small JavaScript runtime. Qvault bundles the Bare executable and a short worker script with the app and starts it only when an AI feature is first used. Design choices:

Verified export

PDF

Every page with at least one approved redaction is rendered to an image, the black boxes are burned into the pixels in Rust, and the page is replaced by a page that contains only that image. Its old content stream, fonts, form objects and annotations are dropped. Pages without redactions keep their original content. Across the whole file, the document information dictionary, XMP metadata, outlines, forms, name trees (embedded files and scripts), open actions and the structure tree are removed.

Export is refused if any approved redaction could not be placed as a rectangle, if a rasterised page holds more occurrences of an approved string than were covered, or if an approved string appears on a page that is not being redacted (a likely missed detection you should review).

DOCX

Every XML part is parsed and rewritten with a real XML reader and writer, never by raw string replacement. Tracked changes are accepted, so deleted revision text disappears. Within the run-concatenated text of each part, every occurrence of each approved string is replaced; the replacement keeps the formatting of the first run and the rest of the span is removed from the following runs. Alt text, tooltips and field codes get the same treatment. Comment and revision authors are blanked, personal fields in the document properties are emptied, and the thumbnail image is removed.

Verification

After writing, Qvault reads the exported file back, extracts every piece of text it can find, and deletes the file with an error if any approved string survives. The same check runs in the automated test suite.

Storage

Qvault keeps its data in the per-user app data folder: a SQLite database (documents, findings, cross-document entities and their groups, optional entity embeddings, page text and an audit log) and copies of the imported document files. The audit log records actions and metadata, such as model downloads, scans and exports, but not document text. Clearing all data in Settings removes everything.

Network use

Qvault makes no network requests of its own except a model download you start in the Model Manager. That download goes to a pinned, revision-locked Hugging Face URL over HTTPS; the size is shown before you confirm, and the file is checked against a pinned SHA-256 before it is used. There is no telemetry, crash reporting, update check or account. The OCR models are not downloaded by the app at all: you fetch them yourself and import them (see Models and setup).

Limits and open work