Security audit

How Jura protects you,
checked line by line.

17 September 2026 · Jura app 2.15.11 (build 117), the web app at jura.best, and the Jura API · a review of the code that actually ships, not of a plan

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.

Download as PDF 9 pages · A4 · 255 KB

Summary

What we checkedFindingStatus
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.

The limit, plainly: this is encryption at rest, not end-to-end encryption. The server holds the key, so the server can decrypt. That is a deliberate trade: on a platform where people meet strangers, a report of harassment has to be readable by a human, or the report is worthless. If you want a channel nobody but you and one other person can ever read, Jura is not that channel and does not pretend to be.

Your photos

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.

The limit, plainly: nothing can stop a person who receives a photo from screenshotting it. The 72-hour rule governs Jura's storage, not someone else's phone.

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.

The limit, plainly: no automated check can prove identity absolutely. Treat the badge as strong assurance that the person in those photos took a live check — not as absolute proof of identity. And when a new profile photo does not match the verified face, Jura does not block it; unreliable matching on ordinary photographs (sunglasses, a side profile, an old picture) would punish honest members far more often than it would catch a dishonest one. It goes to a person in the moderation queue instead, and you are told it happened.

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.

The exact figures are the one thing on this page we do not print. Publishing them would save an abuser the trouble of finding the edge, and they are tuned as abuse patterns change — a number here would be stale as often as it was right.

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.

The limit, plainly: these counters are a cost-and-abuse damper, not a security boundary. They exist to stop loops and expense, and the code says so in as many words.

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.

The limit, plainly: a confirmed address is not an identity. A throwaway inbox costs a minute. What it costs is a minute per account — and it gives moderation something durable to act on when an account misbehaves.

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 limit, plainly: no on-screen challenge is perfect on its own. What stands behind it is the server: the selfie is matched against your profile photos there, and that match fails closed.

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

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.

This page is reviewed whenever the security-relevant parts of Jura change, and the date at the top is updated when it is. Related reading: Privacy · Terms · Delete your account.