Privacy Policy
Last updated: 15 October 2026
In one sentence
ShadeMirror works offline and uploads none of your data.
Try-on, rendering, face detection and saving all happen on your phone and need no network. Your photos, your saved looks and the products you add exist only on your own phone — the app never sends them anywhere.
Three places where the network is used, none of them involving your content
Three things below do produce network traffic. None of them carries your photos, your saved looks, or anything that points to you personally. We write each one down here rather than burying it in the fine print.
1. In-app purchases. Looking up the subscription price, buying and restoring are handled by the operating system's payment component (StoreKit on iOS, Google Play Billing on Android). That step needs the network, and it is the system that talks to Apple's or Google's servers. We neither initiate it nor see any of the data involved.
- iOS: StoreKit runs inside the app process, which is why the system may ask
you "Allow ShadeMirror to use wireless data?". That prompt comes from Apple's payment component, not from our code. Declining it does not affect any part of the try-on; it only prevents subscription actions from completing.
- Android: the installed package does not contain the
INTERNET
permission (it is explicitly stripped with tools:node="remove" in the manifest). Billing goes through inter-process communication to the Google Play Store app, and that app is the one that reaches the network. On Android the app process has no networking capability at all.
2. System backup. If you have iCloud Backup (iOS) or cloud backup (Android) switched on, the operating system backs this app's data — including the photos you have saved — to your own cloud account, and restores it when you move to a new phone. That is the operating system acting on your own settings, not the app, and the data goes to your account. We still cannot see it or reach it.
We deliberately let this app's data take part in system backup — otherwise everything you have built up would be lost the moment you change phones. If you would rather it did not, you can turn backup off for this app in system settings.
3. iOS only: usage statistics from the face-detection library. We use Google's MediaPipe for face-landmark detection (the detection itself runs entirely on your device). That library ships with a piece of Google's statistics-reporting code that we cannot remove: on iOS it sends one request of about 200 bytes to play.googleapis.com roughly a minute after the app starts. The contents are the library's own operating statistics — which model was used, how many calls, how long they took, whether anything failed — plus the device model and OS version.
It cannot send your photos, and it cannot send anything that identifies you — the message format it uses has no fields for either. But it does happen, so we do not claim "zero network requests". The full measured evidence is in *About the telemetry code MediaPipe brings with it* below.
Android is not affected: with no INTERNET permission in the package, the same code cannot send anything at the operating-system level.
Why this is more than a promise
Most privacy policies open with “we care about your privacy” and then list what they collect. Ours is a different situation, and it is one you can verify:
The Android manifest does not declare the INTERNET permission.
On Android, an app without that permission cannot reach the network at the operating-system level. This is not “we promise not to send anything”; it is “it cannot”. Anyone can download the package, unpack the manifest and confirm it for themselves.
iOS has no equivalent permission mechanism. So on iOS we do not say "it cannot"; we say "we have never written a line of networking code" — which you can also verify:
grep -rn "URLSession\|CFNetwork\|NWConnection\|dataTask" ios/Sources/ # no hits
There is real network traffic on iOS, and it comes from two pieces of third-party code: Apple's own StoreKit (subscriptions), and the statistics reporting inside MediaPipe that we cannot strip out. Both are described above under *Three places where the network is used* and below under *About the telemetry code MediaPipe brings with it* — not buried in the details.
The permissions we ask for, and why
Camera
What for: showing the makeup on your face in real time.
The camera feed is processed entirely on your device: face-landmark detection (MediaPipe, running locally) and makeup rendering (your device's GPU). It never leaves the phone. Nothing is recorded or saved unless you press the shutter yourself.
Add to photo library (add-only)
What for: writing the pictures you export into the system photo library.
On iOS this is the “add-only” permission — it allows writing and forbids reading. We cannot see any other photo in your library.
Android 10 and above needs no permission at all. Android 9 and below requires the storage permission because of an OS limitation, and the manifest restricts it to exactly those older versions (maxSdkVersion="28").
The permissions we do not ask for
| Permission | Why we don't need it |
|---|---|
| Read photo library | Importing goes through the system picker (PHPicker on iOS, Photo Picker on Android) — it runs in the system process, and we receive only the picture you selected |
| Network | See above |
| Location, contacts, microphone, device identifiers | Irrelevant to what this app does |
Face data
Face data is sensitive, so it gets its own section: what is detected, what is stored, where it is kept, how long it is retained, and who else sees it.
We do not do face recognition
What the app needs to know is where each part of the face sits in the frame — where the eyes are, where the lip line runs, where the cheekbone is — so it can decide where each layer of makeup goes.
It builds no face template capable of identifying anyone, and performs no matching, authentication, search or clustering. The data answers "where are the lips in this frame", not "who is this".
What is detected, and when anything is written to disk
During live try-on, each camera frame is passed to the MediaPipe face-landmark model running on your device (face_landmarker.task, shipped inside the app bundle, no network access at runtime), which returns a set of 2-D landmark coordinates. Those frames and coordinates exist only in memory and are discarded immediately — nothing is written to a file, nothing is uploaded.
Only when you press the shutter yourself to save a look is anything written to disk. Three things are written:
| What is written | Why it is needed |
|---|---|
| The rendered image | The finished picture you chose to keep |
| The original frame (no makeup) | So that changing a shade or intensity later can be re-rendered on it |
| The face-landmark coordinates | So re-rendering does not have to run detection again, and does not drift slightly each time |
Where it is kept
In the app sandbox on your device, under Application Support/StyleMe/. There is no server, no account, and no copy has ever left the device.
If you have iCloud Backup (or Android cloud backup) turned on, these files are backed up along with the rest of the app's data. This is deliberate: not backing them up would mean losing everything you have built up when you change phones. The backup goes to your own cloud account — we cannot see it or reach it.
How long it is retained
You decide; we set no expiry. Delete a look and its three files are deleted immediately. Uninstalling the app deletes everything in its sandbox; copies you exported to your photo library or Files are managed by those system apps. There is no "retained for N days" policy, because we never hold this data in the first place — it stays on your device.
Whether it is shared with third parties
No. We have no server to upload it to, and we have not integrated any analytics, attribution or advertising component.
The only third-party component involved is MediaPipe (Google's open-source library). It runs on your device; it neither receives nor sends face data. The telemetry it brings with it (described above under "Three places where the network is used", item 3) reports only how many times the model was called, how long it took, the device model and the OS version — no field in that payload can hold an image or a coordinate.
Where your data lives
| Data | Where it is kept | Who can see it |
|---|---|---|
| Looks you save (rendered image + original frame + face-landmark coordinates) | The app sandbox on your device | Only you |
| Products you add (brand, name, colour) | The app sandbox on your device | Only you |
| Interface language, whether you've seen a tip | Local preferences on your device | Only you |
Uninstalling the app deletes everything in its sandbox. Copies you exported to your photo library or Files are managed by those system apps. We have no server, so there is no “account data” to export or erase — because there was never an account.
Third parties
We have not added any advertising, telemetry or analytics component.
The only third-party components the app uses are:
- MediaPipe (open source, Google): face-landmark detection, running
entirely on the device
- AndroidX / CameraX (Google's official Android libraries): wrappers around
system capabilities
About the telemetry code MediaPipe brings with it
Here is one thing we cannot fully remove, stated plainly.
MediaPipe brings Google's telemetry upload code with it. The two platforms carry different copies:
- Android:
com.google.android.datatransport:transport-backend-cct - iOS:
ClearcutLoggingClient+GTMSessionFetcher, compiled into
MediaPipeTasksCommon — not listed as a dependency anywhere
We tried to exclude the Android one, but MediaPipe references it directly in code — with it removed, face detection will not start at all. The iOS one is inside a closed-source static library; there is not even a way to try.
On Android we have made it unable to function. On iOS we cannot. We state each side as it is.
- Android: blocked. Its two permissions —
INTERNETand
ACCESS_NETWORK_STATE — and its three scheduling components have all been removed from the app manifest. Without the network permission it cannot open a connection at the operating-system level, and with no scheduling components there is nothing to wake it. You can verify this yourself; see the appendix.
- iOS: not blocked — it does send. Measured on 16 September 2026: the
Release build sends one request to play.googleapis.com about 60 seconds after launch (234 bytes out, 393 bytes back, HTTP 200), reproduced on three consecutive launches. The sender is Google's ClearcutLoggingClient inside the MediaPipe static library, using its bundled GTMSessionFetcher.
This is not Apple's payment component. We commented out the single StoreKit call in our own code, rebuilt, and the request still went out — so it cannot be attributed to Apple.
What it sends, and what it does not:
The message format (logs.proto.mediapipe.MediaPipeLogExtension) has only these kinds of fields: session start/end, invocation counts and durations, error codes, which model and running mode were used, and the device model and OS version.
No field in it can hold an image, a landmark coordinate, or your content — and the whole request is 234 bytes. It carries no account, no advertising identifier, nothing that points to you. We cannot read any of it either: the recipient is Google, not us. We have no server, and no Google analytics account where those numbers could be viewed.
We do not promise what we cannot deliver. The library is a closed-source static archive with no official off switch (it contains an empty TasksDummyLogger, but nothing exposes a way to select it), and our MediaPipe version is deliberately pinned — changing it means re-validating the whole face detection and camera path on real devices, which is a separate matter. So on iOS we do not claim "zero network requests".
We write all of this out because this policy asks you to verify it yourself — when you unpack the package you will find those classes, and if you capture traffic you will see that request. Better that you hear it from us than find it and start wondering what else reads like this. The appendix tells you how to reproduce it.
Payment
Purchases are completed through the App Store or Google Play. Payment details are handled by Apple or Google; we never see your payment method, card number or billing address. All we receive from the system is a single yes-or-no answer: whether this device has unlocked the full version.
Children
This app is not directed at children under 13, and it collects no age information.
Changes
If we ever introduce a networked feature (for example the product catalogue planned for a later release), we will:
- Say so prominently inside the app,
and leave it switched off until you agree to it
- Update this policy and note the revision date
We will not begin collecting data without your knowledge.
Contact
developer@metaimax.com
Appendix: how to verify the claims above
For reviewers, or anyone who would rather check for themselves.
Verify the Android build can send nothing at all:
# Unpack the build and list the permissions the manifest declares
aapt2 dump permissions app-release.apk
# Should list only CAMERA, WRITE_EXTERNAL_STORAGE (maxSdkVersion=28),
# BILLING (inter-process, not a network permission) and one app-private
# permission. There is no android.permission.INTERNET — so no code in the
# package can open a connection, the telemetry library included.
# You can also confirm its scheduling components are absent:
aapt2 dump xmltree app-release.apk --file AndroidManifest.xml | grep datatransport
# should print nothing
Verify our own code does no networking:
grep -rn "URLSession\|CFNetwork\|NWConnection\|dataTask" ios/Sources/ StyleMeCore/Sources/
grep -rn "OkHttp\|HttpURLConnection\|java.net.Socket\|Retrofit" android/app/src/main/java/
Verify the two pieces of third-party networking on iOS:
# 1. Which network-capable system frameworks the binary links
otool -L StyleMeProto.app/StyleMeProto | grep -iE "CFNetwork|Network|StoreKit"
# You will see StoreKit.framework — Apple's payment component, as described
# above. No CFNetwork, no third-party networking framework, no web view.
# 2. The reporting address inside MediaPipe (statically linked, so it does not
# appear in the framework list)
strings -a StyleMeProto.app/StyleMeProto | grep play.googleapis.com
# You will see https://play.googleapis.com/log — the request described above.
⚠️ The second command does produce output, and that is expected — we are not hiding it. It comes from the static library under Pods/MediaPipeTasksCommon/frameworks/graph_libraries/, whose symbols include mediapipe::tasks::core::logging::google_internal::ClearcutLoggingClient.
To reproduce the request: install the Release build on an iOS Simulator and watch for about 60 seconds with log stream --predicate 'process == "StyleMeProto"'. You will see hostname: play.googleapis.com and taskName = GTMSessionFetcher. That is the method we used ourselves, and where these numbers come from.
Verify the third-party dependency list: read android/app/build.gradle.kts and ios/Podfile. Both contain only MediaPipe and official system libraries.
Verify how the photo library is reached: search for PHPickerViewController (iOS) and ActivityResultContracts.PickVisualMedia (Android). Both are system pickers and require no photo-library permission.