What we can see, and what we cannot
Every financial value in Vault is encrypted in your browser with AES-GCM-256 before it is sent. The key is wrapped by a twelve-word recovery phrase that is never stored and never transmitted. The server holds ciphertext, row identifiers and dates. We cannot read your net worth, and neither can anyone who takes the database.

The key schedule, in full
Four steps, each one standard and none of them ours to invent.
- A twelve-word BIP-39 phrase, 128 bits from your browser's cryptographic random source, with a real checksum.
- The phrase derives a seed (PBKDF2-HMAC-SHA512, 2048 rounds), which derives a non-extractable AES-GCM-256 wrapping key (HKDF-SHA256).
- That wrapping key wraps a per-account data encryption key. Only the wrapped form is ever stored.
- The data key encrypts each row's values individually. What is stored is a version, an initialisation vector, and ciphertext.
What is deliberately not encrypted
Row identifiers, foreign keys, dates, the kind of a record, and your dashboard layout. Structure has to stay readable for the database to be a database. Every figure inside that structure is ciphertext. We list this rather than glossing over it, because a security page that claims everything is encrypted is a security page that is lying.
Unlocking with a passkey
Typing twelve words each time a tab closes is a good way to make people stop. Vault can wrap a second copy of the key under a secret your authenticator derives during an ordinary passkey check — Touch ID, Windows Hello, a security key. Enrolment happens only from an already-unlocked vault. Where a browser does not support it, the phrase still works and nothing errors.
The unwrapped key lives in one tab
Once unlocked, the key stays in that tab's memory and nowhere else. Close the tab and the vault re-locks. It is never written to disk, never sent to the server, and never shared with another tab.
The server side, and why it is boring
Postgres with row-level security, and an application role that owns nothing, holds no schema rights and cannot bypass those policies. Every request runs inside a transaction scoped to one user id; a request that fails to set it sees zero rows. Queries filter by user as well — the policy is the backstop, not the only guard.
Prices are public data, fetched without your name on the request
Quotes and exchange rates come from public market endpoints through a stateless proxy that carries no user identifier. The proxy exists to avoid rate limits and browser restrictions, not to observe you. It stores nothing about you.
What happens if you lose the phrase
Your figures are unreadable. Not by us, not by anyone, and there is no version of this that recovers them — that is what the phrase was. You can still sign in, and Vault offers one honest option: reset the vault. It destroys the unreadable data and the wrapped key, keeps the account, and starts you again. It sits behind a typed confirmation and the screen says exactly that, in those words.
Check the phrase before you need it
A phrase is written down once, at the most stressful minute of setup, and not looked at for a year. Settings has a check that asks for all twelve words and performs a real unwrap with them. It asks for twelve rather than three because the app never stored the phrase — the only honest test is whether it works.
What this does not protect you from
A compromised device or browser can read what you have unlocked. A phrase kept in a note on a synced phone is only as private as that phone. Encryption on your side of the wire is not a substitute for a locked laptop.