Security and encryption
Your portfolio is encrypted in your browser before it reaches our servers, so a full database leak would reveal nothing about your finances. It cannot protect you from a compromised server shipping modified code; that limit, and every other one, is listed below.
How your keys fit together
- You type your password. Argon2id turns it and a salt into a key encryption key (KEK) inside your browser. The password itself never leaves the page.
- The KEK unwraps your data key, a random 256-bit key the server only ever stores in wrapped form. If you set up a recovery code, it unwraps a second wrapped copy.
- The data key encrypts your portfolio with XChaCha20-Poly1305, a fresh random nonce, and authenticated data bound to your account ID.
- Only ciphertext, the nonce, the salt and the wrapped keys reach our servers. The KEK and data key live in memory and are wiped on sign-out, tab close and idle timeout.
What we defend against
- Database leaks. Our hosting provider (Supabase) only sees ciphertext. Anyone who steals a backup or the database itself sees ciphertext.
- Hosting provider read access. Supabase staff cannot read your data, even with full database access.
- Subpoenas of stored data. We can comply by handing over the encrypted blob, but the blob alone reveals nothing without your password.
- Cross-user attacks. Your personal portfolio is cryptographically bound to your user ID, and a shared portfolio to its own ID. Even with full database write access, an attacker cannot move one user's data into another user's account, or one portfolio's into another, without it failing to decrypt.
- A stolen or lost device, after sign-out. Your encryption key lives only in browser memory and is wiped on sign-out, tab close and idle timeout.
- Automated attacks on the login. Sign-up and sign-in are rate-limited and gated by a CAPTCHA (Cloudflare Turnstile), which blunts bot sign-ups and credential-stuffing attempts.
What we do not protect against
These are real limits. We list them so you can weigh the protections above against them.
- An actively malicious server. Every web app, including this one, downloads JavaScript from a server on every visit. A compromised server could ship modified code that exfiltrates your password as you type. Bitwarden, Proton Mail and Standard Notes share this limit, as does every web-based end-to-end encrypted service. Our first-party code ships as hashed, immutable assets, so replacing the bytes requires a deploy by us. Signed builds and a native client remain the stronger mitigation. Subresource integrity is not applied to our third-party origins because the vendors do not publish per-version content hashes, and a pinned hash would break on the next silent rotation.
- A compromised device. Malware, keyloggers and malicious browser extensions run with your privileges. No application can defend its own user from this.
- Metadata. We can see that you have an account, your email, when you saved data, and roughly how big your portfolio is. If you share a portfolio, we also see who shares it with whom and the email address an invite was sent to. We can't see what's in any of them.
- The partner you share with. They can read, edit and export a shared portfolio, and keep copies of what they've seen. Removing them stops them reading later changes, not what they've already seen. Until it's used, an invite link is a key: send it privately. A leaked link alone doesn't let someone join, because it works only for the account it was addressed to.
- A forgotten password without a recovery code. If you forget your password and skipped the recovery code, your encrypted data is permanently unrecoverable. Nobody else holds a key that can decrypt it, including us.
- Coercion. If someone forces you to disclose your password, they disclose everything.
- A supply-chain attack on our dependencies. We pin lockfiles and review updates, but we are not immune.
Forgotten passwords and recovery codes
Because we cannot read your data, we cannot reset it for you. If you forget your password, the only way back in is a 24-word recovery code, if you set one up.
Setting up a recovery code is opt-in, and we strongly recommend it. The code is a BIP-39 mnemonic with 256 bits of entropy. We display it once and never store it (only a wrapping derived from it). Save it somewhere offline: a printed copy, a safe or a password manager. You can set up or rotate it from Settings, under Security.
Verify it yourself
The design and the implementation are open to inspection.
- Encryption design document
- Threat model, primitive choices, key hierarchy, AAD framing, schema and migration plan.
- Crypto source code (MIT)
- Small, pure functions with no I/O. Tests cover round-trip, tamper detection, AAD binding and cross-user isolation.
- Repository
- The full source. File an issue if you find a problem.
Disclosure and contact
If you find a security issue, please disclose it responsibly and do not open a public issue. Email [email protected] or use a GitHub private advisory.
Accessibility
Quantive aims to follow WCAG 2.1 AA. As a micro-business under EU criteria we are not subject to formal conformance reporting, but we treat accessibility as a baseline quality bar. If you run into an issue, email [email protected].
Try the app with illustrative data, or start your own record.