Releases: Flowdesktech/flowvault
Flowvault v1.4.0 — Markdown preview without the leaks Latest
v1.4.0 — Cmd+K search, bounded by your session
Flowvault now has a command-palette search (Ctrl/Cmd + K) across every
notebook you've unlocked in the current browser session. It's fast,
keyboard-first, and — by construction — incapable of seeing anything
you haven't already decrypted.
What's new
- Cmd+K search palette. A single keybind opens an overlay that
searches titles and content across every tab in the currently
unlocked slot. Case-insensitive substring, match highlighting, line
numbers, results grouped by notebook. - Keyboard-first navigation.
↑ ↓move,Enterjumps to the
match,Home/Endgo to the first/last hit,Esccloses.
Mouse-hover syncs with the keyboard cursor so the two drivers never
fight. - Jump-to-match. Selecting a hit switches to the right notebook
and selects the match range directly in the textarea, so the
browser scrolls it into view for you. If you're in pure Preview
mode, the editor flips to Edit so there's a surface to land on;
Split stays Split. - Discoverability button. A new
Search ⌘Kbutton sits in the
editor toolbar for anyone who doesn't already know the shortcut.
Why the constraints matter
A search feature is the kind of thing that can quietly undo a
zero-knowledge design: most apps index everything, store the index
somewhere the server can see, and call it "just metadata." Flowvault's
search does none of that.
- In-memory only. There is no persistent index, no IndexedDB
store, nolocalStoragecache. The corpus is the plaintext that's
already sitting in memory because you unlocked the slot. Locking
the vault drops the bundle and drops the search surface with it. - Zero server contact. The feature never issues a request. It is
a pure function from your unlocked notebooks + your query to a list
of hits, executed in the tab. - Deniability-preserving. Slots whose password you haven't
supplied in this session are not in memory, so the search is
physically incapable of traversing them. A match that exists under
a different password is invisible to the current session — exactly
like the rest of Flowvault. - No index to subpoena. There's nothing for us to hand over and
nothing for a compromised host to exfiltrate later. The same
invariant that makes backups portable makes search ephemeral.
Small notes
- Corpus ceiling per session is tiny by design (32 notebooks × a few
KiB per slot), so the scan runs on every keystroke without
debouncing. Sub-millisecond on real devices. - Per-notebook hit cap (20) and global cap (100) keep the palette
bounded when you search for common words. - The palette is suppressed while another modal owns the screen, so
it can't stack on top of rename, confirm-delete, add-password, or
handover dialogs.
Upgrade
Web: already live on useflowvault.com —
reload and press Ctrl/Cmd + K.
Self-hosted: pull v1.4.0 and rebuild (npm run build). No
migrations; the feature is purely client-side.
Flowvault v1.3.0 — Markdown preview without the leaks
Markdown preview & syntax-highlighted code blocks
Flowvault v1.3 renders your notes as GitHub-flavored Markdown with
syntax-highlighted code blocks, via a new Edit / Preview / Split
toggle in the editor toolbar. The preview is locked down by default:
raw HTML is blocked, external images are click-to-load, and external
links strip referrers. The textarea is still the source of truth;
notes are still stored as plain Markdown text inside the same
fixed-size hidden-volume slots.
Highlights
- GitHub-flavored Markdown — headings, lists, task lists
(- [x] done), tables, blockquotes, strikethrough, autolinks. - Fenced code blocks with Prism highlighting for TypeScript,
Rust, Go, Python, Bash, JSON, YAML, SQL, and every other common
language — highlighting runs entirely in your browser, no remote
theme or grammar fetch. - Edit / Preview / Split segmented toggle. Split appears on
viewports >~900 px; narrower viewports fall back to Preview while
still remembering your preference. Mode preference is persisted in
localStorage, not in the encrypted blob — your 512 KiB slot stays
for content. - Lazy-loaded renderer bundle (~90 KB gz) via
next/dynamic.
Users who live in Edit mode pay zero bundle cost for the feature.
Security-first defaults
- Raw HTML is blocked.
<script>,<iframe>,<img onerror=…>
and friends render as literal text, not elements. There is no
opt-in. - External images are click-to-load. Every
URL
shows up as a placeholder with the exact URL and an explicit
"Load image" button — so a malicious.fvaultrestore or a
hostile collaborator can't silently phone home the moment you
unlock the vault. Base64data:images render immediately (no
network request). - External links are hardened with
target="_blank",
rel="noopener noreferrer", and
referrerPolicy="no-referrer". The destination site never learns
which Flowvault URL or local file the click came from. javascript:and other non-HTTP link schemes render as text, not
as clickable links.
What didn't change
- The wire format. Your vault bytes,
.fvaultbackup format, and
plaintext Markdown.zipexport are identical to v1.2 — v1.3 is
a pure rendering addition with zero migration. - The crypto. Same Argon2id (64 MiB / 3 iter) + AES-256-GCM +
hidden-volume layout. - The server. Still sees only opaque ciphertext; rendering is 100%
client-side, post-decryption.
Docs
- Deep dive: Markdown preview and syntax-highlighted code, without
the usual leaks — https://flowvault.flowdesk.tech/blog/markdown-preview-code-highlighting - Updated FAQ section: Markdown preview & syntax-highlighted code
blocks — https://flowvault.flowdesk.tech/faq
Thanks to everyone who asked for Markdown rendering. Keeping it
lean and zero-third-party-request was the interesting design
problem — and the result is a preview you can trust to render a
vault full of untrusted content.
Flowvault 1.2.0 — Bring Your Own Storage
Flowvault started as "an encrypted notepad at a URL." 1.2.0 keeps that,
and adds the other end of the spectrum: an encrypted notepad that
lives as a single file on your own disk, with Flowvault acting only
as the editor.
No account. No server-side copy of the ciphertext. No upload on save.
Same hidden-volume format, same Argon2id + AES-GCM, same multi-notebook
tabs — just written straight to D:\notes\journal.flowvault (or
wherever you point it) via your browser's File System Access API.
This is the first non-Firestore backend. The storage layer was
refactored into a VaultStorageAdapter interface specifically so more
can follow — S3-compatible (R2 / B2 / Wasabi / MinIO) and WebDAV are
next on the roadmap if there's demand.
Highlights
🗄️ Bring Your Own Storage — local .flowvault files
- Create or open a local vault straight from the home page. Pick a
file, set a password, start writing. The editor opens at
/local/<uuid>and every save round-trips to the file on disk. - Single-file format. The
.flowvaultfile is a tiny JSON header
(format version, per-file UUID, Argon2id salt, KDF parameters, volume
layout, a monotonic CAS counter, timestamps) 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,
.fvaultbackup, and
plaintext Markdown export all work the same way they do on hosted
vaults. Only trusted handover is (by design) disabled for local
vaults — it needs a server-held scheduler, and a file on your disk
has no server watching it. - Zero server involvement for vault I/O. The server never sees the
ciphertext, the file path, the file name, or the UUID — the
/local/<uuid>route is entirely client-side and the UUID is
generated in your browser. - Optimistic concurrency still works. Each save is gated by a CAS
counter inside the file; two devices writing to the same file get a
conflict instead of a silent clobber. - Portable. Copy the file (USB stick, cloud sync, encrypted email —
whatever fits your threat model). On the other device, click
Open local vault, point at the copied file, enter your password.
Everything travels with the bytes. - Browser support. Chromium-based desktops today (Chrome, Edge,
Brave, Opera, Vivaldi, Arc). Firefox and Safari don't implement the
File System Access API yet, so the buttons disable themselves with
a note on those browsers. Hosted vaults at/s/<slug>still work
everywhere.
🔌 Pluggable storage layer (VaultStorageAdapter)
Internally, every vault-blob read/write now goes through a per-site
storage adapter. The Firestore backend is one implementation, the
local-file backend is another, and the dispatcher picks the right one
based on the route you're on. This is the groundwork for:
- S3-compatible backends (AWS S3, Cloudflare R2, Backblaze B2,
Wasabi, MinIO) — great for people who want versioned object storage
they already pay for. - WebDAV backends (Nextcloud, ownCloud) — good self-hosting story.
- Experimental decentralised backends (IPFS / Storj / Arweave).
All of those will follow the same rule as the local-file adapter: the
blob stays opaque, the adapter just moves bytes, and server-dependent
features (trusted handover, hosted routing) stay on the Flowvault
Firestore backend. Prioritisation is driven by demand — please
open a GitHub issue
if a particular backend would unblock you.
📄 MIT license, officially
Flowvault has always intended to be MIT, but the license wasn't spelled
out in the repository. 1.2.0 fixes that: there's now a top-level
LICENSE file with the standard MIT text, and package.json declares
"license": "MIT". Nothing changes about how you can use or fork the
project — this is just paperwork catching up to reality.
Smaller changes
- Editor chrome is BYOS-aware: a local vault shows
local: journal.flowvaultinstead of/s/<slug>in the toolbar,
and the Handover button is hidden (rather than disabled) for local
vaults so it doesn't promise something we can't deliver. - Password gate has optional
displayOverrideand
descriptionOverrideprops, used by the local-vault screen to say
"Enter the password for<filename>" instead of referencing a URL
slug that doesn't apply. - Plaintext Markdown export works on local vaults too. The README
index inside the zip says "local vault" instead of/s/<slug>when
there isn't one. - Export menu and filename suggestions now accept nullable slugs
cleanly (vault.zipfallback for BYOS). - Zustand vault store extended with
storageKind, nullableslug,
anddisplayLabel— the "one URL, one vault" assumption that was
baked into a lot of UI code has been lifted. - IndexedDB-backed handle registry keeps
FileSystemFileHandle
references around between sessions so that reopening a local vault
is one click + a browser permission prompt, not a full re-pick from
disk. (Handles are origin-scoped and permission isn't persisted by
the browser, so you'll always see a permission prompt on the first
save of a new session — that's the web platform, not us.)
Docs & SEO
- New FAQ section: Bring Your Own Storage (local
.flowvaultfiles;
S3 / WebDAV on the roadmap) with 12 Q&As covering what BYOS is,
what the server sees for local vaults, browser support, the on-disk
file format, moving between devices, two-device edits, why trusted
handover is intentionally disabled for local vaults, how
time-locked notes and Encrypted Send still work,.flowvaultvs
.fvault, what happens if you lose the file, and the S3/WebDAV
roadmap. - Home page gets a new Bring your own storage feature card and a
row in the Flowvault vs ProtectedText comparison table. - README adds a BYOS bullet, a row in the security table, and a
reshuffled roadmap (local file: shipped; S3 / WebDAV / IPFS: planned). SoftwareApplicationJSON-LD now advertises BYOS infeatureList.
Threat-model notes
BYOS genuinely reduces what our backend can see about you — for local
vaults, the ciphertext and metadata never reach us at all. But it
doesn't change your local threat model: a .flowvault file sitting on
your disk is still a file on your disk, subject to the usual forensic
risks (shadow copies, cloud-sync providers, file-system journaling).
If that's part of your threat model, store the file on an encrypted
volume (VeraCrypt, LUKS, FileVault) the same way you would any other
sensitive file.
A full writeup will land as a blog post:
flowvault.flowdesk.tech/blog/bring-your-own-storage-local-vaults.
Upgrade
No action required. Existing hosted vaults are unchanged — same slugs,
same URLs, same pass
v1.1.0 — Encrypted backup & restore, plaintext export, and the Flowvault Blog
Flowvault v1.1.0 is a portability-and-docs release. You can now snapshot an
entire vault — every slot, every decoy password, every tab — into a single
zero-knowledge .fvault file, restore it to any Flowvault instance (hosted
or self-hosted), or export the currently-open notebook as a plaintext
Markdown zip for migration to Obsidian, git, or Standard Notes. We also
launched the Flowvault Blog with seven long-form posts that explain every
feature from first principles, and tightened the user-facing copy around
the trusted-handover flow.
Highlights
- Encrypted backup (
.fvault) — one-click download of the opaque
ciphertext the server already holds, plus its KDF salt and volume
layout, bundled into a single JSON envelope. Still zero-knowledge on
disk: no password, no plaintext, decoy slots indistinguishable from
random bytes. Round-trips every password in the vault. - Plaintext Markdown export (
.zip) — behind an explicit confirmation,
the currently unlocked slot's tabs are written to.mdfiles. Decoy
notebooks behind other passwords are never included, preserving
deniability even if you export under coercion. - Restore page (
/restore) — drop a.fvaultfile onto any fresh URL
slug. No password prompt during restore (there's no decryption
happening); Flowvault just re-seats the ciphertext. Existing slugs are
rejected — you pick a new one. - Flowvault Blog (
/blog) — seven deep-dive posts with full SEO
wiring (per-post canonical, OpenGraph article, Twitter card,
BlogPosting + BreadcrumbList JSON-LD, sitemap entries). - Trusted-handover copy refresh — the inheritance feature is now
called Trusted handover across all visible UI. The mechanism is
unchanged; the wording is just less alarming.
What's new
SEO & docs
- New FAQ section: "Backup, restore & migration (
.fvault, Markdown
export, self-hosting)" with ten Q&As. - Home page: new feature card + two new comparison-table rows
("Encrypted backup / restore", "Plaintext export (Markdown)"). - Root metadata + home/FAQ/security keywords extended for the new
feature surface. - Sitemap now emits
/blog,/blog/<slug>for every post, and
/restore. - Navbar: added Blog link.
UX / copy
- All user-visible "dead-man's switch" wording replaced with "trusted
handover" (modal title, toolbar button, error messages, released-gate
text, FAQ, blog, README, OpenGraph image). The Firestore field name
and internal identifiers are unchanged — no data migration needed. - "Released" phrasing softened to "handed over" where it appears in
user-facing copy. - Homepage closing section and footer now cross-link to
/blog. - FAQ footer section links to every post by topic.
Fixes
- Internal error message from
sites.tswhen writing to a released
vault now reads "handed over to its beneficiary" instead of the
previous phrasing. - Minor a11y fix on the restore form (removed an unsupported
aria-disabledon a<section>with implicitrole="region").
Upgrade notes
- No breaking changes. Existing vaults open unchanged under their
existing passwords. Existing trusted-handover configurations keep
working (only the wording changed). - Backups are opt-in. If you never open the Export menu, nothing
about your vault leaves the server. Backups are generated and
downloaded purely client-side. - Restoring requires a fresh slug. We intentionally refuse to
overwrite a live vault at an existing URL; pick an unused slug on
/restore. - Self-hosters: the
.fvaultformat is stable atversion: 1.
Backups created against the hosted instance rest
Flowvault v1.0.0 — First public release
Zero-knowledge encrypted notepad with plausible deniability. One URL can
hide multiple notebooks behind different passwords, and the server cannot tell
how many notebooks actually exist.
- Live: https://flowvault.flowdesk.tech
- Source: https://github.com/Flowdesktech/flowvault
- Hire / contract engineering: contact@flowdesk.tech
What ships in 1.0
Hidden-volume vaults (the headline)
One URL, up to 64 independently encrypted slots, one opaque 512 KiB blob.
Every vault is exactly the same size on disk regardless of how much you've
actually written, so the ciphertext on the server is cryptographically
indistinguishable from a vault that holds a single decoy paragraph.
Hand over the decoy password at a border crossing; the real notebook stays
invisible.
Multi-notebook tabs per password
Each password now unlocks a workspace, not a single page. Add, rename,
reorder (drag-and-drop), and delete tabs. Tab titles, order, contents, and
the active-tab pointer all live inside the encrypted slot — the server
still sees one opaque blob. Decoy slots keep their own independent tab set.
Time-locked notes (drand + tlock)
Encrypt a message to a future date using drand's public randomness beacon
and identity-based encryption over BLS12-381 (tlock-js). The decryption
key literally does not exist until drand publishes the target round's
signature — we can't unlock it early, a subpoena can't unlock it early,
the sender can't unlock it early. Optional password gate on top
double-wraps the note with Argon2id(password) → AES-256-GCM, so a leaked
link plus elapsed time still isn't enough.
Encrypted Send (Bitwarden-Send-style)
One-shot, self-destructing shares for passwords, API keys, recovery phrases.
AES-256-GCM in the browser; the 256-bit key travels in the URL fragment
(#k=…) so browsers never send it to servers. Configurable view count (1–
100, default 1) and TTL (up to 30 days). Reads go through a Cloud Function
so the view counter is atomic; the server hard-deletes the ciphertext the
moment the last view is consumed. Firestore rules deny client reads of the
send document entirely. Optional password gate available.
Dead-man's switch
Arm a vault to auto-release to a pre-chosen beneficiary password if you
stop saving for the interval + grace you configure (weekly / monthly /
quarterly / yearly). The beneficiary key wraps your master key client-side;
the server just schedules the release. Hourly Cloud Function sweep plus
Firestore rules that forbid faking a release or extending one you can't
actually open.
Optimistic concurrency and conflict recovery
Edit the same vault in two tabs without losing work. Version-counter
transactions on writes; on conflict the client auto-refetches, merges
server state into the local store without dropping your edits, and retries.
Save rejections from the dead-man's-switch released state surface a clear
message instead of a silent promise rejection.
Editor
- Clean, modern dark-mode UI
- Keyboard-first: Ctrl/Cmd+S to save
- Debounced autosave for typing, immediate save for structural tab ops
- In-editor "Syncing" indicator, visible save status chip, dirty-state
warning before close - Slot capacity meter (bytes used / available, computed across all tabs)
- Drag-to-reorder tabs with visible borders and active-tab accent
Security
| Property | Flowvault |
|---|---|
| Password → key derivation | Argon2id, 64 MiB memory-hard, 3 iterations, HKDF-SHA256 expansion |
| Symmetric encryption | AES-256-GCM (authenticated) |
| Ciphertext size | Fixed 512 KiB, independent of content |
| KDF parameters | Stored in vault, upgradable without breaking old vaults |
| Optimistic concurrency | Yes — version-counter transactions |
| Time-lock | drand BLS12-381 IBE via tlock-js |
| Zero-knowledge firewall | Enforced by public, auditable Firestore rules |
| Tracking / analytics | None |
| Account / email / phone | None |
Open source, end-to-end
Not just the frontend. The Cloud Functions (dead-man's-switch sweep,
Encrypted Send atomic-view counter, Encrypted Send sweep) and the
Firestore security rules — the actual boundary that stops the operator
from reading or mutating your data — ship in this repo under MIT, deploy
unmodified, and are fully self-hostable.
Threat model
Published and specific. We tell you what we do and do not protect against,
including cases where plausible deniability is weaker (persistent network
observer correlating writes, traffic analysis, endpoint compromise).
See /security.
Deployment & tooling
- Frontend: Next.js 16 + React 19 + Tailwind 4, deployed to Vercel from
themasterbranch. - Backend: Firebase Firestore + Functions on the Blaze plan.
- CI: GitHub Actions — lint, type-check, and build on every push; Firebase
deploy workflow for Functions + rules + indexes on release tags. - SEO: sitemap,
robots.txt, FAQ withFAQPageJSON-LD,
SoftwareApplication/Organization/WebSitestructured data, OG +
Twitter cards, canonical URLs.
Donations
Crypto-only, via NOWPayments. No donor account, no email, fresh address
each donation, 100+ supported coins including Monero. We never see the
donor, only that a donation occurred. See /donate.
Known limitations / not yet shipped
- PWA / offline — on the roadmap for a subsequent release.
- Build reproducibility — release-commit bundle-hash publishing is
planned but not yet automated. - Network correlation — a persistent observer of your IP can correlate
your writes. Route via Tor / a VPN if this matters to you. This is
fundamental to the threat model, not a v1.0 bug.
Install / self-host
npm install
(cd functions && npm install)
cp .env.local.example .env.local # fill in Firebase + NOWPayments values
# dev
npm run dev
# ship
npm run build
firebase deploy --only firestore:rules,firestore:indexes,functions