Decode
by Marvellous Nwachukwu
A weekend project that turned into something I actually use.
My friend and I had been sending each other base64-encoded messages through Claude on our phones. He'd paste the gibberish, ask Claude to decode it, read what I'd sent. We thought we were being clever. We weren't. Base64 is not encryption. Anyone could have decoded the same string in five seconds.
So I built Decode. AES-256-GCM encryption, PBKDF2-SHA256 key derivation, 200,000 iterations. Type a message, type a shared key you've agreed on, get a string that nobody can read without that key.
Constraints
- No backend. No server processing, no logs, no third-party API anywhere.
- Vanilla JavaScript. No bundler, no framework, no build step.
- Static hosting. Drop it on Vercel and walk away.
The key never leaves your device. Anthropic, Vercel, Cloudflare, none of them touch your messages.
How it works
The flow:1. Pick a key. PBKDF2 turns it into a 256-bit AES key using a fresh 16-byte salt.
2. AES-GCM encrypts the message with a fresh 12-byte IV.
3. Concatenate salt + IV + ciphertext, base64 it, send.
4. Receiver pastes it back, types the same key, the same math runs in
reverse.
Wrong key, decryption just fails. No peek, no partial match.
The disguise
The feature I'm most proud of: the app can pretend to be something else.Open Settings, rename it to "Notes", upload a calculator icon, set the
colors. The change reaches your phone's home screen. When you tap "Add to Home Screen", iOS and Android capture whatever you've configured, so your home grid shows a Notes app. Tapping it still opens Decode.
How: localStorage holds the personalisation, and on every page load the app rewrites document.title, apple-mobile-web-app-title, the apple-touch-icon link, the theme-color meta, and the manifest itself (as a Blob URL) before the install prompt can read them.
Chrome extension
On desktop, opening a tab to encrypt one message is annoying. So there's a Chrome extension. Highlight any text, right-click, pick "Encrypt with Decode" or "Decrypt with Decode". The MV3 service worker handles the crypto, a Shadow DOM modal collects the key, and the result either replaces your selection in place or lands on your clipboard.The crypto module is one file shared between the web app and the extension via a symlink. Identical behavior, no risk of format drift.
The shortcut I didn't take
Mid-build I almost added a Claude skill so we could decrypt from inside Claude on our phones. It would have been more convenient than switching apps.Then I realized: typing the key into Claude means typing it into
Anthropic's logs. The whole zero-trust premise gone for the sake of saving one tab switch.
Instead, the app supports URL-hash prefill and the Web Share Target API. On Android, sharing text from any app routes to Decode with the message pre-filled. On iOS or desktop, a "Share link" button generates a one-click URL that opens Decode with the ciphertext loaded. The key still only ever lives on your device.
Stack
HTML, CSS, vanilla JS, Web Crypto API. Service worker for offline. Dynamic Blob URL manifest for the disguise. Chrome MV3 for the extension. No dependencies anywhere.
Source: https://github.com/MarvelNwachukwu/decode
Live: https://decode-orcin.vercel.app
Chrome extension: https://github.com/MarvelNwachukwu/decode/releases/latest
#WebBlog