Note It Down 📝
A clean Chrome extension that gives you a floating, always-on-top notepad using Document Picture-in-Picture and private zero-knowledge sync.
The Backstory
"I know, the world didn't need another notepad. We have 4 billion of them, half VC-funded, all asking 'Sign up to continue' before you've typed your grocery list.
I just wanted notes to sync between my work laptop and personal laptop without using my personal account at work, my work account at home, or making Yet Another Account on Yet Another SaaS tool that'll eventually pivot to 'AI-powered productivity' and start emailing me.
So I built Note It Down. It's a notepad. Not reinventing anything, it just syncs without logging in, which felt rare enough to post about."
Specs & Info
- Environment:Chrome Sandbox (Sidebar)
- PiP Library:@pip-it-up/react
- Security Layer:AES-GCM-256 E2EE
- Relay DB:Cloudflare Workers + KV
Core Features
Floating PiP Window
Pops your active note into a borderless, floating Picture-in-Picture window that stays on top of other desktop windows, so you can take notes while watching videos or reading docs without alt-tabbing.
Shadow DOM Isolation
Mounts the extension UI inside an isolated Shadow DOM on host pages. This stops host page CSS (like on Reddit or GitHub) from breaking the sidebar layout.
Zero-Knowledge Storage
Encrypts notes right in your browser using AES-GCM-256 before transmitting anything. The worker server only receives encrypted blobs, so your notes stay private.
Cloudflare Edge KV Relay
Notes sync directly through a self-hosted Cloudflare Worker. Cloudflare's free tier provides 1GB storage and 100,000 requests/day, making cross-device sync fast and free to host.
🧩 Powered by pip-it-up
I built this extension partly to show how easily @pip-it-up/react lets you float any React component in a native desktop window.
Here is the actual wrapper code from EditorOverlay.tsx:
import { PipWrapper } from '@pip-it-up/react'
// ...inside the component...
<PipWrapper
width={380}
height={360}
open={activeNoteId !== null}
onOpenChange={(openState) => {
if (!openState) {
setActiveNoteId(null)
}
}}
placeholder={<div style={{ display: 'none' }} />}
>
{activeNoteId && (
<NoteEditor
key={activeNoteId}
noteId={activeNoteId}
onClose={() => setActiveNoteId(null)}
theme={theme}
/>
)}
</PipWrapper>Zero-Knowledge Cryptography
Server-blind database read/write validation using WebCrypto
To achieve complete privacy, the extension employs a zero-knowledge structure using WebCrypto APIs:
- Deterministic Key Derivation (HKDF): From a single sync token, the extension derives three independent cryptographic values via HKDF-SHA-256 (using a fixed 32-byte zero salt):
- `address`: The database lookup key (used as the URL segment on the worker).
- `encryptionKey`: An AES-GCM 256-bit key used locally to encrypt/decrypt note database payloads.
- `verifyKey`: Stored on the worker during the first sync to authenticate future writes.
- Write Token Derivation (HMAC): A `writeToken` is derived by computing an HMAC-SHA256 signature of `"nid-write-auth"` using the derived `verifyKey` bytes as the key.
- Zero-Knowledge Boundary: The client only transmits the `address`, the `writeToken`, and the encrypted `blob`. The `encryptionKey` and the raw `token` **never leave your device**.
- Worker Verification: The worker verifies subsequent writes by re-computing the HMAC signature using the stored `verifyKey` and comparing it to the incoming `writeToken`. The worker is content-blind (cannot read notes) and token-blind (does not know the raw token).
Technical Challenges & Solutions
| Challenge | Browser Policy | Problem | Solution |
|---|---|---|---|
| 1. Sandboxed Extension Frame | Extensions block documentPictureInPicture in popup/sidebar frames. | requestWindow() throws security exceptions in default extension overlays. | Migrated UI mount targets to a webpage-injected sidebar drawer element. |
| 2. Gesture Token Expiration | Document PiP requires active user click tokens. | Async messages (popup to script) expire user click context. | Mounted UI directly in host page DOM so clicks act as native events. |
| 3. Content Security Policy (CSP) | Strict host pages block lazy scripts. | Lazy load chunk scripts trigger CSP exceptions on GitHub/Google. | Configured Vite packaging configurations to bundle build into a single IIFE script. |
| 4. CSS Isolation | Injected scripts are isolated from style namespaces. | Copying document.styleSheets into PiP results in blank components. | Injected the absolute CSS link of the extension to PiP head on instantiation. |
| 5. Host Style Bleeding | Host global resets leak onto injected elements. | Sites like Reddit overwrite margins/font alignments in our sidebar launcher. | Encapsulated React app mounting container root inside an isolated Shadow DOM. |