I'm John Doe app icon

I'm John Doe

// Mask your identity before AI sees it.

on-device50+ detectorszero networkmacOS
App Store — coming soon
cat problem.txt

# The Leak

/**

You paste things into an AI chatbot — emails, contracts, messages. Your name, phone number, account details, API keys. It goes to the server before you think about it.

I'm John Doe catches that data and swaps it with typed placeholders like [name], [email], [address], [number]. Not stars. Not random strings. Typed labels — so the AI still understands the structure of your text and gives you a useful answer back.

Nothing leaves your Mac. No server, no account, no network — it even works offline. The app runs entirely on your device.

*/

ImJohnDoe
I'm John Doe app — split pane showing original text and masked output
./setup --first-run

# First Run

Pick a few seed words. I'm John Doe learns their shape — capitals, lowercase, plurals — so "Acme", "ACME", and "acmes" all get caught in the same pass.

Onboarding step 1: enter your name
// step 1always-mask seed
Onboarding step 2: choose detection categories
// step 2category toggles
Onboarding step 3: setup complete
// step 3ready to paste
diff before.txt after.txt

# Before and After

Names become [name], emails become [email]. The shape of the message stays intact, so ChatGPT or Claude still understands what you're asking — just without knowing who.

Empty app state — blank prompt box on the left, Detect placeholder on the right, 0 / 20,000 counter
// empty canvas
Before masking — original text with personal data
// raw paste
After masking — PII replaced with [name], [email], [number]
// masked output
grep -n "PII" ./input

# The Sweep

Paste a whole email thread, hit mask, watch every match light up at once. The highlights stay up for a beat so you can see what got caught before you decide what to do with it.

Source text with individual PII highlighted
// detected, highlighted
Bulk masking with all PII highlighted in source
// drag to select range
sed 's/.*/[masked]/' ./selection

# Bulk Mask

Drag to select a range and pick a placeholder — [masked], [name], [email], or something you type yourself. The whole selection collapses into that one label in a single pass.

Mask Selection popup with placeholder buttons and a custom placeholder input, [masked] selected by default
// pick a placeholder
Selected paragraph replaced with a single [masked] label after confirming the choice
// whole range, one label
cat ~/.config/categories

# Detection Rules

Ten categories, each labeled with what it actually catches — Person (names, orgs), Contact (email, phone), Address (city, postal code). Toggle off what you don't need without guessing what's behind the switch.

Mask Settings — Categories panel with 10 toggleable detection categories
// 10 categories, toggle per context
echo "my-email@gmail.com" >> .always-mask

# Always Mask

Add the things detection misses — a codename, an internal project, a client's nickname. Works alongside Never Mask to cover the gaps automatic rules leave behind.

Always Masked settings — adding a new email
// add with custom placeholder
Always Masked settings — list with name and email entries
// saved entries, per-row delete
echo "John Doe" >> .never-mask

# Never Mask

Keep the words that should pass through — your own product name, public figures, technical terms. If a word ends up on both lists, the app catches it before it becomes a problem.

Never Mask settings panel with John Doe added
// keep-as-is list
diff .always-mask .never-mask

# The Conflict

Add a word to Always that's already on Never, and the app stops to ask. Whichever list had it first keeps it, unless you move it — so the two lists never quietly contradict each other.

Duplication check dialog — conflict between Always Masked and Never Mask
// conflict prompt on save
a11y --text-size

# Read Comfort

Bump the text size up or down with limits on both ends, so the layout doesn't break. Whatever you pick sticks on the next launch — long drafts, low-vision users, same setting every session.

Text size toggle set to maximum — both original and masked views render in large type, "2 items masked" header visible with A and AA buttons.
// maxed, still readable
Text size toggle set to minimum — same masked and original content renders in compact type, leaving generous whitespace around the canvas.
// minimum, room to breathe
wc -l ./features
15+entity types
50+regex detectors
20Kchar input cap
0network calls
100%on-device

"Your data never leaves your device."