End-to-end encrypted notepad โ PWA app (self-hosted)
A quick note saved in your system notepad stays on that one computer โ it is nowhere to be found on your phone or a second device. The cloud (Notion, Keep, OneNote) means an account with a third-party provider and data outside your control. We built an app that flips this model: your data sits on your own hosting, and the encryption key never leaves your devices. The server โ and anyone who gains access to its files โ sees nothing but ciphertext. The app installs from the browser as a standalone window on Windows, macOS and iPhone, works offline and saves changes on its own. One tool, every platform, zero subscription.
How it works โ step by step
Passphrase on the device
Once per device you enter a passphrase; a key is derived from it (PBKDF2) and stays strictly local.
You write a note
The title is the first line of the content. You never click "Save" โ the app does it for you.
Encryption in the browser
Content and title are encrypted with AES-256-GCM before anything leaves the device.
Saved to your hosting
Only ciphertext reaches the server, as a plain file. The backend does not know the key and can decrypt nothing.
Cross-device sync
A note from your computer is waiting on your phone and second computer โ with no action on your part.
Conflict detection
Editing in parallel on two devices ends in a clear choice of version, not a silent overwrite.
Offline mode
With no connection you can still view recent notes; pending saves send themselves once you are back online.
What you get
Your data, yours alone
Even a full leak of the server files reveals no content โ without the key it is unreadable ciphertext.
One app, every platform
A PWA from a single codebase on Windows, macOS and iPhone โ no app stores and no separate versions.
Autosave and zero friction
Saved a second after you pause typing and when you close the window โ you never lose a thought.
Lightweight hosting
Runs on the cheapest PHP + HTTPS hosting: no database, no dependencies, no build process.
Fully auditable
Around 700 lines of custom code and zero external libraries โ no supply-chain vulnerabilities.
Works offline
View and write without a connection; sync catches up automatically once you reconnect.
Details
Privacy that is not a marketing slogan
Encryption happens in the browser (WebCrypto, AES-256-GCM) before data leaves the device. The key, derived from your passphrase (PBKDF2, 600,000 iterations, random salt), stays in the device memory. Even titles are encrypted separately โ the server knows neither the content nor the names of your notes. We deliberately document the boundary of the model: it protects against a data leak from the server, not against tampering with the app code itself โ an unavoidable limit of every web-based E2E application.
Sync without silent overwrites
Every save carries a marker of the version the client last saw. When the server holds newer content, you get a clear choice โ "keep mine / load theirs" โ and late saves after a network failure write a copy alongside rather than overwriting the newer version. On top of that, saves are queued and a note is only switched after it has loaded successfully โ so one note's content never lands under another note's identifier.
Deployment without DevOps
The whole thing is a few files copied to any PHP + HTTPS hosting. No database, migrations or build process. Notes are ordinary files in a directory cut off from the web, with unguessable names and path-traversal protection; login is based on an HMAC token and a year-long cookie (resilient to iOS clearing its storage).
For the curious โ how it works under the hood. The frontend is pure JavaScript (ES2020) with WebCrypto, a service worker and a PWA manifest, without a single external library. The backend is a minimal JSON-RPC API in PHP on flat files โ deliberately "dumb", with no knowledge of the key.
Cryptography
AES-256-GCM with a random IV per operation; the key comes from PBKDF2-SHA256 (600,000 iterations, 128-bit salt). Context is bound via AAD (note id + field), so the server cannot swap ciphertexts between notes or pass off a title as the content. The passphrase is verified by decrypting a known verifier โ a wrong passphrase gives a clear error, never a save under the wrong key.
A "dumb" and resilient backend
A JSON-RPC API over POST (login/list/get/save/delete/meta). Notes are files written with LOCK_EX in a directory cut off by .htaccess; identifier validation blocks path traversal; the key config is created atomically (fopen in x mode) โ a race between two devices ends in a clean 409, not two keys. The password secret lives in a file outside the public directory, and comparisons use hash_equals.
PWA and offline
Service worker: network first (fresh updates), cache as fallback; error responses never poison the cache. Installing from the browser gives its own window and an icon in the taskbar, the Dock and on the iPhone home screen. Inputs are 16 px to avoid iOS auto-zoom, with a two-pane layout on desktop and a single pane on phones with safe-area support.