The encrypted notepad where a decoy password is a feature, not a bug. Pick a URL, set a password, write. One Flowvault link can hold up to 64 independent notebooks behind 64 different passwords — each unlocks its own workspace, and the server sees one indistinguishable blob either way. Hand over a decoy if you’re ever forced to; your real notebook stays invisible.
1 URL · up to 64 notebooks · 512 KiB total · Argon2id + AES-256-GCM · no account · open source end-to-end
Try it · 1min intro (video) · Blog · Security · Self-host · Donate
Prefer a video walkthrough? A 1-minute intro covering plausible deniability, time-locked notes, trusted handover, and Encrypted Send is on YouTube: youtu.be/wkoAIseRicY.
There's also a public demo vault at useflowvault.com/s/demo with two pre-loaded notebooks behind two different passwords, so you can see the hidden-volume design work before picking a password of your own:
| Password | What opens |
|---|---|
CorrectPassword |
The "real" notebook — walkthrough + example tabs for wallet seeds, API keys, scratchpad |
DecoyPassword |
The decoy — boring on purpose, shaped like what a plausible cover notebook actually looks like |
Same URL, same ciphertext blob on the server, two completely different screens. Lock the vault in between to feel it. The server rejects all writes at the demo URL — local edits only, nothing is saved or shared with the next visitor. Don't put real secrets in it anyway; it's a public walkthrough.
Flowvault is deliberately narrow. It's good at roughly two jobs and honest about everything else.
Reach for Flowvault when you want:
- An encrypted scratchpad you can open from any browser without signing up — borrowed laptops, work machines, phones, library kiosks.
- A place for notes you'd rather not be associated with: recovery phrases, wallet seeds, medical info, contact details for sensitive relationships, things you'd hand over a decoy password for at a border crossing.
- A modern ProtectedText replacement with stronger crypto, hidden volumes, and an open backend you can self-host.
- A one-shot self-destructing link (Encrypted Send) to share a password or API key without making the recipient sign up for Bitwarden or 1Password first.
- To keep the ciphertext off our servers entirely — same app, but the vault lives as a single
.flowvaultfile on your own disk (Bring Your Own Storage).
Pick something else if:
- You want a multi-device notes app with sync — try Standard Notes or Notesnook. Flowvault is browser-only; no native mobile app today.
- You keep long-form journals. Each slot holds ~8 KiB (roughly 1,500 words). Great for dense notes; too tight for daily journaling across a year. Use Obsidian or Joplin with your own E2EE sync instead.
- You need real-time collaborative editing. Use CryptPad. Flowvault handles two editors on the same vault via optimistic concurrency, but it isn't a live-cursor experience.
- You're stashing a crypto wallet seed and want an air-gapped workflow — keep using KeePassXC or a paper backup in a safe. Flowvault is great as a second location for a split seed, not as the only copy.
- Your threat model includes a persistent network observer correlating writes against a specific identity. Content deniability is intact; vault existence at your chosen URL is observable. See the security page for the honest version.
- Hidden volumes — VeraCrypt-style plausible deniability for a browser notepad. One URL, up to 64 notebooks, each behind its own password. Empty slots are indistinguishable from real ciphertext. (deep dive)
- Trusted handover — nominate a beneficiary with a separate password; if you stop checking in for an interval you configure, the vault auto-hands over. No account required for either party. (deep dive)
- Time-locked notes — drand + tlock identity-based encryption, so even the sender can’t decrypt a message before its target date. (deep dive)
- Encrypted Send — one-shot, self-destructing links for sharing a password or recovery phrase. Key in the URL fragment, view cap enforced server-side by a Cloud Function. (vs Bitwarden Send / Privnote)
- Bring Your Own Storage — keep the whole ciphertext on your own disk as a single
.flowvaultfile via the File System Access API. Same hidden-volume format, same Argon2id + AES-GCM, same multi-notebook tabs — the server just never sees the blob. S3-compatible and WebDAV backends are on the roadmap. - Markdown preview & syntax-highlighted code blocks — GitHub-flavored Markdown (tables, task lists, fenced code) renders in-browser with a toggleable Edit / Preview / Split view. HTML is blocked by default, external images are click-to-load, and external links use
no-referrer— a preview that can’t quietly exfiltrate your vault contents. (deep dive) - Cmd+K search, bounded by your session — a command-palette (
Ctrl/Cmd+K) that searches titles and content across every notebook you’ve unlocked in this browser session. No persistent index, no server contact, no IndexedDB cache — the corpus is simply the plaintext already in memory. Slots whose password you haven’t supplied stay invisible to it by construction; locking the vault drops the search surface with the bundle. - Zero-knowledge
.fvaultbackup + plaintext Markdown export — portable across self-hosted instances, still opaque on disk. (format spec) - Open end-to-end. Frontend, Cloud Functions, and Firestore security rules all live in this repo. MIT-licensed. Self-hostable. (vs ProtectedText)
Built with Next.js, Firebase Firestore (opaque ciphertext storage), Firebase Functions, and client-side Argon2id (64 MiB / 3 iter) + AES-256-GCM. No account, no email, no phone number — a URL slug and a password is the whole identity system.
Available for hire — Flowvault is built by Flowdesk, a small studio shipping privacy-first web apps, end-to-end encrypted systems, crypto/web3 products, and native & hybrid mobile apps. Led by a senior engineer with 4 years shipping production cryptography at FlowCrypt (OpenPGP email — iOS + Chrome Extension, 2022–2026). Limited engagements per quarter — reach out at contact@flowdesk.tech.
Flowvault is not "another ProtectedText clone." It's a deliberate upgrade on nearly every dimension that matters for a zero-knowledge notepad.
| Property | Flowvault | ProtectedText |
|---|---|---|
| Password-to-key derivation | Argon2id, 64 MiB memory-hard, 3 iterations, HKDF expansion | Argon2id, 32 MiB, adaptive ~300 ms (per current main.js) |
| Legacy plaintext-password blob | No — every blob requires the full Argon2 chain | Yes — every save also uploads encryptedContentLegacy keyed only by the raw password |
| Encryption | AES-256-GCM (authenticated: ciphertext cannot be tampered with undetected) | AES-256-CBC via CryptoJS (no authentication, malleable) |
| Plausible deniability | Yes — multiple passwords unlock different notebooks on the same URL | No — one password, one blob |
| Fixed-size ciphertext | Yes — every vault is exactly 512 KiB regardless of content | No — blob size leaks how much you've written |
| Tamper detection | Yes — GCM auth tag fails on any modification | No — bitflips go undetected |
| KDF parameters stored in the vault | Yes — upgradable without breaking old vaults | No — clients pick parameters at save time |
| Optimistic concurrency on writes | Yes — two tabs can't silently clobber each other | Hash-based overwrite protection (works, but pessimistic — fails the second writer) |
| Open source | Frontend + Cloud Functions + Firestore rules, MIT-licensed, self-hostable | Client JS inspectable in browser; server code explicitly closed (per their own FAQ) |
| Bring Your Own Storage | Yes — vault can live as a single .flowvault file on your own disk (File System Access API); S3-compatible & WebDAV planned |
No — vault always lives on their server |
- Hidden-volume vaults — the headline feature. One URL, N notebooks, one blob. If someone coerces a password out of you at a border crossing, you hand over the decoy. Cryptographically indistinguishable from a single-notebook vault.
- Multi-notebook tabs — each password now unlocks a workspace, not just one page. Add tabs, rename them, reorder them, delete them. Everything lives inside the same encrypted slot, so the tab list, titles, and contents are all zero-knowledge — the server sees one opaque blob, same as always. Decoy passwords get their own independent tab set in their own slot; adding tabs in your real notebook doesn't touch the decoy and vice versa.
- Time-locked notes — encrypt a message to a future date using the drand public randomness beacon and the tlock scheme (identity-based encryption over BLS12-381). The ciphertext is stored opaquely; the decryption key literally does not exist until drand's network publishes the target round signature. Nobody — not us, not the sender, not a subpoena — can unlock it early. Share links look like
useflowvault.com/t/<id>. Optional password gate: tick "Also require a password to read" and the note is double-wrapped — an inner AES-256-GCM layer keyed by Argon2id(password), and an outer tlock layer keyed to the unlock round. Leaked link alone can't read it; the reader needs both the time to pass and the password (shared out-of-band). - Encrypted Send — one-shot, self-destructing notes for sharing a password, an API key, a recovery phrase, or any snippet you'd rather not sit in chat history. AES-256-GCM encrypted in the browser; the 256-bit key travels in the URL fragment (
#k=...), which browsers never send to servers. Pick an expiry (up to 30 days) and a view count (default 1); the server hard-deletes the ciphertext the moment the last view is consumed, and a scheduled sweep removes anything past its TTL. Reads go through a Cloud Function so the view counter is atomic — clients can't read the document directly (rules deny it). Optional password gate on top, using the same FVPW frame as time-locks, so even a leaked link needs an out-of-band password. Share links look likeuseflowvault.com/send/<id>#k=<key>. - Bring Your Own Storage (BYOS) — local
.flowvaultfiles. Prefer not to leave even ciphertext on a server? Create a vault that lives as a single file on your own disk (D:\notes\journal.flowvault, an encrypted external drive, whatever you like). The editor opens the file via the File System Access API and reads/writes ciphertext in place; our backend never sees the blob or the file name. The on-disk format is a small JSON header (UUID, Argon2id salt, KDF params, volume layout, monotonic CAS counter) followed by the raw fixed-size hidden-volume blob — byte-for-byte the same ciphertext that would live in Firestore for a hosted vault. Multi-notebook tabs, decoy passwords, and.fvaultbackup/plaintext-Markdown export all work the same; trusted handover is disabled for local vaults because it needs a server-held scheduler. Chromium-based browsers only for now (Chrome, Edge, Brave, etc.); S3-compatible (R2, B2, MinIO) and WebDAV backends are on the roadmap — open an issue if one of those would unblock you. - Trusted handover — nominate a beneficiary and a check-in cadence. If you stop saving for the interval + grace you configure, the vault auto-hands over to a pre-chosen beneficiary password. Weekly / monthly / quarterly / yearly presets. The beneficiary key wraps your master key client-side; the server just schedules the release. Hourly Cloud Function sweeps expired configs; the Firestore rules forbid clients from faking a release or extending one they can't actually open.
- Markdown preview with security-first defaults. Notes render as GitHub-flavored Markdown in-browser: tables, task lists, strikethrough, autolinks, fenced code blocks with Prism syntax highlighting for every common language. A segmented toggle in the toolbar flips between Edit / Preview / Split; the mode preference persists per device in
localStorage(not in the encrypted blob, so you don't burn bytes of your 512 KiB slot on UI state). The preview is deliberately unusual: raw HTML is blocked (<script>,<iframe>, arbitrary tags render as literal text), external images are click-to-load with a placeholder showing the URL (socan't silently phone home the moment your vault opens), and external links open withtarget="_blank" rel="noopener noreferrer" referrerPolicy="no-referrer"so the destination site never learns where the referrer was. Code highlighting runs locally viaprism-react-renderer— no network request, no WASM, no remote theme fetch. The renderer bundle itself is lazy-loaded vianext/dynamic, so users who live in Edit mode never download it. - In-memory Cmd+K search across unlocked notebooks.
Ctrl/Cmd+Kopens a command palette that searches titles and content across every tab in the slot you've unlocked this session. Case-insensitive substring matching, grouped by notebook, match highlighting, line numbers;↑ ↓to navigate,Enterto jump straight to the match with the range selected in the textarea (so the browser scrolls it into view for you),Escto close. The corpus is just the plaintext already sitting in memory — there is no persistent index, no IndexedDB store, no server round-trip, and no way for the palette to see a slot whose password you haven't supplied. Locking the vault drops the search surface with the bundle, so search can't outlive your session. - Optimistic concurrency — edit the same vault in two browser tabs without losing work.
- Modern editor — keyboard-first (Ctrl/Cmd+S), auto-save with visible status, dark mode, clean typography.
- Slot capacity meter — you always know how much space you have in your notebook.
- Encrypted backup & restore — download the full vault as a
.fvaultfile (opaque ciphertext + KDF params + volume layout). The file is exactly as zero-knowledge as the live vault: it still needs your password to read, and every decoy slot stays indistinguishable from random bytes. Restore to any fresh slug on any Flowvault instance (including a self-hosted one) from/restore. Plaintext Markdown export (current slot only) is also available, behind a confirmation, for migrating out to Obsidian et al.
- Open source, end to end. Not just the frontend — the Cloud Functions (the trusted-handover sweep) and the Firestore security rules (the actual boundary that stops us from reading or mutating your data) are in the same repository, deployed unmodified. ProtectedText publishes its client JS for inspection but explicitly does not open its server code (per their own FAQ). Flowvault is reviewable, licensed, forkable, and self-hostable end-to-end.
- No ads. No tracking. No analytics. Your browser talks to Firestore and nothing else.
- No account, no email, no phone number. A URL slug and a password are all you ever provide.
- Self-hostable, whole-stack. Bring your own Firebase project; the frontend, Functions, and rules all deploy from a single
npmworkspace. - Published threat model. We tell you exactly what we can and cannot protect against — including the cases where plausible deniability is weaker (e.g., a persistent network observer correlating writes).
- Zero-knowledge firewall enforced by Firestore rules. The rules are short, public, and auditable. We physically can't read your notes even if we wanted to.
- Planned build transparency. Release commits will be tagged and their bundle hashes published, so you can verify the JS your browser runs matches a reviewable commit.
- Clean, modern UI with dark mode by default
- Ctrl/Cmd+S to save, save status indicator, dirty-state warning before close
- Ctrl/Cmd+K to open the in-memory search palette across every unlocked notebook
- Slug validation (no surprises with weird characters)
- Fast password gate that tells you clearly whether a vault exists or is new
- Byte/capacity counter so you see when you're near slot limits
# 1. Install deps
npm install
(cd functions && npm install)
# 2. Create a Firebase project and paste the config
cp .env.local.example .env.local
# Fill in NEXT_PUBLIC_FIREBASE_* values from the Firebase console.
# 3. Deploy the security rules (requires firebase-tools)
npx firebase deploy --only firestore:rules
# 4. Run locally
npm run devTo exercise the Firebase emulators instead of deploying:
npx firebase emulators:start --only firestore,functionssites/{siteId}
ciphertext: bytes # fixed-size hidden-volume blob (default 64 × 8 KiB = 512 KiB)
kdfSalt: bytes # per-site Argon2id salt
kdfParams: map # algorithm + cost parameters, upgradable
volume: map # { slotCount, slotSize, frameVersion }
version: number # CAS counter
createdAt: Timestamp
updatedAt: Timestamp
See firestore.rules for the complete zero-knowledge rule set.
See the /security page rendered in the app, which documents what the server
sees, what the hidden-volume format protects against, and — honestly — the
cases where it does not (e.g., persistent network observation of writes).
Shipped:
- Core zero-knowledge notepad
- Hidden-volume format for plausible deniability (64 × 8 KiB slots)
- Argon2id + AES-GCM
- Optimistic concurrency on writes
- Decoy-password management UI ("Add password" in the editor)
- Trusted handover: configure / heartbeat-on-save / scheduled release / beneficiary unlock flow
- drand-backed time-locked notes (
/timelock/newcompose →/t/{id}view) via tlock-js - Encrypted Send: one-shot, self-destructing notes (
/send/new→/send/{id}#k=<key>) - Multi-notebook tabs per slot — one password, many tabs, all inside the same encrypted blob
- Encrypted backup/restore (
.fvault) + plaintext Markdown export for migration - Bring Your Own Storage — local
.flowvaultvault files via the File System Access API (first non-Firestore adapter) - Markdown preview + syntax-highlighted code blocks — GitHub-flavored Markdown, Edit / Preview / Split toggle, HTML blocked, external images click-to-load, external links
no-referrer - Cmd+K in-memory search across unlocked notebooks — command palette, keyboard-first navigation, jump-to-match in the textarea, no persistent index, no server contact
In progress / planned:
- Bring Your Own Storage: more backends — S3-compatible (AWS S3, Cloudflare R2, Backblaze B2, Wasabi, MinIO), WebDAV (Nextcloud, ownCloud), and experimental IPFS / Storj. All sharing the same
VaultStorageAdapterinterface the local-file adapter already uses. Prioritised by user demand — open a GitHub issue if one of these would unblock you. - PWA / offline mode
- Signed build hashes + transparency log
The split is: Vercel deploys the Next.js frontend automatically via its GitHub integration. GitHub Actions deploys the Firebase backend — Cloud Functions, Firestore security rules, and Firestore indexes — which Vercel does not touch.
Workflows in .github/workflows/:
ci.yml— runs on every PR and push: lint, TypeScript type-check, and production build for both the Next.js app and the Cloud Functions workspace. Catches regressions before they reach either Vercel or Firebase.deploy-firebase.yml— runs on pushes tomasterthat touchfunctions/**,firestore.rules,firestore.indexes.json, orfirebase.json(plus a manualworkflow_dispatch). Builds the Functions, authenticates with a service account, and runsfirebase deploy --only functions,firestore:rules,firestore:indexes.
Required repository secrets (Settings → Secrets and variables → Actions):
| Secret | Purpose |
|---|---|
FIREBASE_SERVICE_ACCOUNT |
Full JSON of a service account with roles: Cloud Functions Admin, Firebase Rules Admin, Service Account User, Cloud Datastore Index Admin, and (first deploy only) Artifact Registry Writer. |
NEXT_PUBLIC_FIREBASE_API_KEY |
Public client config — also configured in Vercel, duplicated here so CI builds succeed. |
NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN |
same |
NEXT_PUBLIC_FIREBASE_PROJECT_ID |
same |
NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET |
same |
NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID |
same |
NEXT_PUBLIC_FIREBASE_APP_ID |
same |
Required repository variables (non-secret, Settings → Secrets and variables → Actions → Variables tab):
| Variable | Example |
|---|---|
FIREBASE_PROJECT_ID |
flowvault-prod |
NEXT_PUBLIC_APP_URL |
https://useflowvault.com |
NEXT_PUBLIC_GITHUB_URL |
https://github.com/Flowdesktech/flowvault |
Vercel's GitHub integration handles the frontend automatically. Connecting the repository to a Vercel project is enough:
- Push to
master→ production deploy at the canonical domain. - Push to any other branch, or open a PR → preview deploy at a
*.vercel.appURL. NEXT_PUBLIC_*env vars live in Vercel's project settings and are injected at build time.
Nothing in this repo needs to push to Vercel, so no VERCEL_TOKEN /
VERCEL_ORG_ID / VERCEL_PROJECT_ID secrets are required in GitHub.
The Firebase workflow above is the only deploy this repo owns.
(If you ever want to drive Vercel from Actions instead — for
example to keep all build logs in one place — the commands are
vercel pull --environment=production, vercel build --prod, and
vercel deploy --prebuilt --prod. Plain vercel deploy without
--prod produces a preview, not a production release.)
Flowvault is built on the honor system. We don't show ads, sell data, or ask for your email — by design, not oversight. Those are the usual ways an app pays for itself, and all of them conflict with being zero-knowledge.
Donations go through the NOWPayments donation
widget, embedded on /donate. We picked it because it's one of the
very few processors where a donor can contribute without creating an
account or providing an email — receipts are optional, only generated
if the donor wants one. The widget also generates a fresh deposit
address per donation, so two donors can't cross-reference each other
on-chain.
- ~100+ coins supported (BTC, ETH, LTC, XMR, USDT on TRC-20 / ERC-20, SOL, and many more)
- Monero (XMR) available for donors who want amount + identity cryptographically hidden, not merely pseudonymous
- No donor sign-up, no donor email required
- Tor / VPN friendly on the donor side
- Short / vanity link:
https://nowpayments.io/donation/flowdesktech
Every traditional payment rail (cards, PayPal, Stripe) requires identifying info to receive money. A processor with an anonymous donation flow is the closest match to the rest of Flowvault's model.
Set these in .env.local (they aren't secrets — both are embedded in
the public bundle and visible in the iframe src):
NEXT_PUBLIC_NOWPAYMENTS_API_KEY=d1809dbe-265d-44fc-af65-16cce1b7186b
NEXT_PUBLIC_NOWPAYMENTS_DONATION_URL=https://nowpayments.io/donation/flowdesktech
If you fork Flowvault and want donations to go to your own NOWPayments account, replace both values with your own api key and vanity slug.
If crypto is a non-starter for you, no pressure — these help just as much:
- Use Flowvault and tell someone who'd benefit
- Star the GitHub repository
- File a thoughtful bug report or security issue
- Submit a PR
MIT. See LICENSE for the full text.