Security audit
How Jura protects you,
checked line by line.
Most apps ask you to trust a sentence. This page tells you what the software does, where it is strong, and — just as plainly — where it is not. Every claim below corresponds to code in the running system; the honest limits are in the same list as the guarantees, not hidden beneath them.
Summary
| What we checked | Finding | Status |
|---|---|---|
| Passwords | Never stored. Hashed with PBKDF2-SHA256 at 500,000 iterations, each with its own random salt. | Strong |
| Messages at rest | Encrypted with AES-256-GCM before they touch a disk. A stolen database is unreadable. | Strong |
| End-to-end encryption | Not implemented, by choice — reported conversations must be readable by moderation. | Stated limit |
| Chat photos | Deleted from storage 72 hours after upload, permanently, by an automatic hourly sweep. | Strong |
| Photo access control | A photo sent in one conversation cannot be opened by anyone outside that conversation. | Strong |
| Your location | Coordinates are never sent to another member. Distance is deliberately coarsened. | Strong |
| Face verification | A live, server-authorised check. It raises the cost of impersonation; it does not make it impossible. | Stated limit |
| Moderator power | One account. It can read a reported conversation and nothing else, and every use is logged permanently. | Constrained |
| Account deletion | Immediate and real: photos removed from storage, account row deleted. | Strong |
| Tracking and advertising | No analytics SDK, no advertising SDK, no third-party tracker in the app at all. | None present |
| Abuse limits | Every expensive action is rate-limited per member — including browsing, swiping, blocking and reporting. A damper on abuse, not a hard wall. | Stated limit |
| Email address | Confirmed by a six-digit code before an account can be seen by anyone, swipe, message or post. Google accounts are already proved and skip it. | Strong |
| Signing up in a browser | Behind a Cloudflare bot check, enforced on the server. It raises the cost of scripted signup; it is not an identity check. | Stated limit |
| Who can call the API from a browser | Only jura.best. A clone of the app hosted elsewhere is refused by the browser itself. | Strong |
| Where the rules live | On the server. Every rule that matters is enforced there, whichever app or browser is asking. | Strong |
Your account
Your password is never stored anywhere, in any form you could read. It is put through PBKDF2-SHA256 500,000 times with a 16-byte random salt unique to you, and only the result is kept. Two members with the same password produce completely different stored values, and working backwards from the stored value to the password is not practical.
If you sign in with Google instead, Google confirms who you are and Jura never sees a password at all.
Your session is a signed token that expires after 60 days and carries its own unique id. Signing out revokes that specific token immediately — it stops working even though it has not expired.
Your messages
Every message body is sealed with AES-256-GCM before it is written, each with its own random nonce, and stamped with a version so the key can be rotated without losing anything. The key lives only in the server's secret store — never in the database, never in the app. If someone walked away with a complete copy of the database, they would hold ciphertext.
The encryption is proved every time the service reports its health: the system encrypts a known phrase, decrypts it again, and refuses to report itself healthy if the round trip fails.
Your photos
- Profile photos stay while your account does.
- Photos sent in chat, verification selfies and photos on public Occasions are given an expiry of 72 hours at the moment they are uploaded. An automatic sweep runs every hour, deletes the expired objects from storage, and marks the record. After that the image is gone — not hidden, not archived. Requesting it returns "this photo expired" to everyone, including us.
- Chat photos and verification selfies must be taken live in the app. The app offers no way to choose an old picture from your camera roll.
- The one selfie that earned a verification badge is kept while that badge stands, because later photos are quietly compared against it. It is deleted when the badge is removed or a newer one replaces it, and it is never shown to another member.
Who can open a photo
Private images are not simply hard-to-guess links. Every request is checked against who you are: you can open your own; a photo sent in a conversation can be opened by the person it was sent to in that conversation — not by the sender's other matches, not by a guardian who has joined that conversation, and not by anyone else on Jura.
Family in a conversation
A member may link one guardian — a parent or wali — to their account. The guardian must sign in with the exact email address they were invited at, and prove it, before they can accept. A guardian joins a particular conversation only when their member brings them in, and the other member is told in the conversation. From then on they can read and write there, but the server returns only messages sent after the moment they joined; nothing earlier is ever sent to them, and chat photos never are. Every message a guardian writes is labelled as theirs on both members' phones, and either member can ask them to leave at any time.
Your location
If you allow location access, your coordinates are stored so the app can say how far away someone is. Your coordinates are never sent to another member. What they see is a distance that has been deliberately blunted: never finer than a mile, and beyond ten miles rounded into five-mile steps.
That rounding is not cosmetic. A precise distance, read from two or three places, can be turned back into an address. A coarse one cannot. You can also turn distance off entirely in Settings, or refuse location access — the app works without it.
Face verification
Verification runs live, in the app. On most phones the camera watches while you follow a short sequence in a random order — look straight, turn, blink, smile — so a printed photo or a replayed video does not pass. The captured face is then compared with the faces already on your profile, and the badge is granted only on a positive match; every other outcome, including "unclear", grants nothing.
Two details worth knowing. First, the upload must be authorised by a ticket the server issued moments before — one ticket, one selfie, ten minutes — so a stolen access token and a command line cannot mint a badge without the app. Second, if the matching service is unreachable, verification fails closed: no badge. A checker that grants badges when it is broken is worse than no checker.
Moderation, and the power we hold
Moderation is a single named, audited role. It can see reports, ban, and remove accounts. It can read the conversation attached to a report, and only that conversation — there is no route in the software that lets anyone browse private chats at will. Every moderation action, including every time a reported conversation is opened, is written to a permanent audit log that is never edited or erased.
Deleting your account
Settings → Account → Delete my account is not a flag on a row. Your images are deleted from storage in batches, and your account record is then deleted outright — profile, photos, matches and messages with it. Records of moderation actions remain, as an audit trail; they are what makes the paragraph above verifiable.
Tracking, advertising, data selling
The app ships with no analytics SDK, no advertising SDK, and no third-party tracker of any kind. Nothing measures you for a marketing company, because nothing of that sort is installed. We do not sell data and we do not show ads.
The permissions the app asks for are short and each has an obvious purpose: internet, camera (photos and verification), and location (distance, optional). Microphone access is explicitly removed from the app at build time — Jura cannot record audio even by accident.
Where it runs
Jura runs on Cloudflare: the API on Workers, profiles and messages in D1, images in R2, live chat on Durable Objects. Face matching runs on a private Jura service that images pass through for comparison and which does not retain them; it is reached over a short-lived signed link, never with your session. Transport is HTTPS throughout.
Abuse limits
Every expensive or abusable action is capped per member, per window — verification attempts, photo uploads, messages, swipes, profile cards opened, deck pages, blocks, reports, Occasions posts, searches, and more. The ceilings run from a handful an hour for the costly ones to a few hundred for ordinary browsing, chosen so that no member using Jura normally will ever meet one. Registration, sign-in and password reset are additionally capped per IP address as well as per account.
Reports are capped for a reason worth naming: they are read by a person, so flooding the queue is itself an attack — it buries the real ones.
Confirming your email
An account created with a password sends a six-digit code to the address given. Until that code is entered the account can sign in, build a profile and look around — and can be seen by nobody. It cannot swipe, message, open a conversation, post an Occasion or comment. The line is drawn where an account starts reaching other people, because that is the only thing an automated signup wants.
Codes are stored hashed, expire in thirty minutes, and allow six attempts. Signing in with Google skips this: Google's token already proves the address, and asking twice would be ceremony rather than security.
Using Jura in a browser
Everything above applies to the web app at jura.best exactly as it does to the phone apps — the rules live on the server, and the server does not care which one is asking. Three things are specific to the browser:
- The page cannot be framed by another site, cannot be sniffed into running as a script, and asks for the camera and your location only for itself. Those are set as response headers and are visible in any browser's network tab.
- Signing up in a browser passes a Cloudflare bot check first.
- Face verification runs the same turn-blink-smile challenge in the browser that it runs on a phone, using the camera live. If the camera will not open, verification stops there rather than falling back to choosing a file — a picture from your gallery is refused on the web for the same reason it is refused on a phone.
Testing
The backend ships with an automated test suite — 81 tests across 4 suites, all passing — covering the cryptography, the encryption round trip, liveness tickets, face verdict handling, distance maths, request handling and Occasions rules. The code also type-checks clean before every deploy. Tests are not proof of security; they are proof that the parts described on this page behave as described, every time they change.
What this page is not
This is a first-party audit: our own review of our own code, published so you can hold us to it. It has not been carried out by an outside security firm, and we are not going to imply otherwise. If that changes, this page will say so, with a date and a name.
Check some of it yourself
- The app's requested permissions are listed on its store page. Compare them with the short list above.
- The service reports its own health, including a live self-test of message encryption, at /v1/health.
- Send yourself a photo in a chat and try to open its link 72 hours later. It will be gone.
- Delete a test account, then try to sign in to it.
Found a hole?
Tell us before you tell anyone else and we will work with you, thank you publicly if you want that, and fix it as fast as we can: support@jura.best. We will not threaten anyone who reports a vulnerability in good faith.