Repository navigation
Release History
Everything Anonify has shipped, fixed and hardened, release by release, with the reason each change was made. The authoritative, per-release changelog is CHANGELOG.md, and each release's notes are on the Releases page. This page adds context and links each change to the wiki page that explains it.
Current release: v1.10.0 (2026-09-29).
| Version | Date | Headline |
|---|---|---|
| 1.10.0 | 2026-09-29 | Batch downloads in the original format (a mailbox comes back as a mailbox) or as processed files in provenance folders. Batches over 50 documents are exported whole. Non-Latin-1 filenames download correctly. |
| 1.9.3 | 2026-09-28 |
pnpm bench:models verifies each model before benchmarking it. |
| 1.9.2 | 2026-09-28 |
pnpm bench:models and pnpm bench:charts: models benchmarked on the labelled corpus. |
| 1.9.1 | 2026-09-28 | A synthetic, labelled PII corpus and a scorer (pnpm corpus:*). |
| 1.9.0 | 2026-09-27 | Search, RegEx rules, global rules, the Hush review assistant, a shortcut sheet, word-level click-to-redact. |
| 1.8.0 | 2026-09-27 | OpenRouter, Synthetic, LM Studio, llama.cpp, any OpenAI-compatible endpoint, ChatGPT subscription sign-in, the pnpm ai command. The standalone build no longer copies the working tree. |
| 1.7.0 | 2026-09-27 | Streaming I/O, part 2: a page-at-a-time editor, ranged reads of Office and PDF files, streamed downloads. |
| 1.6.3 | 2026-09-23 | OCR misconfiguration fails at once and names a misspelled Mistral key. |
| 1.6.2 | 2026-09-23 | Local vision models (Qwen3-VL through Ollama) work: regions on a 0–1000 grid. |
| 1.6.1 | 2026-09-23 | CI tests the model pass with a scripted provider and OCR against a pinned model. |
| 1.6.0 | 2026-09-23 | Chunked encryption (1 MB authenticated chunks) and streamed uploads, mailboxes, email and text. Each stored file is bound to its own name. |
| 1.5.0 | 2026-09-23 | Multi-copy exports fixed and downloadable as one zip. PDF redaction box placement security fix (GHSA-h3hv-rpqw-7p84). Dependency advisories cleared. |
| 1.4.0 | 2026-09-23 |
pnpm setup pages large model catalogs, shows list prices, and can finish a local install for you. |
| 1.3.0 | 2026-09-12 | Bring your own AI provider: official AI SDK adapters, Azure, Bedrock, Vertex, local Ollama. MinIO replaced by RustFS. |
| 1.2.0 | 2026-09-08 | Release notes written from changelog.d/ fragments. |
| 1.1.1 | 2026-09-08 | HTML email bodies analysed as markdown; a message folds into its parts. Releases cut by a workflow. |
| 1.1.0 | 2026-09-06 | MBOX mailboxes accepted; each opens as a batch with one document per message. |
| 1.0.0 | 2026-09-06 | First release: ten formats, batches, presets, anonymisation methods, reports, durable processing, verified exports. |
Upgrades that need a database migration (pnpm db:migrate:deploy; the container image runs it for you): 1.6.0, 1.7.0, 1.9.0. Every migration in prisma/migrations/ is listed under Data Model.
-
The batch download asks what to deliver.
-
Original format (the default) returns what was uploaded. A mailbox comes back as
inbox-redacted.mbox. It is rebuilt from its messages' verified exports, with theFromseparator lines written fresh rather than copied, and the whole mailbox is verified again before it is sent. A message comes back carrying its redacted attachments. A single file comes back as itself. - Processed files is every document's own output, in folders that mirror where it came from. Folders are named by message number and MIME part path, never by subject or filename, because both are routinely the personal data being removed.
- Both puts the two layouts side by side in one zip.
-
Original format (the default) returns what was uploaded. A mailbox comes back as
- A message left out of a rebuilt mailbox is named in the batch report. So is the check a mailbox failed when its own verification withholds it.
- An expanded mailbox in the workspace offers Download redacted mailbox directly.
- The batch archive is no longer flat.
GET /api/batches/:id/downloadtakesoutput=original|processed|both(defaultoriginal), and a single upload comes back as that one file rather than a zip.
- A batch of more than fifty documents (so any mailbox of more than fifty messages) was exported, downloaded and listed as only its first fifty. From the 51st document on, the workspace also lost its place in the batch. Every document is now included and navigable.
- Downloading or restoring a file whose name is outside Latin-1 (
收件箱.mbox,Отчёт.pdf, an emoji) no longer fails, and the file saves under its real name.
Detail: Batches and Mailboxes · Export and Anonymisation.
The architecture has always claimed that running patterns first, asking a model only the contextual question and expanding repeats locally costs far fewer tokens at comparable quality. This series built the tooling to test that claim.
1.9.3 — 2026-09-28 · #59
-
Fixed:
pnpm bench:modelsverifies each model before its first phase, the waypnpm ai verifydoes, for that run only. Before, a model other than the one verified in.envwas treated as unsupported, and every document was scored on the pattern detectors alone.
-
Added:
pnpm bench:modelsbenchmarks the models in.env(or aBENCH_MODELSlist) on the labelled corpus. It measures recall per category, tokens and cost per document, throughput as concurrency rises, and the tokens the deterministic pass saves against the same model with it switched off.pnpm bench:chartsdraws light and dark charts into the README. A model benchmarked later is added beside the earlier ones without changing their results.
1.9.1 — 2026-09-28 · #57
-
Added:
pnpm corpus:generatebuilds a synthetic, labelled PII corpus through the Claude Code or Codex CLI you are already signed in to. Every email address and phone number comes from a range reserved for fiction, and so does every card and ID where such a range exists. A document containing a real-looking email, phone, URL or IP is rejected.pnpm corpus:validatehas a model from another family review the labels. CI fails if a committed corpus file holds a value outside the reserved ranges.pnpm corpus:scoremeasures precision, recall and a weighted cost (a missed ID costs more than a false alarm), with patterns alone or with the configured model.--format pdf|docx|eml|csv|xlsx|txtrenders the corpus and measures it through the same extraction an upload goes through.
Detail: Testing and Benchmarks.
2026-09-27 · #142, PR #146 · needs a migration
-
Search (
Ctrl/⌘+For/). A find strip docked above the document searches the whole document on the server, not just the loaded pages, in every format. It offers match case, whole word and RegEx, an "N / M" counter announced to screen readers, amber highlights, a results panel grouped by page, and hit counts on page thumbnails. From a hit, Redact this or Redact all (which opens a rule), with optional counts across the batch. Searches and rule previews have their own rate limit,ANONIFY_RATE_LIMIT_SEARCH. - RegEx rules on a linear-time engine (RE2): a pattern cannot hang the server. Backreferences and lookaround are refused, and a rule that matches too much applies nowhere rather than partway. Every rule is previewed with its count and matches in context before anything is written.
- Global rules apply to every document you upload from now on. The pattern is encrypted at rest and deleted after 30 days unused. The session cookie now renews when you upload or change a rule, so rules in use are not lost when the cookie turns 30 days old. Global rules export and import as JSON.
- A Rules tab in the inspector: switch off, edit with a fresh preview, or remove with everything the rule redacted.
-
Hush (
Ctrl/⌘+K), a review assistant that reads, searches and scans the document with tools. It lists where a value occurs and whether each occurrence is covered, finds what the detectors can see that nothing covers yet, explains suggestions, and proposes rules and redactions. Every change is a card that waits for Approve. Approvals are signed by the server and re-checked before they run. Hush asks once before it first reads a document, renders replies as Markdown that cannot load links or images, stays within the spend cap, and has a per-visitor daily token allowance,ANONIFY_QUOTA_ASSISTANT_TOKENS. It also offers rules locally, with no provider, once you have hand-redacted the same shape of value a few times. -
?lists every keyboard shortcut, and zoom has a slider. - A new failure code,
rule-too-broad: a batch or global rule that matches too much of a newly processed document fails that document. It can still be reviewed, and it retries once the rule is narrowed.
- Clicking text redacts the value under the pointer (an ID, an email, a name) instead of the whole span. In a text file a span is a whole line, so one click used to redact the line. Grouped values (card numbers, spaced IBANs, phone numbers) are taken whole; names are taken a word at a time. Dragging redacts exactly the selection. PDFs get the same word-level click from their measured character positions.
- The canvas shows suggestions and accepted redactions over exactly the characters they cover. Before, any line with a redaction on it was drawn fully blacked out, which overstated what the export removes.
- Plain-text, RTF and slide pages have thumbnails, and changing page opens the new page at its top.
Detail: Search and Rules · Hush Assistant · Review Workspace.
- Setup and the new
pnpm aicommand offer OpenRouter, Synthetic, LM Studio, llama.cpp, and any OpenAI-compatible endpoint byAI_BASE_URL. Each gets the same model discovery, verification and visible "unsupported" degradation as the existing providers, and adding a vendor is one row in a table. LM Studio and llama.cpp are treated like Ollama: no spend, one request at a time, reachable from Docker. -
pnpm ai login --provider openaisigns in with a ChatGPT subscription. The token is sealed in the database, never written to.env. Read the caveat first: it uses Codex CLI's public client, and OpenAI's terms decide whether a plan may be used this way. Prefer an API key for anything deployed. There is no Anthropic subscription sign-in, because Anthropic's terms do not allow it. -
pnpm ai verifyverifies a model and writes it to.envwithout running the rest of setup.pnpm ai statusshows what is in force. - A model that fails verification now says why: which check failed, the HTTP status, and what the provider said, with credentials redacted.
- Link previews of a self-hosted instance point at its own address. Set
ANONIFY_PUBLIC_URLand rebuild the image. - The expiry sweep no longer fails with "No record was found for a delete" when a document is removed while the sweep is running. A document already gone counts as deleted, and one document that cannot be purged no longer stops the rest of the run.
- A standalone build (
NEXT_OUTPUT=standalone) no longer copies the working tree into its server output. Before, it copied the source, tests, docs, every document in local storage (.anonify-storage), downloaded OCR models, and.envwith the key that opens those documents. Container images and Vercel deployments were not affected. If you built standalone on a machine that stores documents locally and copied.next/standaloneelsewhere, delete that copy's.anonify-storage.
Detail: AI Providers · Deployment.
2026-09-27 · #129 · needs a migration (adds Document.normalizedIndex)
- Documents are read a piece at a time instead of whole. The editor loads the pages you look at (an outline, then ≤ 24 pages kept). Word, PowerPoint, Excel and PDF files are read by ranged reads, and pictures and other media are no longer unpacked. Emails are parsed without holding their attachments. Downloads, single and batch, stream while their checksum is verified. Output is unchanged. Documents processed before the upgrade keep working and are read whole.
- A download the browser abandoned partway no longer leaves the stored file's handle or storage connection open.
Detail: Streaming IO.
1.6.3 — 2026-09-23 · #134
-
Fixed: a document that needs OCR fails at once as a
configurationproblem whenOCR_PROVIDER=mistralhas noMISTRAL_API_KEY. Before, it was retried five times and reported as an unknown error with a retry button. A Mistral key set under another name (such asMISTRAL_AI_KEY) is named in the error.
1.6.2 — 2026-09-23 · #132
- Fixed: image and PDF-page analysis works with local vision models such as Qwen3-VL through Ollama. Regions are requested as whole numbers on a 0–1000 grid, the convention those models answer in. Before, they were asked for 0–1 fractions, which they ignored, so validation failed and every image had no suggestions. An over-long region explanation is shortened rather than discarding the region.
- Added: the model-assisted analysis pass is tested in CI without an API key or a network call (a scripted provider). The tests cover the rule that a value the model reports but the document does not contain is discarded, and that a failing provider still leaves the pattern detections.
- Added: scanned images are tested against the real OCR engine in CI, including where each word's redaction box lands.
-
Changed: uploads, mailbox and email-attachment expansion, and plain text, CSV and TSV extraction stream instead of holding whole files in memory, so a large mailbox no longer has to fit in memory. New documents are stored in 1 MB authenticated chunks (envelope
v1). Documents already stored stay readable exactly as they are (v0). Tuned withANONIFY_ENCRYPTION_CHUNK_SIZEandANONIFY_STREAM_MEMORY_BUDGET. - Security: each stored file is bound to the name it was written under. Someone with write access to the storage bucket can no longer swap one of a document's encrypted files for another (say, its report for its source). This covers documents uploaded from 1.6.0 on.
Detail: Storage and Encryption.
- An export with a second copy can be downloaded as one zip holding every copy and its report. The vault is left out on purpose and is still downloaded on its own.
- Exports with a second copy (pseudonymized, tokenized or encrypted) work again. Every one of them had failed with "The export could not be generated". A malformed export request is answered
400, not500. - Accept all leaves ignored suggestions alone instead of redacting them after all. An ignored suggestion is marked and dimmed in the inspector.
- The export dialog scrolls instead of growing past the screen. With two copies, each copy's checksum and report fold away while its downloads and vault stay in view.
- The documents list says a review is finished instead of showing "0 accepted" under a full progress bar. The upload panel no longer jumps or sits at 0% when an upload starts.
- Undo and redo save only the redactions they changed. Before, pressing Ctrl+Z in a tab holding stale decisions could overwrite every decision on the document, and undoing a method change was never saved.
- PDFs that use the standard fonts without embedding them, or that need the bundled CMaps, render and extract on the server with the right font data again.
- GHSA-h3hv-rpqw-7p84: PDF redaction box placement. Boxes are now placed from the font's own character widths and reach below the baseline. On a line of proportional text a box could sit a few characters away from its value and leave descenders showing, and the export burned in that same box, so part of an accepted value could survive in the exported PDF. PDFs analysed before 1.5.0 are covered a whole text run at a time instead. Upload them again for boxes that fit the value.
-
Next.js 16.3.6 fixes two critical and several high-severity advisories in 16.2.x. Vulnerable transitive copies of undici, mysql2, nanoid, deepmerge-ts, devalue, uuid, lodash and postcss were replaced, and
pnpm auditreports nothing.
-
pnpm setuppages and searches large model catalogs and shows each model's advertised capabilities and context window apart from what setup verified. It can finish a local install for you: start Postgres and RustFS, migrate, warm OCR and start the app, or run everything in Docker, with retry, skip or stop on any step that fails. -
pnpm setupshows list prices for models on the Vercel AI Gateway, DeepInfra (and, from 1.8.0, OpenRouter), labelled with source and age. It offers, never automatically, to save the chosen model's price toAI_MODEL_PRICES. Model lists are cached in.cache/models(orANONIFY_MODEL_CACHE_PATH);pnpm models:warmrefreshes them.
2026-09-12 · #115, #117, PR #116
- Added: AI detection can use official AI SDK providers, including Azure, Bedrock and Vertex, or a local Ollama server. Setup discovers models, blocks incompatible choices, and verifies structured output and image support before saving. Existing AI Gateway configurations keep working.
- Changed: self-hosted object storage uses pinned RustFS images instead of the archived MinIO Community Edition tooling. It reuses the existing data volume and initialises its bucket idempotently. See Deployment for the one-way migration.
-
1.2.0 (2026-09-08, #105): release notes are written by the people who make the changes. Every version-moving pull request carries a fragment in
changelog.d/, and the release workflow assembles them. -
1.1.1 (2026-09-08, #101, #104):
- Added: email bodies written in HTML are converted to markdown before analysis, so detection reads the text a person sees rather than the markup around it. A message folds into its parts (headers, bodies, alternatives, attachments).
- Changed: releases are cut by a workflow. A merged pull request's labels decide which part of the version moves.
- MBOX mailboxes are accepted as an upload, and open as a batch with one document per message.
2026-09-06
- Support for CSV, TSV, plain text, RTF, EML and PPTX, alongside PDF, DOCX, XLSX and images (#72, #47, #49).
- An email attachment is a document in its own right, and a message carrying attachments opens as a batch (#74, #75).
- Anonymisation beyond masking: a method per category, consistent surrogates for a repeated value, variants, and restore (#87, #95).
- Export reports, batch review, and detection presets named for what they look for (#50, #52, #54, #71).
- Bounded batch concurrency instead of one document at a time (#94).
- External services paced and retried, with configurable limits (#96).
- A document that fails partway reports what actually happened, and only the stages that can succeed are retried (#69).
- Deployable on Vercel Hobby (#53).
The first release came after a long audit of what was half-wired, insecure or untested. These closed issues are worth knowing because the same failure shapes recur:
| Issue | What was wrong | What changed |
|---|---|---|
| #7 | The expiry sweep never ran outside Vercel, so nothing was ever deleted |
pnpm cleanup and an opt-in Compose scheduler
|
| #14 | DOCX headers and footers were swept on export but never shown for review | Every text-bearing part is extracted and reviewable |
| #15 | A hidden worksheet was rendered exactly like a visible one | Hidden / very-hidden sheets, rows and columns are marked |
| #17 | Fixed-window rate limiting allowed double the rate across a boundary | Token buckets that refill continuously |
| #18, #19 | A self-hosted install rationed itself with the demo's quotas and limits | Deployment profiles (self-hosted, demo) |
| #20 | Spreadsheet cells were charged by used-range area | Only filled cells are charged |
| #27 | YAML read the ENCRYPTION_KEY placeholder as the integer 0
|
assertMasterKey() refuses to boot on a malformed key |
| #39 | Images inside a PDF were never analysed | Analysed |
| #41 | Every spreadsheet export failed once a column was accepted | Fixed, with tests |
| #44 | The model proposed columns that do not exist | Proposals are located against the sheet |
| #51 | Uploads were refused on the browser's guess at the content type | Magic bytes decide, at ingest |
| #62 | Raw internal error messages were stored and shown | A fixed table of failure codes |
| #64 | Retry had no backoff and re-ran failures that could not succeed | Paced backoff and FatalError
|
| #74 | EML attachments were carried through unredacted | Attachments become child documents |
#3 Neon-only database adapter → postgres or neon · #4 migrations committed · #5 pnpm setup · #6 a container image · #8 the Compose stack exercised in CI · #9 .env.example committed · #21 Prisma out of the browser bundle · #22 pdfjs in a bundled server · #24 OCR model no longer committed · #25 an opt-in scheduler no longer breaks Compose · #53 Vercel maxDuration · #60 typecheck on a fresh checkout · #73 tesseract.js in the standalone build.
#10 scanned PDFs OCR'd · #11 blur and pixelate reachable · #12 AI cost shown · #13 real page thumbnails · #16 OCR suggestions placed on the image · #26 a scroll area that scrolls · #31, #34, #36 DOCX pagination and rail · #46, #48 the grid shows what export will do · #56 keyboard-reachable redactions · #58 selectable OCR provider · #61 the failure screen and retry reachable.
#67 Apache-2.0 licence · #68 security policy, code of conduct, templates · #76–#84 API, data model, client state, editor, storage, failure codes, boot sequence and security-internals docs · #91 README and About.
| Advisory | Fixed in | Summary |
|---|---|---|
| GHSA-h3hv-rpqw-7p84 | 1.5.0 | PDF redaction boxes placed by a proportional approximation could miss characters of an accepted value, and the export burned in the same box. Boxes now come from font widths and include descenders. Re-upload PDFs analysed before 1.5.0. |
| (no advisory; release note) | 1.6.0 | Stored objects were not bound to their logical names, so a party with bucket write access could swap one of a document's objects for another. Chunked v1 objects bind the name into every chunk's AAD. |
| (no advisory; release note) | 1.8.0 | A standalone build copied the working tree, including local document storage and .env, into its output. |
To report one: Security Model §Reporting.
Anonify: AI proposes, the application applies, and only what a person accepted is removed.
Repository · Report a bug · Request a feature · Security policy · Apache-2.0
This wiki describes v1.10.0 and summarises and links the in-repo documentation. Where the two disagree, the files under docs/, CHANGELOG.md and SECURITY.md are canonical.
Getting going
Using it
- Supported Formats
- Review Workspace
- Search and Rules
- Hush Assistant
- Redaction Presets
- Export and Anonymisation
- Batches and Mailboxes
How it works
- Architecture
- Processing Workflow
- Redaction Engine
- Storage and Encryption
- Streaming IO
- Data Model
- API Reference
- Security Model
When it breaks
Contributing
Repository · Issues · Releases · Changelog