Five characters. Then we stop looking.
Hashing happens on your device
When you check whether a password is compromised, your browser computes a SHA-1 hash locally. We never see the password. The plaintext never crosses the wire.
Only the first five characters leave your device
Our server only receives the first five hexadecimal characters of the hash — a prefix that matches roughly 400 to 600 candidate hashes. We return all matches in that bucket; your browser checks the rest locally.
We cannot build a credential database
Because we never see the full hash and never see the plaintext, our query logs cannot be converted into a password database. The architecture is what makes the promise enforceable — not our intentions.
In short: your browser hashes the password on your device and sends us only the first five characters of that hash, never the password itself. We return every match in that bucket; your browser finds the one that matters. We can’t rebuild your passwords from what we see, by design.
The question this page answers
If you use the KnytX breach checker or the password check inside the Pro tier, you might reasonably ask:
“If I’m checking whether my password was leaked, doesn’t KnytX have to know my password?”
The answer is no, and this page explains why. It is not because we promise not to look. It is because the design makes looking impossible. This is the difference between a privacy promise and a privacy architecture.
The walkthrough
Step 1. Your password (or email) is hashed on your device
A hash is a one-way fingerprint of a string. The same input always produces the same hash. You cannot work backwards from the hash to recover the input without guessing the input first. For password checks we use SHA-1. Not because it is the strongest hash — it isn’t. It is the format the public Pwned Passwords corpus is published in, and matching that format is what lets us interoperate.
When you ask KnytX to check whether a password has been compromised, your browser does this locally:
password = "password"
hash = SHA1(password)
= "5BAA61E4C9B93F3F0682250B6CF8331B7EE68FD8"
That string of 40 hexadecimal characters never leaves your machine.
Step 2. We send the first five characters — and only those
prefix = hash[0..5] = "5BAA6"
suffix = hash[5..40] = "1E4C9B93F3F0682250B6CF8331B7EE68FD8"
Your browser sends 5BAA6 to our server. That is it. Five hexadecimal characters identify a bucket containing roughly one in every 1,048,576 possible hashes — typically 400 to 600 real hashes in our breach corpus.
The five-character prefix is what cryptographers call a k-anonymity boundary: every prefix is shared by hundreds of other possible passwords, so the server cannot tell which one you actually meant.
Step 3. Our server returns the entire bucket
The server looks up every hash in our breach engine that starts with 5BAA6 and returns the list of suffixes plus how many times each has appeared in known breaches. The response looks like:
1E4C9B93F3F0682250B6CF8331B7EE68FD8:1842
1E4C9B93F3F0682250B6CF8331B7EE68FE2:3
…approximately 400 more rows…
Step 4. Your browser does the final match — locally
Your browser scans the returned list for the suffix it computed in Step 1. If it finds a row, the count tells you how many breaches contain that password. If it does not, your password is not in our corpus.
The server never knows which row in the response your browser cared about. From its point of view, the request was indistinguishable from a request by any other user whose password happened to share the same five-character prefix.
Why this matters
We cannot, by design, build a database of your passwords
Suppose a hypothetical adversarial KnytX engineer wanted to build a database of which users use which passwords. What does our log show?
- An IP address (typically a Cloudflare edge IP, not your home IP)
- A timestamp
- The string
5BAA6
That is not a password database. It is not a credential leak when we get breached. It is a list of which 5-character prefixes were popular on a given day, which is approximately as useful as a list of which two-digit numbers got typed into a calculator. The full hash is unknowable to us; the plaintext is unknowable to anyone except your device.
We cannot lie about this
The architecture is verifiable. If you open your browser’s network inspector and watch a check happen, you can see exactly what we send. The relevant code path is in our open-source mobile apps under GPLv3 (see /transparency/open-source/), and the same model is used in the web client. The promise is not “we won’t look” — it is “we structurally can’t look”.
The same model applies to email and phone checks
The breach engine uses the same 5-character SHA-256 prefix model for email and phone lookups. Your full email and full phone number stay on your device for the routine breach check. (The dark-web monitoring scanners in Pro+ require plaintext because the underlying vendors are text-indexed, not hash-indexed — see the sub-processor list for which vendors get plaintext and which get hashes.)
Limits and honest caveats
- SHA-1 is weak for general cryptography. We use it for breach lookups because the Pwned Passwords ecosystem standardised on it. SHA-1 is fine for k-anonymity because we are not relying on collision resistance. We are relying on the cost of brute-forcing a specific 35-character suffix from a 5-character prefix, which is astronomical. Where we hash other data (email, phone), we use SHA-256.
- Your network sees that you made a request. The five-character prefix is meaningless, but the fact that you used our breach checker at a particular time is visible to your ISP. If that threat model matters to you, run KnytX through a VPN. In practice almost nothing useful leaks from the fact of a breach check.
- A k-anonymity check confirms presence; it does not confirm absence with certainty. If your password is not in our corpus, it means it is not in the breaches we have indexed. New breaches happen weekly. We re-scan our corpus and re-notify; we do not promise that a one-time clean result is forever clean.
- The breach corpus is a snapshot. We update it on a documented schedule. Email legal@knytx.com for the technical paper describing the full breach-engine architecture.
Where this fits in the broader trust story
K-anonymity is one of three architectural commitments we make:
- Hashed prefix queries for routine breach checks (this page).
- Local processing for biometric features — face and voice embeddings are extracted on our own infrastructure, never on a cloud API. See /legal/biometric-policy/.
- On-device processing for mobile call and SMS screening — the actual content of your messages and calls never reaches our servers. See /legal/mobile-permissions/.
Together, these mean the most sensitive parts of your data — passwords, biometrics, message bodies, call audio — never leave the device you control. We hold what we cannot avoid holding; we minimise what we do.
Questions
Architectural questions: security@knytx.com. Policy questions: legal@knytx.com.