← All posts
Company

Why our privacy policy lives on our website, not in our app

By JDGX Labs · 12 August 2026 · 7 min read

WallMind shipped with its privacy policy written directly into the app — a Compose screen with the text laid out in Kotlin. It looked tidy. It was a mistake.

The problem with two copies

Google Play requires a publicly reachable privacy policy URL for your store listing. So the policy existed on our website too. That is two copies of one document, and two copies of a document whose only job is to be accurate.

They drift. Not through carelessness — through ordinary work. Someone corrects a sentence on the website because a reviewer asked. Nobody remembers the same sentence is compiled into an APK that shipped three weeks ago. Now the app and the website disagree about what happens to a user's data, and the app's version is the one that cannot be fixed without a release.

That last part is the real cost. Correcting a typo in the in-app policy required a full Play Store release — build, upload, review, rollout. Days, for a comma.

What we changed

The app no longer contains the policy at all. Settings → About → Privacy policy opens a WebView pointing at our site. The website is the single authoritative copy.

Play is satisfied by this: the requirement is a privacy policy link within the app, not the text rendered inside it.

One document. One place to fix it. A correction is live the moment we deploy.

The parts that are easy to get wrong

Opening a web page inside an app is not quite the same as opening it in a browser, and we got a list of constraints from the app side that shaped how the page is built:

We also removed the advertising script from those pages. A privacy policy that loads an ad script is an easy complaint to make and a hard one to defend, even when the script is switched off.

And the font is self-hosted rather than loaded from Google Fonts. A page explaining what we do with someone's data should not hand their IP address to a third party while they read it.

The bug this found

Here is the part worth the price of admission.

Once the website owned the policy, we asked the app side to check every factual claim in it against the actual implementation. Not to rewrite anything — just to confirm the words were true.

The deletion page said that deleting your account removes your records, your settings, and the photos from your device. The comparison found that deletion was leaving the full-resolution photos and three preference files on the device — including the user's own API key.

The page was not wrong about what should happen. It was wrong about what did happen. Nobody would have noticed by reading the code, because nothing looked broken. It surfaced because someone read a sentence and went looking for the line that made it true.

That is a reason to write policies specifically rather than vaguely. A vague policy — "we may retain certain data" — cannot be checked against anything. A specific one can, and occasionally the check fails.

What we lost

The app used to have a test that failed the build if the policy stopped mentioning somewhere data actually goes. That guard died with the in-app text, and nothing on the website can replace it.

So it became a human check, written into our project notes: the policy must name every destination user data reaches. For BillNest that is currently four — Cloud Firestore, the user's own Google Drive, Firebase Crashlytics, and Wikimedia Commons. If a fifth appears, the policy is updated before that build ships.

Worse than an automated check. Better than nothing, and better than a policy that quietly goes stale.

If you are deciding this for your own app

Put the text on the web and link to it. Keep exactly one copy. Make the page work inside a WebView — theme, background, no storage. And once a year, read your own policy and go looking for the code that makes each sentence true.

← Back to the blog