How the locks work
The technical version of “we can’t read it”. If you just want the plain-English map, see what we can see.
In short: Splash encrypts synced data on your device with AES-GCM-256, using a key derived with PBKDF2-SHA256 (310,000 iterations) that never reaches our server. Partner shares use a separate key carried only in your QR code. The server (Supabase, EU) stores ciphertext and account metadata. Someone with your unlocked phone remains the main risk.
Last updated
On this page 11 sections
Summary
- Local-first. The app works fully offline with no account. Data lives in your browser’s storage on your device.
- End-to-end encrypted sync. With an optional account, your whole state is encrypted on your device before upload. The server only stores ciphertext.
- Keys stay with you. Keys leave your devices only inside QR codes or links you choose to show, and inside your recovery code.
- Standard primitives only. Everything uses the browser’s built-in WebCrypto API. No custom ciphers.
- Described in full. Every primitive, parameter and server rule is on this page. Security reports are welcome (see below).
Want to see it working? Splash needs no account; your logs stay on your phone unless you turn on encrypted backup. Get Splash, free
The cryptography
| Cipher | AES-GCM with 256-bit keys, a fresh random 12-byte IV for every encryption |
|---|---|
| Payload | JSON → UTF-8 → gzip (where the browser supports CompressionStream) → AES-GCM → base64url |
| Key wrapping | PBKDF2-SHA256, 310,000 iterations, random 16-byte salt, deriving an AES-GCM-256 key from your recovery code |
| Recovery code | 24 characters in 6 groups of 4 (Crockford base32, 120 bits), shown once |
| Share proof | SHA-256 of the share key, compared server-side before any ciphertext is returned |
| Hosting | Supabase, Paris, France (EU, eu-west-3); app files served by Vercel |
You can watch the exact same encryption code run on the privacy page demo.
Vault key and recovery
When you set up sync, your first device makes a random 256-bit vault key. It is stored in your browser’s IndexedDB and encrypts everything you sync.
To let you recover on a new phone, the vault key is wrapped (encrypted) with a key derived from your recovery code using PBKDF2-SHA256 with 310,000 iterations and a random salt. Only the wrapped key and salt are stored on the server. Without the code, the wrapped key is useless.
To add a device without typing the code, your old phone can show a “link device” QR code that carries the vault key directly. Treat that QR code like a house key: only show it to your own new phone.
Sharing with people you choose
- Every partner share gets its own random share key. Your device encrypts a snapshot containing only the categories you ticked, and uploads the ciphertext.
- The share key travels to your partner only inside the QR code or link you show them. Their app decrypts on their phone.
- To fetch a share, their app must send the SHA-256 of the share key. The server compares it with the stored hash. A share ID on its own returns nothing.
- Stop sharing wipes the share’s ciphertext and pending notifications. Pause freezes the last snapshot server-side so no device can update it. Shares can also expire.
- Partner replies (“On it”) are encrypted with the same share key, rate-limited, and deleted after 3 days.
- The server does see which categories a share includes (for example “cycle, mood”), but not their contents.
Clinician links
- A clinician link uses the same design as a share: a fresh 256-bit key per link, carried only in the part of the URL after the
#, which browsers never send to a server. - The server stores the AES-GCM ciphertext of the report sections you picked, plus a hash of the key as proof. Links end on their own after 24 hours, 7 days or 30 days, and you can revoke one sooner.
Backup files
A backup file (.splash) is encrypted on your phone with AES-256-GCM, using a key derived from a password you choose with PBKDF2-SHA256 (310,000 iterations and a random salt). It includes your photos. Plain JSON export is still there under Advanced, for moving data to other tools.
Server and access rules
- All tables use Postgres row-level security. Signed-in users can reach only their own rows. Anonymous visitors have no table access at all.
- Partners never read tables directly. They go through a few database functions that check the share proof first.
- Every table cascades from the account, so deleting your account deletes every row about you.
- The privileged service key exists only inside the server function that sends notifications. It is never shipped to browsers or kept in the code.
Everything the server can read, in one list:
- Your account email
- When you sync, and how big the copy is
- Which categories a share includes (not the data)
- Push notification addresses
- Notification text and send time (neutral by default)
Notifications
Web Push needs the server to know what to send and when, so notification text is the one thing stored as plain text. Your device schedules the next 60 days of reminders. By default they use neutral wording such as “Reminder”. Send times can still hint at predicted dates, which is why discreet wording is the default. Sent jobs are deleted after 14 days.
Threat model
If we were ever legally compelled, this is the full list of what we hold: your account email and sign-up date; the locked copy of your data (ciphertext we can’t read) and when it synced, with its size; which categories a partner share includes (not the data itself); push notification addresses, and notification text and send times (neutral by default); our host’s short-lived request logs: IP address, device type and time.
| Threat | What protects you |
|---|---|
| Our server is breached, or we’re asked for data | Logs, settings and snapshots are ciphertext; we don’t hold keys |
| Someone with your unlocked phone | PIN lock, decoy PIN, discreet mode, panic wipe |
| Someone gets your share link or QR | Stop sharing at any time; shares can expire |
| Network eavesdropping | HTTPS everywhere, plus the payload is already encrypted |
| Malicious code in the app itself | No ad or analytics SDKs in the app |
If you’re worried about a partner, family member or anyone else checking your phone, read safety.
This website
The site you’re reading is static. It sets no cookies, uses no local storage, loads no third-party scripts or fonts, and sends a strict Content-Security-Policy (script-src 'self', connect-src 'self', frame-ancestors 'none'), HSTS and a locked-down Permissions-Policy.
Report a vulnerability
Found something? Please email hello@splash.care with steps to reproduce. We’ll reply as soon as we can, keep you updated, and credit you if you’d like. Please don’t access other people’s data, and give us reasonable time to fix things before going public.
Machine-readable details: /.well-known/security.txt. There is no paid bug bounty yet.
Encryption described in full, in an app that costs nothing.
No account needed. If you turn on backup, it is locked on your phone before it leaves.
Last updated 28 October. Every change, including anything that touches your privacy, is in the public changelog.
