An open API service indexing awesome lists of open source software.

https://github.com/flowdesktech/flowvault

Privacy-first notepad: Argon2id + AES-256-GCM, hidden-volume plausible deniability (N notebooks per URL), optimistic concurrency, dead-man's switch, drand/tlock time-locked notes, and one-shot encrypted send. Next.js frontend, Firebase Functions, auditable Firestore rules. MIT.
https://github.com/flowdesktech/flowvault

aes-gcm argon2id client-side-encryption cryptpad-alternative encrypted-notepad end-to-end-encryption firebase firestore hidden-volumes nextjs plausible-deniability privacy-tools privnote-alternative protectedtext-alternative self-hostable standard-notes-alternative tailwindcss trusted-handover typescript zero-knowledge

Last synced: about 2 months ago
JSON representation

Privacy-first notepad: Argon2id + AES-256-GCM, hidden-volume plausible deniability (N notebooks per URL), optimistic concurrency, dead-man's switch, drand/tlock time-locked notes, and one-shot encrypted send. Next.js frontend, Firebase Functions, auditable Firestore rules. MIT.

Awesome Lists containing this project

README

          

# Flowvault

**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](https://useflowvault.com)** · **[1min intro (video)](https://youtu.be/wkoAIseRicY)** · **[Blog](https://useflowvault.com/blog)** · **[Security](https://useflowvault.com/security)** · **[Self-host](#setup)** · **[Donate](https://useflowvault.com/donate)**

## See it in 30 seconds — live demo, no sign-up

A 1-minute intro covering plausible deniability, time-locked notes, trusted handover, and Encrypted Send (also on **[YouTube](https://youtu.be/wkoAIseRicY)**):




Flowvault 1-minute intro — click to watch on YouTube


There's also a public demo vault at **[useflowvault.com/s/demo](https://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.

## Moving from another tool?

- **ProtectedText:** [modern ProtectedText alternative](https://useflowvault.com/alternatives/protectedtext) with Argon2id (64 MiB / 3 iter), AES-256-GCM, hidden-volume decoy passwords, and `.fvault` backups.
- **Privnote / Bitwarden Send:** [Encrypted Send](https://useflowvault.com/alternatives/privnote) creates view-capped, expiring, self-destructing links for short secrets.
- **Standard Notes / Notesnook:** [use Flowvault as the deniable scratchpad beside your main notes app](https://useflowvault.com/alternatives/standard-notes), not as a full PKM replacement.
- **Plausible deniability:** [try the live use case](https://useflowvault.com/use-cases/plausible-deniability) with `CorrectPassword` and `DecoyPassword`.

## Is this the right tool for you?

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.
- A one-shot self-destructing **file** upload (**Encrypted File Send**) for sharing up to 10 MiB — recovery key files, signed PDFs, screenshots of secrets — with a separate *secure delete link* you can fire any time before it expires.
- To keep the ciphertext off our servers entirely — same app, but the vault lives as a single `.flowvault` file on your own disk (**Bring Your Own Storage**).

**Pick something else if:**

- You want a multi-device notes app with sync — try [Standard Notes](https://standardnotes.com) or [Notesnook](https://notesnook.com). 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](https://obsidian.md) or [Joplin](https://joplinapp.org) with your own E2EE sync instead.
- You need real-time collaborative editing. Use [CryptPad](https://cryptpad.fr). 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](https://keepassxc.org) 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](https://useflowvault.com/security) for the honest version.

---

## What's unique in this category

- **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](https://useflowvault.com/blog/plausible-deniability-hidden-volumes-explained))
- **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](https://useflowvault.com/blog/trusted-handover-encrypted-notes-beneficiary))
- **Time-locked notes** — drand + tlock identity-based encryption, so even the sender can’t decrypt a message before its target date. ([deep dive](https://useflowvault.com/blog/time-locked-notes-drand-tlock))
- **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](https://useflowvault.com/blog/encrypted-send-vs-bitwarden-send-privnote))
- **Encrypted File Send** — same shape as Encrypted Send, but for files up to 10 MiB. Pick an expiry (max 7 days) and a download cap (default 1); the encrypted ciphertext lives in Cloud Storage and is hard-deleted the moment the last download is consumed. The sender also gets a separate **secure delete link** (a token bound to the upload by SHA-256) so they can destroy it before the cap or expiry. Optional password gate, just like Encrypted Send.
- **Bring Your Own Storage** — keep the whole ciphertext on your own disk as a single `.flowvault` file 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](https://useflowvault.com/blog/markdown-preview-code-highlighting))
- **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 `.fvault` backup + plaintext Markdown export** — portable across self-hosted instances, still opaque on disk. ([format spec](https://useflowvault.com/blog/encrypted-backup-fvault-format))
- **Open end-to-end.** Frontend, Cloud Functions, and Firestore security rules all live in this repo. MIT-licensed. Self-hostable. ([vs ProtectedText](https://useflowvault.com/blog/flowvault-vs-protectedtext))

Security and crypto review is welcome. Open a GitHub issue with the
`security-review-wanted` or `crypto-review-wanted` template if you spot
a threat-model gap, metadata leak, crypto invariant problem, or wording
that overclaims what Flowvault can protect.

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](https://flowdesk.tech)**, 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](https://flowcrypt.com)** (OpenPGP email — iOS + Chrome
> Extension, 2022–2026). Limited engagements per quarter —
> reach out at
> **[contact@flowdesk.tech](mailto:contact@flowdesk.tech?subject=Flowdesk%20%E2%80%94%20project%20inquiry%20(via%20Flowvault))**.

---

## Why use Flowvault instead of ProtectedText (or similar)?

Flowvault is not "another ProtectedText clone." It's a deliberate upgrade on
nearly every dimension that matters for a zero-knowledge notepad.

### Security

| 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 |

### Features you actually want

- **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](https://drand.love) public randomness beacon and the [tlock](https://github.com/drand/tlock-js) 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/`. **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 like `useflowvault.com/send/#k=`.
- **Encrypted File Send** — same threat model as Encrypted Send, but for actual files. Drop a file (up to 10 MiB), pick an expiry (max 7 days) and a download cap (default 1, max 10), and you get back two links: a *download link* to share with the recipient (`useflowvault.com/file/#k=`) and a *secure delete link* to keep for yourself (`useflowvault.com/file//delete#t=`). The file bytes are AES-256-GCM encrypted in the browser; the ciphertext lives in Cloud Storage and the metadata (filename, type, size) is encrypted into a small AEAD blob in Firestore. A Cloud Function atomically consumes a download by issuing a short-lived (5 min) v4 signed URL, deletes the document on the final download, and the scheduled `fileSendsSweep` purges expired or consumed objects (plus orphan-uploads from failed creates). The secure delete is authorized by SHA-256-matching a 256-bit token — the server never stored the token, so possession of the original delete link is the proof. Optional password gate is the same Argon2id-derived layer as Encrypted Send, mixed into HKDF so an attacker who steals the URL still needs an out-of-band password.
- **Bring Your Own Storage (BYOS) — local `.flowvault` files.** 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 `.fvault` backup/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](https://github.com/Flowdesktech/flowvault/issues/new) 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** (``, `<iframe>`, arbitrary tags render as literal text), **external images are click-to-load** with a placeholder showing the URL (so `![](https://attacker/pixel?v=target)` can't silently phone home the moment your vault opens), and **external links open with `target="_blank" rel="noopener noreferrer" referrerPolicy="no-referrer"`** so the destination site never learns where the referrer was. Code highlighting runs locally via `prism-react-renderer` &mdash; no network request, no WASM, no remote theme fetch. The renderer bundle itself is lazy-loaded via `next/dynamic`, so users who live in Edit mode never download it.
- **In-memory Cmd+K search across unlocked notebooks.** `Ctrl`/`Cmd` + `K` opens 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, `Enter` to jump straight to the match with the range selected in the textarea (so the browser scrolls it into view for you), `Esc` to 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 `.fvault` file (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.

### Trust & transparency

- **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 `npm` workspace.
- **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.

### Ergonomic upgrades

- 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

---

## Setup

```bash
# 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,storage

# 4. Run locally
npm run dev
```

To exercise the Firebase emulators instead of deploying:

```bash
npx firebase emulators:start --only firestore,functions,storage
```

> **Encrypted File Send setup:** the feature uses Firebase Cloud
> Storage in addition to Firestore. Three one-time setup steps are
> required before the first deploy will succeed:
>
> 1. **Provision the default Storage bucket.** Open
> `https://console.firebase.google.com/project/<project-id>/storage`
> → **Get started** → pick a region (match your Firestore region
> when you can). Without this, `firebase deploy --only storage`
> fails with `Permission 'firebasestorage.defaultBucket.get' denied`
> or `defaultBucket … may not exist`. The CLI cannot create the
> default bucket itself; the console flow is required once.
> 2. **Grant the deploy service account the Firebase Storage role.**
> Add `roles/firebasestorage.admin` (or the broader
> `roles/storage.admin`) to whatever service account is in your
> `FIREBASE_SERVICE_ACCOUNT` GitHub secret &mdash; the
> Firebase Rules Admin role alone is not sufficient to look up the
> default bucket configuration. Quickest path:
> ```bash
> gcloud projects add-iam-policy-binding <project-id> \
> --member="serviceAccount:<deploy-sa-email>" \
> --role="roles/firebasestorage.admin"
> ```
> 3. **Grant the Functions runtime service account the Token Creator
> role on itself** (`roles/iam.serviceAccountTokenCreator`), so
> `readFileSend` can sign v4 download URLs. Without this, the
> function falls through to an `internal` error on the first call.
> The runtime SA on a v2 Cloud Functions deploy is usually
> `<project-number>-compute@developer.gserviceaccount.com`;
> confirm with
> `gcloud functions describe readFileSend --gen2 --region=us-central1 --format='value(serviceConfig.serviceAccountEmail)'`.
> The role must be bound on the SA *resource*, not at the project
> level &mdash; use `gcloud iam service-accounts add-iam-policy-binding`
> (singular `service-accounts`), not `gcloud projects ...`:
> ```bash
> SA=<runtime-sa-email>
> gcloud iam service-accounts add-iam-policy-binding "$SA" \
> --member="serviceAccount:$SA" \
> --role="roles/iam.serviceAccountTokenCreator"
> ```
> 4. **Set CORS on the Storage bucket** so the browser can fetch
> signed-URL ciphertext. `firebase deploy` handles `storage.rules`
> but not bucket-level CORS; apply once with:
> ```bash
> gcloud storage buckets update gs://<bucket-name> \
> --cors-file=storage.cors.json
> ```
> Without this, browser downloads fail with `ERR_FAILED 400` /
> `Failed to fetch`. The committed `storage.cors.json` allows the
> canonical app origin, the Firebase Hosting domains, and
> localhost; expand it if you serve File Send from a different
> host.

## Firestore schema (summary)

```
sites/{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

fileSends/{id} # Encrypted File Send metadata
storagePath: string # "fileSends/{id}" — points at the Storage object
ciphertextSize: number # bytes in the Storage object (≤ 10 MiB + AEAD overhead)
metadataCiphertext: bytes # small AEAD blob: { name, contentType, size }
expiresAt: Timestamp
maxViews: number # 1..10
viewCount: number # bumped by readFileSend (Admin SDK only)
deleteTokenHash: bytes # SHA-256 of the 256-bit secure-delete token
passwordProtected?: bool
passwordSalt?: bytes # 16 bytes when password gate is enabled
consumedAt?: Timestamp # set on the final download; sweeper drops the object
createdAt: Timestamp
```

The File Send ciphertext itself lives in **Cloud Storage** at
`fileSends/{id}`; `storage.rules` denies client reads (only Admin SDK
can sign URLs) and caps uploads at 10 MiB with `application/octet-stream`.

See `firestore.rules` and `storage.rules` for the complete
zero-knowledge rule set.

## Security & threat model

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).

## Roadmap

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/new` compose → `/t/{id}` view) via [tlock-js](https://github.com/drand/tlock-js)
- Encrypted Send: one-shot, self-destructing notes (`/send/new` → `/send/{id}#k=<key>`)
- Encrypted File Send: one-shot, self-destructing file uploads up to 10 MiB with a secure delete link (`/file/new` → `/file/{id}#k=<key>` + `/file/{id}/delete#t=<token>`)
- 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 `.flowvault` vault 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 `VaultStorageAdapter` interface the local-file adapter already uses. Prioritised by user demand — [open a GitHub issue](https://github.com/Flowdesktech/flowvault/issues/new) if one of these would unblock you.
- PWA / offline mode
- Signed build hashes + transparency log

## Deployment & CI

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 to `master` that touch
`functions/`**, `firestore.rules`, `firestore.indexes.json`,
`storage.rules`, or `firebase.json` (plus a manual
`workflow_dispatch`). Builds the Functions, authenticates with a
service account, and runs
`firebase deploy --only functions,firestore:rules,firestore:indexes,storage`.

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, Firebase Storage 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` &rarr; production deploy at the canonical domain.
- Push to any other branch, or open a PR &rarr; preview deploy at a
`*.vercel.app` URL.
- `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 &mdash; for
example to keep all build logs in one place &mdash; 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.)

## Support Flowvault

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.

### Crypto donations via NOWPayments

Donations go through the [NOWPayments](https://nowpayments.io) 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.

### Configuring the donation widget

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.

### Non-crypto ways to help

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

## License

MIT. See [`LICENSE`](./LICENSE) for the full text.

---

<p align="center">
<a href="https://www.shipit.buzz/products/flowvault?ref=badge" target="_blank" rel="noopener noreferrer"><img src="https://www.shipit.buzz/api/products/flowvault/badge?theme=dark" alt="Featured on Shipit" /></a>
</p>