npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

aegis-redact

v0.4.5

Published

Zero-knowledge, client-side PII redaction for PDFs. Documents never leave the browser.

Readme

aegis-redact

Zero-knowledge PII redaction for PDFs. The document never leaves the browser.

No upload, no server call, no telemetry. Give it a PDF, get back an image-only PDF with the sensitive fields destroyed at the pixel level — not covered, destroyed. There is no text layer in the output, so there is nothing to recover.

npm install aegis-redact

Quick start

import { createAegis } from 'aegis-redact/browser';

const aegis = await createAegis();
const result = await aegis.redact(file);      // a File from <input type="file">

result.redactedPDF;      // ArrayBuffer — image-only
result.detected;         // what was found, where, by which path, at what confidence
result.redactionCount;   // rectangles that ACTUALLY covered pixels
result.warnings;         // non-fatal observations worth surfacing to your user

The package ships ESM and CJS only — there is no global/IIFE build, so it needs a bundler or a native ESM import. A <script src=...> tag pointing at a .global.js will 404.

What it detects

| Document | Fields | | --- | --- | | W-2 | SSN / ITIN | | Form 1040 | SSN / ITIN (taxpayer, spouse, dependents), routing, account, IP PIN | | Bank statement | Account number, member number, routing | | Payslip | SSN / ITIN, routing, account |

EIN, phone and date of birth are additionally available as opt-in field types (see Options).

Document type is auto-detected. Override it when you already know:

await aegis.redact(file, { documentType: 'BANK_STATEMENT' });

Options

await aegis.redact(file, {
  documentType: 'W2',              // skip auto-detection
  signal: controller.signal,       // cancel a long job
  onProgress: (p) => setBar(p),    // 0 → 1
  redactEin: false,                // opt-in, off by default
  redactPhone: false,              // opt-in, off by default
  redactDob: false,                // opt-in, off by default
});

EIN, phone and date of birth are off by default on purpose. The employer EIN is public information and identifies the employer, not the employee; phone and DOB are what income verification needs for identity matching: income verification needs them for identity matching, and redacting them reduces the document's utility without a corresponding privacy gain. Turn them on for workflows that share documents onward.

Errors

redact() throws AegisError rather than returning a partial result.

| Code | Meaning | | --- | --- | | FILE_TOO_LARGE | Over 10 MB or over 50 pages | | INVALID_PDF | Not a PDF | | CORRUPTED_PDF | pdf.js could not parse it | | PASSWORD_PROTECTED | Encrypted | | UNKNOWN_DOCUMENT_TYPE | Could not classify — pass documentType | | UNREADABLE_DOCUMENT | Expected targets, found none — see below | | ALREADY_IMAGE_ONLY | No text layer; possibly already redacted, or a scan | | ABORTED | Cancelled via signal |

UNREADABLE_DOCUMENT is the important one. When a document type that always contains a target yields zero detections, the text layer probably failed silently. The SDK refuses to hand back a document it may not have redacted. Surface it to your user and ask them to re-upload; do not treat it as a soft failure.

try {
  const result = await aegis.redact(file);
} catch (e) {
  if (e instanceof AegisError && e.code === 'UNREADABLE_DOCUMENT') {
    showRetryPrompt();
  }
}

Serving the static assets

By default the pdf.js worker and font data resolve from your node_modules and your bundler handles them. Under a strict CSP, serve them yourself:

const aegis = await createAegis({ assetBaseUrl: '/aegis-assets' });

Copy node_modules/pdfjs-dist/build/pdf.worker.min.mjs and node_modules/pdfjs-dist/standard_fonts/ into that directory.

The font data is not optional. Without it pdf.js cannot build glyph outlines for the base-14 fonts and text extraction degrades silently — which means missed detections.

Content Security Policy

script-src  'self' 'wasm-unsafe-eval';
worker-src  'self' blob:;
img-src     'self' data: blob:;
connect-src 'self';

wasm-unsafe-eval is required by pdf.js.

connect-src 'self' is sufficient only for the rules-only configuration. That is the default and it genuinely makes no outbound request — which is worth proving to your own security reviewers. But the two optional layers do fetch, and their assets are large:

| Configuration | What it fetches | CSP | | --- | --- | --- | | Rules only (default) | nothing | connect-src 'self' | | + OCR | Tesseract worker, WASM core and eng.traineddata | connect-src 'self' only if you self-host; otherwise Tesseract's CDN | | + NER | ~14.7MB ONNX model from huggingface.co | connect-src 'self' only if you self-host; otherwise https://huggingface.co https://cdn-lfs.huggingface.co |

Self-hosting both gets you back to connect-src 'self', and that is the recommended enterprise posture — pass workerPath/corePath/langPath to the OCR engine and serve the model from your own origin. Neither fetch ever carries document content; they are downloads of static assets, in the opposite direction. But an allowlist that does not mention them will fail closed in staging, so plan for it before your AppSec review rather than during it.

Optional layers

Both are off by default and neither is a dependency of this package — install them only if you want them.

# OCR fallback for scanned or photographed documents
npm install tesseract.js
import Tesseract from 'tesseract.js';
const aegis = await createAegis({ ocr: { tesseract: Tesseract } });
# NER recall layer (adds a ~14.7 MB model download, cached in IndexedDB)
npm install @nationaldesignstudio/rampart @huggingface/transformers
const aegis = await createAegis({ ner: true });

Without an OCR engine, a document with no text layer raises ALREADY_IMAGE_ONLY rather than being returned unredacted.

Browser support

Chrome/Edge 94+, Firefox 105+, Safari 16.4+. The floor is OffscreenCanvas.

Bundle size

~20 KB gzipped for the ESM entry and its chunk; ~59 KB for the full published dist (ESM + CJS + type declarations). pdf.js is a peer cost on top (~1.1 MB, lazily imported and separately cacheable), and its worker is a separate file that is not in your entry chunk.

The trade-off, stated plainly

Output is image-only. It is not searchable and not readable by screen readers. That is inherent to irreversible redaction, not an oversight: a black rectangle drawn over live text leaves the text intact in the content stream, which both the NSA and GCHQ have published warnings about. If redaction is reversible, it is not redaction.

If your workflow needs searchable output, this is the wrong tool.

Scope

Aegis is a best-effort upstream filter, not your last line of defence. A miss restores the status quo — the value reaches your server as it did before Aegis existed — it does not create new exposure. Keep your existing access controls, encryption and retention policies.

Licence

Proprietary. Free 30-day evaluation; production use requires a commercial licence. See LICENSE and THIRD_PARTY_NOTICES.md.