Redact sensitive information on any webpage — instantly, locally, and privately.
One-Click Redact is a Chrome extension that lets you draw redaction boxes over any region of a page and export a clean PNG to your clipboard or as a download. All processing happens locally in your browser — no uploads, no analytics, no network calls.
- Three redaction modes — Black Box, Blur, and Pixelate with adjustable strength
- Full rectangle editing — draw, move, resize (8 handles), and delete with keyboard
- One-click export — copy to clipboard or download as
redacted.png - Context menu integration — right-click any image → "Redact this image" to pre-select its bounds
- Keyboard shortcut —
Alt+Shift+R(macOS:Option+Shift+R) - Grid overlay — optional alignment guide while drawing
- Recapture on scroll — refresh the viewport snapshot without losing your rectangles
- Offline-first — works without any network connection
- Pro licensing — Ed25519-signed JWTs, device seat management, and seat transfers
This is a monorepo with three packages:
/ # Chrome extension (MV3, TypeScript + esbuild)
├── src/
│ ├── background/sw.ts # Service worker (context menus, commands, capture)
│ ├── content/
│ │ ├── overlay.ts # Canvas overlay: rectangles, toolbar, export
│ │ ├── overlay.css # Overlay styles
│ │ └── algorithms.ts # Black / blur / pixelate (DPR-aware)
│ ├── ui/
│ │ ├── popup.* # Toolbar popup
│ │ └── options.* # Options page + license activation UI
│ └── lib/
│ ├── license.ts # Activation, JWT verification, proof-of-possession
│ ├── crypto.ts # Ed25519, PBKDF2, base64url helpers
│ ├── public-jwk.ts # Build-time license config and public key
│ ├── capture.ts # captureVisibleTab helper
│ ├── clipboard.ts # Copy and download helpers
│ ├── storage.ts # chrome.storage sync schema
│ └── messaging.ts # Background ↔ content message types
├── assets/ # Extension icons (16/32/48/128 px)
├── docs/
│ ├── QA.md # Manual QA checklist
│ └── STORE.md # Chrome Web Store listing copy
├── licensing-api/ # Supabase Edge Functions + DB schema
└── marketing-landing-page/ # Next.js 15 landing page (Tailwind, Framer Motion)
- Node.js ≥ 18
- Chrome or Edge (Chromium-based, MV3 support)
npm install
npm run build # outputs to dist/- Open
chrome://extensions(oredge://extensions) - Enable Developer mode
- Click Load unpacked and select the
dist/folder
npm run dev # rebuilds on file changes| Method | Action |
|---|---|
| Toolbar popup | Click the extension icon → Start Redaction |
| Keyboard shortcut | Alt+Shift+R (macOS: Option+Shift+R) |
| Context menu | Right-click anywhere on the page → Start Redaction |
| Image shortcut | Right-click an image → Redact this image (pre-selects bounds) |
- Click-drag to draw a rectangle; hold
Shiftto constrain to a square - Click a rectangle to select it; drag to reposition
- Drag any of the 8 handles to resize
Delete/Backspaceremoves the selected rectangle
| Mode | Strength Range | Notes |
|---|---|---|
| Black | — | No slider; full opacity fill |
| Blur | 0–40 px radius | Gaussian blur |
| Pixelate | 4–50 px block | Block size in pixels |
- Copy — Places a PNG on the clipboard (requires a user gesture). Falls back to download automatically if the clipboard API is blocked, with a toast notification.
- Download — Saves
redacted.pngdirectly.
If you scroll after starting redaction, the overlay shows a prompt. Press R or click Recapture to take a fresh screenshot while keeping your rectangles.
Open the Options page from the popup or chrome://extensions:
- Default redaction mode
- Default blur radius and pixel block size
- Show grid overlay by default
- Include cursor glyph in exported PNG
One-Click Redact Free includes basic black-box redaction. The Pro tier unlocks:
- Blur and Pixelate modes
- Unlimited rectangles (free plan: 3)
- Download export
- Enhanced context menu workflows
- Purchase a license via the hosted checkout (Lemon Squeezy)
- Open Extension Options → Activate Pro and enter your license key
- The extension validates with the server and stores a short-lived JWT locally
- Pro features work offline until the token expires (24–72 h); the extension auto-refreshes in the background
| Plan | Devices |
|---|---|
| Individual | 2 |
| Team 5 | 5 |
| Team 10+ | Configurable |
When a seat limit is reached, request an email transfer link from the Options page to move the seat to a new device. An emergency grace token (valid 72 h, one-time use per license) is also available.
- Device identity uses random UUIDs — no fingerprinting
- All server requests are signed with an Ed25519 proof-of-possession key
- JWTs are short-lived (24–72 h) and verified against a baked-in public key
- Only the license hash, JWT, and a device UUID are stored locally
| Code | Meaning | Resolution |
|---|---|---|
INVALID_KEY |
License key not found | Check purchase email; contact support |
LIMIT_REACHED |
All seats active | Transfer a seat or deactivate an unused device |
TRANSFER_EXPIRED |
Transfer link expired (30 min TTL) | Request a new link |
SIGNATURE_INVALID |
PoP verification failed | Clear extension storage, re-activate |
NETWORK_ERROR |
Cannot reach licensing server | Pro features continue until token expires; auto-retried on restart |
The backend is a set of Supabase Edge Functions with a Postgres schema.
licensing-api/
├── supabase/
│ ├── functions/
│ │ ├── activate/ # Key validation, seat check, JWT issuance
│ │ ├── refresh/ # PoP verification + JWT refresh
│ │ ├── deactivate/ # Device deactivation
│ │ ├── transfer-init/ # Email magic link generation
│ │ ├── transfer-complete/ # Seat transfer completion
│ │ └── webhooks-lemonsqueezy/ # Payment webhook handler
│ └── migrations/ # DB schema (licenses, instances, events, transfers)
├── env.example # Required environment variables
└── DEPLOYMENT.md # Deployment walkthrough
cd licensing-api
supabase start
supabase functions serve --env-file env.exampleUpdate src/lib/public-jwk.ts with your deployed values:
export const LICENSE_CONFIG = {
apiBase: 'https://your-project.supabase.co/functions/v1',
serverSalt: 'your-pbkdf2-salt',
}
export const LICENSE_JWT_PUBLIC_JWK: JsonWebKey = {
kty: 'OKP',
crv: 'Ed25519',
x: 'your-base64url-public-key',
}A Next.js 15 landing page with pricing, feature showcase, and Lemon Squeezy checkout links.
cd marketing-landing-page
npm install
npm run dev # http://localhost:3000
npm run build # production buildDeployment target: Vercel. See marketing-landing-page/README.md for configuration details (checkout URLs, extension store link, support email).
npm run build
npm run zip # → one-click-redact.zipUpload the zip in the Chrome Web Store Developer Dashboard. Store listing copy and required assets are documented in docs/STORE.md.
| Permission | Reason |
|---|---|
activeTab |
Capture the visible tab as a bitmap |
scripting |
Inject the overlay content script |
storage |
Persist presets and license data |
contextMenus |
Right-click shortcuts |
alarms |
Background license refresh scheduling |
<all_urls> |
Required for tab capture and content script injection on any page |
| Command | Description |
|---|---|
npm run build |
Production build to dist/ |
npm run dev |
Watch mode |
npm run zip |
Package dist/ into one-click-redact.zip |
- Full-page capture (beyond viewport)
- On-device auto-detection (faces, text)
- Team preset import/export
- Firefox and Edge extension ports
MIT © Ethan