We cannot read it
It reaches us already encrypted, and the key is derived on your device from a passphrase we never receive. A full copy of our database is a pile of ciphertext.
Most privacy policies are written to survive a lawyer. This one is written against the
database schema, which is published, so every claim below can be checked against
server/src/db/schema/pg.ts rather than taken on trust. Where
the honest answer is "we have not built that yet", it says so.
It reaches us already encrypted, and the key is derived on your device from a passphrase we never receive. A full copy of our database is a pile of ciphertext.
We keep per-feature means and standard deviations. We do not keep your keystrokes, and after enrolment finishes we do not keep the measurements they came from.
That is the whole of it. No name, no phone number, no address, no payment details — there is nothing to pay for yet.
The marketing pages carry GA4 and it sets cookies. The Rhythm Trial deliberately carries none, because that page's whole claim is that nothing leaves your browser.
The remaining tables hold nonces, token buckets and lockout counters — mechanical state with no content of yours in it. Everything keyed to your account goes when the account goes, and the audit row below says precisely what that leaves behind, because "we delete everything" is the kind of sentence a policy says and a schema quietly contradicts.
| Table | What is actually in it | Can we read it? |
|---|---|---|
vault_items |
Ciphertext and a nonce, per item. A version number and a cursor so your devices can sync. Nothing else — not the site, not the username, not the title. | No |
users |
Your username and email in the clear. A salt, the Argon2id cost parameters, and a hash we compare against — never your passphrase. Your vault key, wrapped. The timestamp of your consent and the policy version you consented to. | Username and email, yes |
biometric_profiles |
Per-feature means, standard deviations and weights. The length of your script, the number of samples it was built from, and commitments that let us check you typed the same phrase — not what the phrase is. | Yes, and it is not reversible |
enrollment_samples |
The eight measurement vectors, and only while you are enrolling. The moment the profile is built they are deleted. This is the one table in the schema permitted to hold a feature vector, and a test enforces that. | Briefly, then gone |
auth_score_history |
One row per unlock: a similarity score, the band it fell in, which device, and when. This is how thresholds will adapt to you rather than to an average stranger. It is a number about a comparison, not a recording of typing. | Yes |
devices |
A public key, the name you gave the device, its platform, and when it was last seen. You can see this list and revoke any row in it from settings. | Yes |
backup_codes |
One-way hashes of your ten codes, and whether each has been used. The codes themselves are not recoverable from this table, by us or by anyone who steals it. | No |
audit_log |
Security events — logins, device changes, step-ups. The IP address is hashed, never stored in the clear. Deleting your account removes every row that named you. What is left is the row recording the deletion itself, and rows from signup and login, which were written with no account on them at all because there was no session yet to name one. | Event and timestamp, yes |
Two things are deliberately absent from that list and from the schema: any column for raw keystroke events, and any column for a passphrase. A test asserts their absence by pattern, so adding one is a build failure rather than an oversight.
How you type is information about your body. Several jurisdictions — Illinois' BIPA, the GDPR's Article 9, Texas and Washington — treat that as a special category needing explicit, informed, opt-in consent rather than a checkbox buried in terms. We assume the strictest of them applies to everyone, because sorting users by jurisdiction to decide who deserves consent is not a thing we want to build.
Consent is explicit and versioned. You tick a box that names this policy by version before anything is captured, and the version you agreed to is stored on your account. If this document changes materially, that number changes, and you will be asked again rather than deemed to have agreed retroactively.
There is always a visible indicator. The Rhythm Light is on screen whenever a field is measuring, and it is the only time a field is measuring. It is not a decoration we could quietly stop honouring — the capture component and the light are the same component.
What a login does with your timings. Your browser turns the keystrokes into a vector of numbers and sends that. The server compares it to your profile, returns a band — pass, grey or fail — and discards the vector in the same request. It is never written to a table. The server also never returns the score itself, which is a security property rather than a privacy one, and the technology page explains why.
What we cannot do with the profile. Means and standard deviations are not a recording. You cannot reconstruct a passphrase from them, and you cannot replay them at a login — ours or anyone else's. That is the point of storing statistics: a stolen profile is not a usable impersonation of your hands.
You can turn it off. Settings has a pause, and disabling the rhythm factor entirely is supported — it falls back to passphrase plus step-up. Deleting the profile deletes the statistics; nothing derived from them is kept elsewhere.
It never leaves your device. What crosses the network is a value derived from it through Argon2id and HKDF, and what we store is a hash of that.
The audit log stores a hash of it. Rate limiting stores an HMAC of it under a server secret. Neither table can be read backwards into an address.
No key names, no key codes, no per-key timings after enrolment. The schema has no column that could hold them.
Encryption and decryption happen on your device. The server has never seen a decrypted item and has no code path that could.
We would rather tell you than have you find it in devtools. The marketing pages —
this one, the landing page, technology, compare, pricing, beta, founder — load Google
Analytics 4, property G-J20MXLEEJV. It sets
_ga and _ga_* cookies in
your browser and reports page views to Google LLC in the United States, who process
it on our behalf.
We use it for one thing: which pages people read before they ask for a beta seat. We do not send it anything about your typing, and there is nothing about your account to send, because these pages have no account.
The Rhythm Trial carries none of it. /demo.html tells you in its own rail that it has made zero network requests, and that claim has to be true on the one page a sceptical visitor is most likely to check. A test in the repository fails the build if analytics ever reaches that page, including indirectly through a shared bundle.
How to refuse it. Google publishes a browser opt-out add-on, and any content blocker stops it. Nothing on this site breaks without it.
What we owe you here, and have not yet built. Under EU and UK ePrivacy rules, analytics cookies want consent before they are set, not disclosure afterwards. We do not yet show a consent banner. That is a gap, it is named in our own roadmap, and it will be closed before public launch — either with a banner or by replacing GA with something that sets no cookies at all. Until then, this paragraph is the honest version of a notice you should have been given first.
| Who | What for | What they can see |
|---|---|---|
| Google Cloud | Hosting — Cloud Run, Cloud SQL and the CDN, in us-central1 |
The database as it sits on their disks, which is the encrypted vault and the tables above. Same view we have, and no more. |
| Google Analytics | Page views on the marketing pages | Which page, from roughly where, on what kind of device. Never the trial. |
| MXroute | Mail for @cypherkey.io |
Email you send us, because that is what a mail host is. |
No advertising networks, no session recording, no heatmaps, no chat widget, no A/B testing service, no CDN for fonts — the fonts are served from this origin precisely so that loading a page tells nobody else you did. The site's Content-Security-Policy is what enforces most of that, and it is the reason adding a tracker here is a deliberate act rather than a paste.
Export. Your vault is already yours — the extension exports it, decrypted, on your device, without asking us. That is the only copy that is readable anywhere.
Deletion. Email hello@cypherkey.io from your account address and we will delete the account. It is one operation and it is immediate: the database removes your vault, your rhythm profile, your devices, your tokens and your Backup Codes in the same statement, because every one of those tables is keyed to your account and set to go with it. Your audit rows are deleted alongside them. Nothing is queued, and there is no thirty-day window in which we still hold it.
This should be a button, and it is not one yet. The server can do it — the endpoint exists, and it requires your passphrase in the request rather than merely an unlocked session, so a borrowed laptop cannot destroy a vault. What is missing is the control on the settings screen, which is why the email above is the real mechanism today rather than a fallback we mention politely. The screen ships before public launch.
Correction and access. The settings screen shows your devices, your rhythm profile's summary and your account details. Anything it does not show, ask us for.
Self-hosting. The whole server is AGPL and runs with
docker compose up. Run it yourself and none of this
section is about you — there is no account with us to delete, and no third party in
the list above except the ones you choose.
This policy is version 2026-09-01. Your account stores the version you consented to, so we can tell the difference between someone who agreed to this text and someone who agreed to an older one. A material change gets a new version and a fresh ask — not a "last updated" line at the bottom and an assumption you re-read it.
Security disclosure has its own address and its own policy:
security@cypherkey.io, and
SECURITY.md in the repository.