Permanent, local PDF redaction for macOS.
Masker finds repeated personal information, lets you review every proposed mask in a continuous PDF view, and creates sanitized copies without uploading the document or its contents.

The screenshots use a generated Joe Farmer tax return, not a real financial document.
Preview's redaction tool is effective, but selecting every occurrence by hand is tedious. Masker automates the repetitive part while keeping the consequential decision - what gets removed - visible and reversible until export.
Read the short development story: Nothing Personal: I built a local PDF redactor.
JOE AND MARY FARMER can also find Joe Farmer.MERRILL LYNCH and right-aligned table amounts visible.FORM 8879.Masked PDFs.
Incremental search finds a synthetic name that was not in the original mask set.

Labels preserve useful relationships without retaining the original PII.
Download the universal macOS build from the latest release, unzip it, and open Masker.app. It includes native Apple Silicon and Intel executables.
The downloadable build is ad-hoc signed, not Apple-notarized. On first launch, macOS may require you to right-click the app and choose Open. You can also build it directly from source.
Requirements: macOS 13 or newer and Xcode Command Line Tools. Full Xcode and third-party dependencies are not required.
git clone https://github.com/cpatil/masker.git
cd masker
./build.sh
open Masker.app
The build uses SwiftUI, PDFKit, Vision, and Core Graphics from macOS.
Maintainers can create the universal release archive with ./package-release.sh.
Choose Discovery Mode... and select a folder. Masker finds every PDF below it, including nested folders. The session starts with the values, labels, and detector settings already in section 2. Move back and forth freely while growing that shared set, then export it from section 2. Discovery is saved locally and can be resumed after restarting Masker.
Choose Batch Convert..., select a folder, and select a mask-set JSON. Masker scans every PDF below the folder and automatically applies every match from that set. Separate outputs are written under Masked PDFs, with the original subfolder layout preserved; a PDF with no matches is still copied into the output hierarchy. Source PDFs are never combined or overwritten, and the output folder is excluded from later runs.
Discovery fingerprints each source PDF when the session starts and refuses to open a document that changed afterward. Batch conversion reports processed and failed counts in the app. Review the mask set before running a batch, then inspect the exported pages before sharing them.
The optional MCP companion lets Codex open Masker, advance between opaque discovery IDs, start local scans, begin Batch Convert, and report counts. It has no tool for reading filenames, paths, PDF text, mask values, labels, search results, or screenshots. Masker handles the files and the user handles review.
The native app still has no third-party dependencies. The MCP companion requires Node.js 20 or newer:
cd mcp
./install.sh --configure-codex
Restart Codex after installation. See mcp/README.md for the tool list and privacy boundary.
Client or Account 1 beside a match. Use the adjacent text-style menu to adjust font, maximum size, width, and alignment. Leave the label empty for a plain black mask.Masker never overwrites an original. The default destination is a Masked PDFs folder beside the input, and it can be changed before export. Output names end in _masked.pdf; if that name exists, a number is added.
Each exported page is rebuilt from sanitized pixels at 300 DPI using Core Graphics. The original PDF objects are not copied into the output. Masker then reopens the result, verifies that active content is gone, and compares the coarse visual structure of every page with the expected masked source before reporting success.
This intentionally removes the searchable and editable text layer. Replacement labels are rendered into the sanitized page image rather than added as PDF text. Preview and other apps may still use OCR to search text that remains visibly rendered on the page; masked values are removed from those pixels. Rasterization also increases file size compared with object-level redaction.
Mask-set exports contain values, optional replacement labels and appearance, detector settings, and account-suffix exceptions. They do not contain a PDF filename, source path, page numbers, coordinates, or orientation, so the same JSON can be imported anywhere. Import deduplicates values case-insensitively, keeps an existing label, fills an empty label from the imported set, enables detectors selected in either set, and combines exceptions. Label orientation is inferred again from each occurrence. Older per-PDF Masker JSON files remain importable.
./test.sh
The self-test generates its own searchable, scanned, and rotated PDFs. It covers the JOE AND MARY FARMER to Joe Farmer name variant, identifier variants, institution suffixes, exceptions, cached OCR search, generic mask-set JSON and detector settings, repeated file loading, merge behavior, pixel-rendered labels, click-to-review selection, resumable recursive discovery, batch conversion, status sanitization, permanent export, and residual-text checks. The MCP tests exercise both supported protocol eras and verify the status privacy boundary. No tax documents or private fixtures are stored in this repository.
An optional local-only corpus test is also available:
./private-smoke-test.sh /path/to/private/pdf/folder
It reports aggregate counts, creates its temporary output outside the repository, and removes that output after validation.
41 commits
Hacker News (1)
Swift
92.0%
JavaScript
5.4%
Shell
2.6%
Permanent, local PDF redaction for macOS.
Masker finds repeated personal information, lets you review every proposed mask in a continuous PDF view, and creates sanitized copies without uploading the document or its contents.

The screenshots use a generated Joe Farmer tax return, not a real financial document.
Preview's redaction tool is effective, but selecting every occurrence by hand is tedious. Masker automates the repetitive part while keeping the consequential decision - what gets removed - visible and reversible until export.
Read the short development story: Nothing Personal: I built a local PDF redactor.
JOE AND MARY FARMER can also find Joe Farmer.MERRILL LYNCH and right-aligned table amounts visible.FORM 8879.Masked PDFs.
Incremental search finds a synthetic name that was not in the original mask set.

Labels preserve useful relationships without retaining the original PII.
Download the universal macOS build from the latest release, unzip it, and open Masker.app. It includes native Apple Silicon and Intel executables.
The downloadable build is ad-hoc signed, not Apple-notarized. On first launch, macOS may require you to right-click the app and choose Open. You can also build it directly from source.
Requirements: macOS 13 or newer and Xcode Command Line Tools. Full Xcode and third-party dependencies are not required.
git clone https://github.com/cpatil/masker.git
cd masker
./build.sh
open Masker.app
The build uses SwiftUI, PDFKit, Vision, and Core Graphics from macOS.
Maintainers can create the universal release archive with ./package-release.sh.
Choose Discovery Mode... and select a folder. Masker finds every PDF below it, including nested folders. The session starts with the values, labels, and detector settings already in section 2. Move back and forth freely while growing that shared set, then export it from section 2. Discovery is saved locally and can be resumed after restarting Masker.
Choose Batch Convert..., select a folder, and select a mask-set JSON. Masker scans every PDF below the folder and automatically applies every match from that set. Separate outputs are written under Masked PDFs, with the original subfolder layout preserved; a PDF with no matches is still copied into the output hierarchy. Source PDFs are never combined or overwritten, and the output folder is excluded from later runs.
Discovery fingerprints each source PDF when the session starts and refuses to open a document that changed afterward. Batch conversion reports processed and failed counts in the app. Review the mask set before running a batch, then inspect the exported pages before sharing them.
The optional MCP companion lets Codex open Masker, advance between opaque discovery IDs, start local scans, begin Batch Convert, and report counts. It has no tool for reading filenames, paths, PDF text, mask values, labels, search results, or screenshots. Masker handles the files and the user handles review.
The native app still has no third-party dependencies. The MCP companion requires Node.js 20 or newer:
cd mcp
./install.sh --configure-codex
Restart Codex after installation. See mcp/README.md for the tool list and privacy boundary.
Client or Account 1 beside a match. Use the adjacent text-style menu to adjust font, maximum size, width, and alignment. Leave the label empty for a plain black mask.Masker never overwrites an original. The default destination is a Masked PDFs folder beside the input, and it can be changed before export. Output names end in _masked.pdf; if that name exists, a number is added.
Each exported page is rebuilt from sanitized pixels at 300 DPI using Core Graphics. The original PDF objects are not copied into the output. Masker then reopens the result, verifies that active content is gone, and compares the coarse visual structure of every page with the expected masked source before reporting success.
This intentionally removes the searchable and editable text layer. Replacement labels are rendered into the sanitized page image rather than added as PDF text. Preview and other apps may still use OCR to search text that remains visibly rendered on the page; masked values are removed from those pixels. Rasterization also increases file size compared with object-level redaction.
Mask-set exports contain values, optional replacement labels and appearance, detector settings, and account-suffix exceptions. They do not contain a PDF filename, source path, page numbers, coordinates, or orientation, so the same JSON can be imported anywhere. Import deduplicates values case-insensitively, keeps an existing label, fills an empty label from the imported set, enables detectors selected in either set, and combines exceptions. Label orientation is inferred again from each occurrence. Older per-PDF Masker JSON files remain importable.
./test.sh
The self-test generates its own searchable, scanned, and rotated PDFs. It covers the JOE AND MARY FARMER to Joe Farmer name variant, identifier variants, institution suffixes, exceptions, cached OCR search, generic mask-set JSON and detector settings, repeated file loading, merge behavior, pixel-rendered labels, click-to-review selection, resumable recursive discovery, batch conversion, status sanitization, permanent export, and residual-text checks. The MCP tests exercise both supported protocol eras and verify the status privacy boundary. No tax documents or private fixtures are stored in this repository.
An optional local-only corpus test is also available:
./private-smoke-test.sh /path/to/private/pdf/folder
It reports aggregate counts, creates its temporary output outside the repository, and removes that output after validation.
Hacker News (1)
41 commits
Swift
92.0%
JavaScript
5.4%
Shell
2.6%