Skip to content

Releases: Flowdesktech/flowvault

Flowvault v1.4.0 — Markdown preview without the leaks Latest

24 Apr 04:52

Choose a tag to compare

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, Enter jumps to the
    match, Home/End go to the first/last hit, Esc closes.
    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 ⌘K button 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, no localStorage cache. 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

22 Apr 13:50

Choose a tag to compare

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 ![](https://…) URL
    shows up as a placeholder with the exact URL and an explicit
    "Load image" button — so a malicious .fvault restore or a
    hostile collaborator can't silently phone home the moment you
    unlock the vault. Base64 data: 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, .fvault backup format, and
    plaintext Markdown .zip export 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

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

22 Apr 07:44

Choose a tag to compare

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 .flowvault file 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, .fvault backup, 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.flowvault instead 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 displayOverride and
    descriptionOverride props, 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.zip fallback for BYOS).
  • Zustand vault store extended with storageKind, nullable slug,
    and displayLabel — 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 .flowvault files;
    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, .flowvault vs
    .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).
  • SoftwareApplication JSON-LD now advertises BYOS in featureList.

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

21 Apr 09:37

Choose a tag to compare

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 .md files. Decoy
    notebooks behind other passwords are never included, preserving
    deniability even if you export under coercion.
  • Restore page (/restore) — drop a .fvault file 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.ts when 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-disabled on a <section> with implicit role="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 .fvault format is stable at version: 1.
    Backups created against the hosted instance rest

Flowvault v1.0.0 — First public release

20 Apr 09:19

Choose a tag to compare

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.


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
    the master branch.
  • 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 with FAQPage JSON-LD,
    SoftwareApplication / Organization / WebSite structured 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