PII Masking
Captain can strip personally identifiable information (PII) from content at
index time. Set mask_pii: true on any indexing request and Captain detects
and masks PII in the parsed text, the layout regions, and the pixels of
images, before anything is embedded or written to the search index.
It is opt-in and per-job: the flag defaults to false, so existing indexing is
unchanged, and only the files you index with mask_pii: true are masked.
What gets masked
Masking covers three layers, so PII cannot leak through any of them:
-
Text: the parsed content of documents, media transcripts, and the text Captain reads out of images. Detected PII is replaced with an entity tag, for example:
becomes
-
Images: for standalone image files and images embedded in PDFs, PII text that is visible in the pixels is covered with solid boxes (shown above). This applies to both the stored, retrievable image and the visual embedding.
-
Regions: when you query with
include.regions, the per-region text is masked as well, so the layout view carries no raw PII either.
What Captain detects
Captain masks a curated set of high-value identifiers. The same detection rules run on text and on the text Captain reads out of image pixels. Each category is listed by the tag that replaces it in masked output.
Identity
Contact
Financial
Government and professional IDs
Compliance
The compliance entities are label-anchored: a bare number is never masked on its own, so loan numbers, NAIC codes, MIC numbers, and dollar amounts on the same form stay readable. Company names in ordinary prose (a header or a letterhead) are only masked when a bond label identifies them.
Network
In masked output, each detected value in chunk text (and in region text) is
replaced with its tag, for example <US_SSN> where the number appeared. In
images, the pixels of each detected value are covered with a solid black box.
If a PII category matters to your use case and is missing here, tell us at support@runcaptain.com and we will look at adding it.
What is not masked
Captain deliberately does not mask dates or generic numbers. Masking those would corrupt ordinary content (a revenue figure, a row count, a timestamp) without meaningfully protecting anyone.
Image masking covers OCR-readable text in images. Detecting and blurring faces, handwriting, and signatures is on the roadmap but not available yet.
Detection engine
Captain’s PII detection is built on Microsoft Presidio
(source on GitHub) with Captain-tuned
recognizers: the curated 18-entity allowlist above, a custom high-confidence US
SSN recognizer, custom compliance recognizers validated against the
CFPB TRID model forms and state licensing bond forms, and the spaCy
en_core_web_trf transformer model backing <PERSON> and <LOCATION>
detection. Every tag listed follows Presidio’s entity semantics.
Masked responses
The examples below are real (trimmed) responses from a collection indexed with
mask_pii: true.
Search response
Get document
The figure region’s image_url serves the redacted crop: the pixels behind
each detected value are covered with solid black boxes, so the retrievable
image carries no raw PII either.
Enable it
Add "mask_pii": true to any indexing request. It works on every indexing
endpoint (S3, GCS, Azure, R2, Supabase, Backblaze, Dropbox, Google Drive,
SharePoint, OneDrive, URL, YouTube, and text/file upload), across the bucket,
directory, and single-file variants.
Index a file
Index raw text
Query the collection afterwards and the returned results[].text (and
regions, if requested) contains the entity tags, never the original values.
Masking report
Every job indexed with mask_pii: true keeps a record of what was masked, so
the original values can be handed to a system that needs them without ever
entering the index. Captain writes it while the job runs, as one JSON object per
file (doc.json) or per text chunk (chunk-{n}.json):
value is the original text; replacement is the tag that took its place in
the stored content. bbox uses the same 0 to 1 page-normalized coordinates as
document regions. page and bbox are null for text-lane objects and for
documents so large that the parser returned no block layout.
Two endpoints manage the report, available to any key with the index
permission:
GET /v2/jobs/{job_id}/piilists the objects with signed download URLs (valid for at most 300 seconds) and entity counts. The response itself never contains a value.DELETE /v2/jobs/{job_id}/piidestroys the stored objects. AfterwardsGETanswers410withdeleted_at, forever; the report is never regenerated even though the source files still exist.
The job status response points at the report while it exists:
Reports are kept for 90 days, then expire (GET answers 410 expired). Jobs
indexed before 2026-08-28 have no report (409 predates_feature);
re-index them to obtain one. Every GET and DELETE is audited.
Notes
- Opt-in, per job. Files indexed without
mask_pii: trueare stored as-is; the flag does not retroactively mask previously indexed data. - Source files are untouched. Captain only masks the copies it generates and stores (parsed text, region data, figure crops). Your original file in your own bucket is never modified.
- Fail-safe. If masking cannot run for a file, that file fails to index rather than being stored unmasked.