privacy policy
Privacy Policy
Effective: April 7, 2026 · Last updated: September 29, 2026
TL;DR
PFFT keeps your data on your phone. What your phone sends us is a request for the map area around you (Section 6). Your workouts, GPS routes, health metrics, and personal information are never sent to us or to anyone else — there is no PFFT server that receives your workout data, no account system, no analytics, no telemetry. (The PFFT-run infrastructure that does exist: the waitlist form on this website, which passes the email address you typed in yourself — and your platform choice, kept only as which list you join — to a mailing-list server we also run ourselves, with no third-party provider involved — Section 7 — and the map host we operate on Cloudflare's network that serves the map areas the app draws — Cloudflare carries those requests and sees them — Section 6. The app never talks to the waitlist form.) Without you doing anything, the copies of your workout data that exist are on your phone (encrypted with keys your device's secure hardware controls) and, if you have device backup enabled, in your own Google account backup, which we never see. Copies you take out yourself are a different matter and get their own section: seven routes carry workout data out of the app — four exports you start with a tap, Health Connect and the watch link once you set those up, and one plain-text share — and where a copy goes after it leaves is your choice, and beyond our reach (Section 10.1). The app does fetch a few resources from the network (the map areas it draws, from a host we operate) — every one is listed in Section 6, with what it sends and how to turn it off. Section 6 also lists the weather chip, which is switched off at the feature level and fetches nothing in current builds. The map downloads are addressed by which square of the world you are in, which makes them an approximate-location signal; Section 6 says so out loud rather than rounding it to zero.
1. Who We Are
PFFT ("Private Fucking Fitness Tracker") is developed and published by Niotenia LLC ("we," "us," "our"). PFFT is a fitness tracking application for Android. It is not published yet — there is no Google Play listing you can install from today, and no iOS release (no App Store listing, no TestFlight). This policy is published in advance so it can be read before the app is. Our website is pfft.app.
For privacy questions: privacy@pfft.app
2. Our Core Principle
Your data belongs to you. Period.
PFFT is designed from the ground up so that we never have access to your personal data. This is not a marketing claim — it is an architectural decision enforced by code. The app has no server component, no cloud sync, no user accounts, and no network call that transmits your workout or health data or uploads a GPS fix. One of the calls in Section 6 downloads map areas, and asking for the square of the world you are in is an approximate-location signal — we say so there rather than rounding it to zero.
You don't have to take our word for it. Our build carries a network canary test that statically scans the app's own JavaScript and pins the calls we write to the short list in Section 6 — adding one there breaks the build. It reads our own code, not a native library at runtime. So check it from the outside: a researcher with a proxy and the APK should find Section 6 complete, including the map traffic it now names — requests to tiles.niotenia.com and to no other map host, and no map request at all once maps are switched off — that switch governs the map and nothing else, so the other calls in Section 6 are unaffected and each has its own row and control there. If you find otherwise, that's a bug — tell us at privacy@pfft.app.
3. Data We Do NOT Collect
PFFT does not collect, sell, share, or store on any server any of the following:
- GPS coordinates or workout routes
- Fitness data (distance, speed, duration, calories, elevation)
- Health data (weight, active minutes, heart rate)
- Location data — your position, your movement patterns, the places you visit. (The map area your phone asks for is a square of the world, not a position; Section 6.)
- Device identifiers (IMEI, advertising ID, hardware ID)
- Usage analytics or behavioral data
- Crash reports or diagnostics — nothing is sent to us; a crash is logged on your phone (Section 4.4)
- Contacts, photos, files, or any other personal information (saving a route image writes one picture out to your photo library, with your permission — Section 10.1)
- Your name, email address, or any identity information
4. Data Stored on Your Device
PFFT stores your data on your device: workout records as encrypted files in the app's private storage (with the encryption key protected by Android's hardware-backed keystore, via expo-secure-store) and non-sensitive settings in local storage (AsyncStorage). This data includes:
4.1 Workout Data
GPS coordinates, distance, duration, speed, calories burned, elevation, and route data for each workout. Each workout record is stored as an AES-256 encrypted file on your device. This data is never transmitted to us or to any third party; the only copies outside your phone are your own device backup (Section 6.1), if you have it enabled, and any copy you export or share yourself (Section 10.1).
4.2 App Settings
Your preferences: units (metric/imperial), theme, auto-pause settings, language, activity order. Stored in local AsyncStorage on your device.
4.3 Health Data
If you enter your weight (optional), it is stored in encrypted secure storage on your device. If you enable Health Connect integration (optional, disabled by default), PFFT writes your completed workouts to Android Health Connect so other apps you authorize can see them — it holds write-only permissions and reads nothing back. This happens entirely on your device; nothing is transmitted.
Two facts about that sync belong here rather than in fine print. First, once the toggle is on it runs automatically at save time — there is no per-workout prompt, and it keeps happening until you switch it off. Each save writes an exercise session (activity type, start and end time, and a title naming PFFT and the activity) plus, when the workout has them, total distance, total calories and elevation gain: session statistics only, never your GPS route, never a coordinate. Second, those records live in your phone's health store, outside PFFT's sandbox. "Delete All Data" now asks Health Connect to delete them — the records PFFT itself wrote, and only those — and tells you if the platform refuses. Two cases it cannot reach: records written under a permission you have since revoked, and anything at all once you uninstall, because the app is gone before it could ask. In either case, delete PFFT's data inside Health Connect itself. Switching the sync off, or revoking the permission, stops future writes; it does not withdraw the ones already made.
4.4 Crash Logs
If the app crashes, a local crash report is saved on your device. These logs contain error messages and stack traces — no personal data, no GPS coordinates, no workout details. The app keeps at most the 20 most recent entries (older ones are overwritten). They are never sent to us. PFFT runs no crash-reporting service — no Sentry, no Bugsnag, no telemetry — and no code in the app uploads them; sharing one is a manual export you perform yourself. Being precise about where they sit, though: they live in the app's local storage, which is inside your phone-backup scope, so with device backup on a copy rides your own Google account backup along with the rest of your PFFT data (Section 6.1). You can view and delete them in Settings.
4.5 Debug Logs
During active workouts, PFFT may write GPS accuracy and motion data to temporary log files on your device for debugging purposes. These files live in the app's cache directory — a transient location the OS is free to clear, which is excluded from your device backup — and are deleted when you use "Delete All Data" in Settings or uninstall the app.
4.6 Keep-awake during a Blind-First workout
- Why: so the clock and the spoken cues stay on time with your screen off and the phone in your pocket, during a Blind-First workout.
- When used: from the Blind-First count-in until the workout ends. It lets go while you're paused, within half a minute after you finish (and again within half a minute if you retry a save that failed), about 15 seconds after you cancel the count-in, and it is never held more than two minutes if the app stops running.
- What it does with your data: nothing. It keeps the phone's processor awake; it collects, stores and sends nothing.
- Battery: it uses some extra battery while it does.
- It isn't a new permission: Android already listed it for this app (it comes with the media player library). PFFT now uses it on purpose.
5. Encryption
Every saved workout record is encrypted using AES-256-CBC with HMAC-SHA256 authentication (encrypt-then-MAC) under a randomly generated vault key. The vault key is protected by Android's hardware-backed keystore (Keymaster/StrongBox where available). Biometric authentication (fingerprint or face) can be required to access the app.
Two things are deliberately not in that vault, and we would rather name them than let "all workout data" quietly cover them. While a workout is actively recording, the in-flight route (raw GPS coordinates and laps) is written unencrypted to an app-private file that is excluded from your phone's backup and from device-to-device transfer, so an OS force-kill mid-workout cannot destroy your pre-crash route; it is deleted the moment the workout's save is durable. And the index of workout IDs, your settings and your art-library entries live in plain local storage (AsyncStorage) — no GPS, no route, no coordinate.
Encrypted backups (the .pfft file) use PBKDF2-SHA256 with 200,000 iterations, a random salt, and a random IV. The backup password is never stored — only you know it. The file also opens with a recovery key generated at your first backup and never stored by the app. The bulk "export everything" ZIP is a different file and is not encrypted at all — Section 10.1.
6. Network Connections — the complete ledger
PFFT makes no network calls that transmit your workout data. This section is the complete list of every network call the app can make — what triggers it, what it sends, and how to turn it off. The weather chip is listed there too, and it is switched off at the feature level: it makes no call at all in current builds. Not every call here is signal-free, and rather than bury that in a footnote or reduce it to a count, we will name it: PFFT downloads map areas from our own map host, and asking for the square of the world you are in is an approximate-location signal. Its row below spells out exactly what that means.
this row last changed: 2026-09-23
Map rendering (PFFT map tiles — tiles.niotenia.com): the map is drawn on your phone by MapLibre, an open-source renderer, from map files PFFT downloads and keeps on the device. A low-zoom world map ships inside the app and needs no network at all. Detail arrives as whole pieces of the world — cells up to about 111 km on a side (one degree by one degree), plus smaller metro and county pieces, the label fonts and the map style — each a static file fetched over HTTPS from
tiles.niotenia.com, a host we operate on Cloudflare's network: Cloudflare carries the request and sees it, and no server code of ours is in that path. PFFT downloads the area around you shortly after the app starts, and again the moment you grant location on the map welcome — it does not wait for a map to be on screen. With a map up it also fetches ground you move into and do not already hold: on unmetered wi-fi without asking, on mobile data only after the strip asks you first. On unmetered wi-fi it fetches the cells next to you ahead of need as well. Opening a past workout on unmetered wi-fi can also download the part of that workout's area the phone does not hold, without asking, when that part is 75 MB or less — and that request tells the host roughly where that workout took place. Every byte is checked against a catalogue we sign, and from then on that area draws fully offline. State it plainly: this is an approximate-location signal. A request for a cell tells that edge which square of the world your phone asked for, when, from which IP address — as any web server sees — and the generic network-library string Android attaches to the request, which names neither PFFT nor its version. It carries no workout history, no recorded route, no GPS fix, no cookie, no account, and no identifier we generate. And one thing here is visible to more than that edge: the NAMEtiles.niotenia.comtravels in the clear. TLS carries the server name in its opening handshake, so anyone on the network path — the wi-fi you are using, your mobile carrier — can see that your phone contacted PFFT’s map host. What they cannot see is anything inside the session: which square of the world you asked for, the file names, the headers. That is ordinary HTTPS rather than a choice of ours — nothing in the app turns on the extension that would hide the name, and the HTTP client we are handed does not offer one. Your position — or, for a past workout, its recorded route — is used on the device to choose the cell and never leaves it. It is coarser than the per-viewport requests the Google Maps SDK made in earlier builds, and it happens per area rather than continuously — but it is real. What happens to that record on our side: nothing. We keep no log of these requests, we have set up no export of per-request records to ourselves, and the storage bucket’s direct public address is switched off, so the files are reachable only through the provider’s edge. The network provider that serves the files keeps its own operational and aggregate traffic records under its own published retention. We never view, export, copy or keep the per-request rows. The only thing we do look at is the provider’s aggregate daily totals — request counts and bytes served — which carry no IP address and no per-request detail. The off-switches: settings → map → "show maps" turns the map system off entirely and downloads nothing; "wi-fi only for downloads" holds the AUTOMATIC area download back until you are on wi-fi — be precise about its reach, because the switch does not cover everything: an area you ask for while a map is open is offered to you first and downloads only if you accept, over whatever connection you are on at that moment; "fetch nearby areas on wi-fi" turns the ahead-of-need fetch off on its own; and an area already on the phone is not fetched again unless a newer one is published. PFFT no longer contains the Google Maps SDK or a Google Maps API key, and no map request reaches Google.- Weather (world weather — switched off at the feature level; makes no call in current builds): the Home weather chip is not enabled in the builds we ship, so nothing is sent to Open-Meteo and no coordinate of any kind leaves the device for weather. There is still a show world weather row in Settings; while the feature is off, that row governs nothing. If the feature ships, this is the shape it would ship in: a one-line forecast for a random world city — never your location (the app reads no GPS or location permission for weather) — sending that random city's coordinates to Open-Meteo's free public endpoint, with no API key, no account and no identifier. This entry moves to the present tense on the day that becomes true, and not before.
- Background art — disabled in current builds: artwork prefetch is switched off at the feature level; the app makes no art-related network call at all. When the art-reveal feature ships, its prefetch (signed, content-hash-verified artwork manifests from a PFFT-operated CDN — no workout data, identifier, cookie, or auth header) will be disclosed here and toggleable in Settings.
- Strava import — no network call: importing a Strava archive reads a file you picked. The archive download happens in your own browser, on strava.com — the app itself never contacts Strava.
The PFFT app contains zero analytics SDKs, zero tracking pixels, zero telemetry endpoints, and zero Firebase or similar services. A repository-level test (the network canary) statically scans the app's own JavaScript and fails the build if a request or host literal appears there outside this list. Its reach, stated rather than implied: it reads calls we write in our own code. The map is now inside that reach rather than outside it — the map host appears exactly once in our own JavaScript, and the canary checks that one appearance against its allowlist; the Google Maps SDK row this replaced was the opposite case, traffic a bundled native library originated by itself, which the canary could not see. What a source scan still cannot do is watch a native library at runtime, so here is what stands in its place rather than a promise: every address we hand the renderer is a path on your phone — the map files and the label fonts by their on-device paths, and no icon sheet at all — and a separate build-time guard fails if any of them names a host. The site serves its font from pfft.app itself rather than from Google Fonts.
6.1 Your device backup
Your PFFT data is included in your phone's standard backup to your own Google account. That is the OS-level backup you control in your phone's settings — your data, in your cloud; we never see it, never receive it, never sell it, and PFFT has no server in the path. If you'd rather PFFT not be in your backup, exclude it in your device's backup settings. Debug logs (which can contain raw GPS while recording) are stored in a cache location that is excluded from the backup, and the in-flight route file described in Section 5 is excluded from it too. That backup copy survives an uninstall: it sits in your own Google account (Section 10.5).
What a restore can and cannot bring back — the part that matters, and we would rather you knew it now than on your new phone. Your settings, your workout index and your art-library entries are ordinary data and come back normally. Your saved workouts are different: they are encrypted, and by default the only key that opens them lives in your phone's hardware keystore, which is deliberately excluded from the backup — a key copied to the cloud beside the data it locks is protecting nothing. So the encrypted workouts do ride the backup onto a new phone, but the key does not, and PFFT tells you it cannot open them rather than pretending otherwise. Your body weight is a third case: it is kept in the same hardware-backed store as the vault key and is left out of the backup for the same reason, so it does not come back and you will need to enter it again. Calorie figures on workouts you already saved are unaffected — each was worked out and stored at save time — but any workout you record before re-entering your weight is estimated against a default body. If you want your history to move to a new phone, set a backup password before you need it — that adds a second, portable wrap of the same key, and that one travels. It is opt-in, and it is the only thing that makes your history portable.
The backup has a size cap, and it is Android's, not ours. Android gives each app 25 MB of cloud backup. PFFT's workout vault gets past that after a few dozen GPS workouts. Once it does, Android stops backing PFFT up to the cloud — quietly, with no warning — and your cloud copy stays at the last one that fit. Moving to a new phone by direct phone-to-phone transfer has no such cap. If you want your history to survive a lost phone, make a .pfft backup (Section 10.1): it has no size limit and opens on any phone with your backup password.
7. Waitlist & Newsletter
If you voluntarily sign up for our waitlist at pfft.app, your email address is sent — via a Cloudflare Worker — to our own mailing-list server: self-hosted Listmonk, on hardware we operate. No third-party newsletter provider is involved: the list lives only on our own server. The emails themselves are delivered through our mail host, Purelymail — the same carrier that handles all @pfft.app mail — which, like any mail carrier, sees recipient addresses in transit. The platform preference you pick selects which announcement list(s) you join — Android, iOS, or both. That is all it does: it is kept only as your list membership on our own server, and it appears in no log. You are not on the list until you confirm from your own inbox (double opt-in). We store your email for the sole purpose of sending launch notifications and use it for nothing else. The emails we send contain no tracking pixels and no per-recipient tracking links. We keep the waitlist only until the launch notification has been sent or you ask to be removed, whichever comes first — at that point we delete your address from the list. You can unsubscribe at any time via the one-click link in any email.
8. Third-Party Services
PFFT integrates with the following third-party services, none of which receive your workout or personal data:
- MapLibre (maps): an open-source renderer bundled with the app. It draws the map on your phone from files already on the device; it is a library, not a service, so nothing is sent to it and nothing is transmitted through it. The files themselves come from a host we operate on Cloudflare's network — Cloudflare is a third party in that request path and sees the request, and Section 6 says exactly what that edge can see.
- Open-Meteo (world weather): receives nothing in current builds — the weather chip is switched off at the feature level and makes no call. If the feature ships it would receive a random world city's coordinates — never yours (the app reads no GPS for weather) — with no API key, no account and no identifier.
- Android Health Connect (optional, on-device, automatic once you switch it on): PFFT writes completed workouts to Health Connect if you explicitly grant permission — write-only; it reads nothing back. Nothing is uploaded, and once the toggle is on there is no per-workout prompt (Section 4.3).
PFFT has no billing integration and no paid subscription — there is nothing to buy inside the app, and no payment data flows anywhere.
9. Children's Privacy
PFFT is not directed at children under 13 (or under 16 in the EEA). We do not knowingly collect personal information from children. The app asks nobody for any; the website's waitlist takes only the email address a person types in themselves, and our map host sees a map-area request and an IP address, nothing more.
10. Data Retention, Export & Deletion
All workout and app data is stored on your device, and you control it. Being precise about the word "all", because it has carve-outs: your waitlist email lives on our own mailing-list server (Section 7); copies you export or share yourself go wherever you send them; workouts you sync to Health Connect sit in your phone's health store; and, with device backup on, a copy of your PFFT data sits in your own Google account. The sections below say what each of those means.
10.1 Exporting your data — the seven ways out
You can export your data. Seven routes take copies of it out of the app: four exports you start with a tap, two channels that run on their own once you set them up, and one small text share. The four tap-started exports are built by code that makes no network request — a file leaves through the Android share sheet to an app you choose, or, for the route image, into your photo library. One caveat on that, because it could be read too generously: the route image is a picture of the map, and that map was drawn from the area files disclosed in Section 6 — an approximate-location signal — so the capture itself makes no request, but the picture only exists because those downloads happened. Where a copy goes after it leaves is your choice, and beyond our reach.
- One workout as a file. Every workout's share menu has "export file", which produces a single file in your chosen format — GPX, TCX, CSV or FIT. You pick the format in Settings → "sync & export"; the default is GPX, and the button names the format it will produce. File names follow
pfft-<date>-<activity>-<short id>.<format>— the date and activity type are in the name itself, so even a folder listing reveals when and what you trained (the id part is a short hash, not the raw internal id). - Everything at once. "export everything", in that same room, builds one ZIP: one file per workout in your chosen format, plus a
manifest.jsonand aREADME.txt. The ZIP is not encrypted and not password-protected — its own manifest says so in plain text. And the manifest itself lists every workout's id, date, type, distance and duration, even when the per-workout format you chose carries none of that. - An encrypted backup (
.pfft). "create encrypted backup", in that room's "backup & restore" section, writes your workouts, settings and body-weight entry into one file encrypted as described in Section 5. Read it with a hex editor and here is exactly what sits in the clear: the format marker, the app name and version, the export date, and the file's own cryptographic plumbing — which wrap is which, the KDF name and iteration count, each wrap's random salt, the IVs and the HMAC integrity tags. Those parameters must be plaintext or the file could never be opened by anyone, including you, and none of them reveals anything about your workouts or your password. The workouts, settings and body-weight entry themselves are ciphertext. Creating a backup first asks your device to confirm its screen lock — and if your device has no PIN, pattern or biometric lock at all, the app warns you in plain words and then lets you continue anyway. The file exports above run with no such check. - A route image. "share image" and "save to photos" on a workout capture a PNG of the workout card as drawn on your screen: the map with your full recorded route on it, start and end marked, plus the workout's weekday, calendar date and start time of day — the card's header carries the clock time, so the picture says when you train as well as where — its activity, headline figures, elevation and auto-pause details. "save to photos" writes that PNG into your device photo library — outside the app's sandbox: there it is an ordinary photo, visible to any app you let read your gallery, included in any photo cloud backup you run, and untouched by anything you later do in PFFT, up to and including uninstalling. It asks for photo-library permission first; refuse, and nothing is captured. "share image" needs no photo permission — the picture goes straight to the share sheet. Only on a device with no share sheet does it fall back to saving into your photo library, asking permission first; refuse there and it does nothing, and tells you nothing — a rough edge we would rather admit than hide.
- Health Connect — automatic once you switch it on. Every workout you finish is written into Android's health store on your phone at the moment it saves: no per-workout tap, and it keeps happening until you switch it off. Section 4.3 sets out exactly what each save writes, that it is write-only and generates no network traffic, and that neither "Delete All Data" nor uninstalling removes those records.
- The watch link — automatic while a paired watch runs the PFFT watch app. The phone mirrors workout state and spoken captions to your watch, and the watch sends live heart rate and pause / lap / repeat / stop commands back, over the device-to-device Wearable channel described in full in Section 16. No toggle gates it — the off switch is not installing (or uninstalling) the watch app. It is a live mirror between two devices you own: it reaches no PFFT server, and we never see what crosses it.
- One small text share. "share stats" sends a plain-text summary — activity, date, duration, distance, average speed, calories, elevation gain, and a PFFT tag line. No coordinates are in it.
"Nothing exports on its own" used to be true of this app; with Health Connect armed or a watch paired it is not, so we no longer say it.
Blind-First Mode reaches less of this, and we would rather state the difference than pretend the two shells match. That shell has no "sync & export" room — bulk export, the format picker, the encrypted backup, "Delete All Data" and the route image are all out of reach in it. What it does have is single-workout export: the "share · export" button on a workout's detail screen, producing the format already chosen — GPX unless it was changed in the standard app — with the spoken hint naming that format out loud. There is no way to change the export format from inside Blind-First Mode. To reach everything else, switch shells: Blind-First settings → "mode" → "off". A blind user's export path is narrower than a sighted user's; that is a real difference, and it belongs in the policy rather than in the gap between two screens.
10.2 What is inside an exported file
All four formats carry your recorded GPS track, point by point. The rest differs — measured from the code, not from intentions:
- GPX — per point: latitude/longitude, altitude (when recorded), timestamp and speed. Per workout: activity type, start time, distance and duration. The file names PFFT and pfft.app as its creator.
- CSV — per point: timestamp, latitude, longitude, altitude, speed and a
heart_ratecolumn. Nothing workout-level at all — no activity type, date or totals in the file itself (in a ZIP export, the manifest carries those). - TCX — per point: time, position, altitude and cumulative distance. Per workout: sport, start time, total time, distance and calories — the only format that carries calories. No speed.
- FIT — per point: timestamp, position, cumulative distance, altitude (when recorded). Per workout: sport, start and elapsed times, total distance. No speed, no calories.
Two fields deserve plain words. Speed rides in GPX and CSV: point-by-point speed turns a route into a movement profile — not just where you went, but how fast you were moving through it. One number in that profile needs a caveat: when the phone supplies no speed reading for a point, the app stores a literal 0 rather than leaving it out, and the exported file cannot tell that 0 from a real standstill. (Altitude is handled the stricter way — a point with no reading carries none — which is why "altitude (when recorded)" above is exact and speed earns this caveat instead.) Heart rate looks present and is not. The CSV format reserves a heart_rate column, so PFFT always writes that column header; TCX and FIT define heart-rate fields, and PFFT's writers emit them only when a workout sample actually carries a heart-rate number. PFFT never saves one — that includes the live heart rate a paired watch shows during a workout — so in every file the app exports the CSV column is empty and no TCX or FIT heart-rate field is written at all.
10.3 What happens to an exported copy afterwards
- The GPX / TCX / CSV / FIT files and the export ZIP are written into PFFT's own cache folder — inside the app's sandbox — and handed to the share sheet. They are left there afterwards, and deliberately: the share sheet hands another app a path, so the file has to outlive the tap. "Delete All Data" deletes them. That is the only thing in the app that does — deleting the workout does not, and nothing runs on a schedule. Short of pressing it, an export sits in that cache until Android reclaims the space or you uninstall the app; uninstalling removes it, because the cache is inside the sandbox. The temporary image files behind the route-image shares are not covered by that wipe and remain the old story: a third-party screenshot library writes them under its own names, nothing cleans them up, and they sit in app-private storage until you uninstall.
- The file picker makes its own copy, and that copy is the biggest thing in that folder. When you hand PFFT a file — your Strava export, or a
.pfftbackup you are restoring from — Android hands the app back a reference its file reader cannot open directly, so the picker copies the whole file into PFFT's cache folder first and gives the app a path to the copy instead. For a Strava export that copy is your entire location history in cleartext, sitting next to the encrypted records that are the only other place those same workouts live. Nothing used to remove it. Now the import deletes it the moment it finishes; abandoning a pick without importing deletes it; an import that fails, or that you cancel, before a single activity has landed deletes it there and then, because there is nothing to come back to; and "Delete All Data" removes whatever is left. There is one deliberate exception and it is the only one: an import that stopped partway with activities already imported — you cancelled it, or it was interrupted — keeps its copy so you can pick up where you left off. The importer offers you that choice the next time you open it, and the copy goes as soon as you either finish the import or choose to start over. - Those cache files do not ride your phone backup — Android's backup does not include an app's cache directory. That is platform behaviour, not a rule we wrote, and we would rather you knew which protections are ours and which are Android's.
- The
.pfftbackup is the odd one out twice. It is the one export the app cleans up after — and the one not written to the cache when you MAKE it (picking one to restore FROM is the other direction, and that does leave a picker copy — the bullet above): the app writes it briefly into its private document folder (still inside the sandbox), hands it to the share sheet, and deletes it as soon as the share call returns, completed, failed or cancelled. What that guarantee does not cover is the app's process being killed while the share sheet is still open: then the delete never runs, nothing sweeps the leftover afterwards, and — because the document folder, unlike the cache, is inside your phone-backup scope — such a leftover would ride your device backup until you uninstall. It would ride it as what it is: a fully encrypted container that opens only with your password or recovery key. - The route PNG in your photo library sits outside the sandbox. Deleting the workout does not delete it. "Delete All Data" does not delete it. Uninstalling PFFT does not delete it. It also rides any photo cloud backup you have switched on. If you want it gone, delete it in your gallery.
- Anything you already shared out — to a drive, a chat, anywhere — is in the receiving app's hands and beyond PFFT's reach.
10.4 Deleting your data
- Delete individual workouts: the delete button in a workout's details
- Delete all data: Settings → "sync & export" → "backup & restore" → Delete All Data (requires biometric authentication). It clears everything the app keeps in its own storage. Two halves, and they are not the same kind of promise — so here is which is which. The ordinary storage, where almost everything lives, is cleared by enumeration rather than by a list somebody maintains: the app reads back every key it holds, removes them all, then reads again and tells you if anything survived. The secure storage — your body-weight entry, a Notion token if you ever connected one, and the keys to your backup vault — cannot be enumerated at all: the Android API it uses can read, write and delete a key you name, but it cannot list what is there. That half is a named list we maintain, and a named list is the shape that can grow a gap. What stands in front of it is a check that runs on every build, and it is a safety net rather than a proof — a difference worth spelling out rather than blurring. It reads our own source — the app's code, not one folder of it — looking for the places that call the secure-storage library by name. Where it can work out which key one of those calls writes, it compares that key against the erasure list and fails the build if the list does not cover it. Where it cannot work out the key at all, it says so and fails the build rather than answering anyway; a file whose use of that library it cannot read stops it there. That polarity is deliberate: a check that guessed, and guessed a key that happened to be on the erasure list already, would read exactly like a clean bill of health. But the refusal only covers the cases it notices. Working out the key is a reading of the text, not an understanding of the program, and there are ways of binding a name — several of them ordinary — where the check reads one thing and the code means another. It then checks the wrong key, and if that wrong key is one the erasure list already covers, the build passes. Nothing about that case announces itself. A secret written through such a binding can survive the wipe. This passage used to say that a key the source declares in more than one place is one the check refuses to choose between; we tested that, and it is not so, which is why the sentence is gone rather than softened. And a write made through a reference to that library that has been handed on under another name is not one the check can follow either — that one it does not report at all, because it never sees the write. We would rather name the edges than let a sentence quietly cover them. The files — your workout records, the art cache, the debug logs, the files you exported, the downloaded voice model — are not keys either, and each is removed by its own step. All of that covers every workout record and the workout index, your settings, the background-art cache, your body-weight entry, your backup vault (the keys that unlock your on-device history — so the app goes back to asking you to set a backup password), your art-library entries, the in-flight route file and the live session's skeleton, the intervals configurator, the home-screen activity-grid layout, the Strava-archive import's resume markers, the picker's cached copy of the Strava export itself (a file rather than a stored setting, so it is named again in the file list below), a Notion token and page pointer if you ever connected one, the goal-, review- and battery-prompt counters, your language choice, the autopause calibration, your PolyMaps chapter names, your badge state, your saved interval patterns, the local crash log, the debug log files and buffer, the GPX / TCX / CSV / FIT files and export ZIPs you exported that are still in the app's cache, the copies the system file picker left in that same cache of files you handed to PFFT (a Strava export, a
.pfftyou restored from), and the downloaded voice model if Blind-First Mode ever fetched one. It also asks Health Connect to delete the records PFFT wrote there (Section 4.3) - What "Delete All Data" does not reach, named plainly. The files you exported are no longer on this list, and neither is the copy the file picker makes of anything you hand to PFFT. The wipe used to walk straight past the GPX / TCX / CSV / FIT files and the export ZIP sitting in the app's own cache — and a GPX is a full route, every coordinate of the workout in plaintext — so it now deletes them. It also walked past the picker's copy of your Strava export, which is the same kind of plaintext and usually far larger, and it now deletes that too. What it still does not reach, and the reason in each case: a copy that has already left the app — shared to another app, to your cloud drive, to your downloads folder — is outside PFFT's sandbox and beyond anything the app can do; the route image you saved to your photo library is outside the sandbox too, and is removed by neither the wipe, nor deleting the workout, nor uninstalling; and the temporary image files behind the route-image shares, which a third-party screenshot library writes under its own names rather than PFFT's, so the wipe does not recognise them — those sit in app-private storage and go on uninstall. Described in full in Section 10.3
- Your exported backups still work. Erasing the vault removes the keys from this device; it does not reach a
.pfftfile you already exported. Every.pfftcarries its own key material inside the file, so one you made before the wipe still opens with the same backup password or recovery key, wherever you put it - Copies that already left the app are outside both wipes: a shared file, a route image in your photo library, and any Health Connect records PFFT could no longer reach (see the revoked-permission case in Section 4.3). Delete each where it lives — the file where you sent it, the image in your gallery, the Health Connect records inside Health Connect
- Debug logs: stored in the app's transient cache (the OS may clear it at any time); removed by "Delete All Data" or uninstalling
- Waitlist email: the one thing you can hand us lives on our own mailing-list server, not on your device — it is deleted after the launch notification has been sent, or as soon as you ask to be removed, whichever comes first (Section 7)
In Blind-First Mode "Delete All Data" is out of reach entirely: deletion there is per-workout, or uninstalling, or switching to the standard shell first (Section 10.1).
10.5 What an uninstall does and does not remove
Uninstalling removes everything in the app's sandbox — workouts, settings, and the exported files and picker copies still sitting in its cache. "Everything" needs three carve-outs, so here they are:
- Copies you exported, shared, or saved to your photo library are yours and stay where you put them
- Workouts you synced to Health Connect stay in your phone's health store — delete them inside Health Connect (Section 4.3)
- With device backup on, the backup copy of your PFFT data stays in your own Google account, where reinstalling the app can restore it — within the limits in Section 6.1
Because we never receive your device or workout data, there is nothing for us to delete on our side. The single exception is the waitlist email above, and Section 7 covers exactly how and when it is deleted.
11. Data Sharing
We do not share your workout data with anyone. We cannot: we have no server, database, or mechanism that can reach it. Where it actually lives, stated exactly rather than rounded off: saved workout records sit encrypted on your device (Section 5); the workout index, your settings and your art-library entries sit unencrypted in the app's local storage, holding no coordinate; and, if device backup is on, a copy rides your own Google account backup (Section 6.1). Copies you take out — an exported file, a route image in your photo library, a workout written to Health Connect, a workout mirrored to your watch — leave that boundary because you asked them to, and Section 10.1 lists all seven routes. The only information you can hand us is what the waitlist form takes — your email address, kept on our own self-hosted mailing-list server as described in Section 7, and your platform preference, kept only as which announcement list you joined. Nothing else, and none of it ever handed to a data broker, an advertiser, or an insurer.
12. International Users
PFFT processes your workout data locally on your device — we move none of it across any border because we never have it. (The disclosed fetches in Section 6 reach a content server — the PFFT-operated map host — the way any app or browser does; your workout data is not part of them. Open-Meteo is not among them: the weather chip is switched off at the feature level and contacts nobody in current builds.) PFFT is built to comply with GDPR, CCPA, PIPEDA and equivalent regimes: no workout data ever reaches us — copies outside your phone are yours, whether that is your own device backup (Section 6.1) or something that left by one of the routes Section 10.1 lists, and none of them is a transfer to us. So most obligations around personal-data processing are satisfied by default.
Your rights are met on the device rather than by writing to us. Access — every workout, statistic and GPS route is visible in the app. Portability — export any workout as GPX, TCX, CSV or FIT, export everything at once as a ZIP, or create an encrypted backup (Section 10.1); in Blind-First Mode the reachable route is single-workout export, in the format already set. Erasure — delete workouts individually, wipe the app's store with "Delete All Data", or uninstall, with the exact reach of each set out in Sections 10.4 and 10.5; a copy you exported is yours to delete where you put it, and Health Connect records are deleted inside Health Connect. Objection — don't use the app.
13. Google Play Data Safety
For the Google Play Data Safety section, PFFT declares:
- Data collected: approximate location. When PFFT downloads the map for the area around you, our map host learns which area your phone asked for, and your IP address. That is what our host gets from you. It is not shared and not sold, and you can turn map downloads off in settings → map.
- Not collected: your workouts, your routes, your health data, your personal information, any identifier.
- Data shared: none.
- Security: the map request travels over HTTPS; your workouts are encrypted on your phone (Section 5).
- Deletion: nothing about you is kept on our side, so there is nothing to delete there; "delete all data" clears your phone.
14. Apple App Privacy (Future)
When PFFT launches on iOS, our App Store Privacy Nutrition Label will declare:
- Data Not Collected: PFFT does not collect any data
- Data Not Linked to You: No data is linked to your identity
- Data Used to Track You: None — PFFT contains no tracking
15. Open Audit
PFFT's privacy claims are verifiable. The app contains no analytics SDKs, no tracking code, no server communication for user data, and no telemetry. We encourage independent security researchers to verify these claims. Contact security@pfft.app for responsible disclosure.
16. Changes to This Policy
If we ever change this privacy policy in a material way, we will update this page at least 30 days before the change takes effect, and call the change out in the release notes that ship with the app update that carries it. The "Last updated" date at the top reflects the most recent revision.
We cannot notify you inside the app, and we will not claim we can. Being exact about why, because the old wording here claimed more than is true: the app does contact a host we run. It downloads map areas from tiles.niotenia.com. That host serves static files and is not an API — the app asks for a file and a file comes back. What follows from that is the part that matters: it carries no channel to you, nothing in the app asks any server whether this policy has changed, the build contains no push service at all, and we hold nothing of yours we could reach or address remotely. That is the same architectural fact that makes the rest of this policy true.
Being precise about that, because "no messaging of any kind" would be false: if you install the PFFT watch app, your phone and your watch talk to each other through Google Play services' Wearable data layer — a channel between two devices you own. It carries workout state, spoken captions and haptic cues from the phone to the watch, and live heart rate plus pause / lap / repeat / stop commands from the watch back to the phone. It reaches no PFFT server, it is not a way for us to contact you, and we never see what crosses it.
Notifications are the same shape. The phone posts the foreground-service notification that keeps GPS alive during a workout you started. The watch app posts its own: a workout-presence chip mirroring a phone-confirmed workout, and — when the watch is running its own sensing session — that session's foreground-service notification. Every one of those is posted locally, by a workout you started, on a device you are holding. None of them can be sent by us. So the honest instruction is: check this page, or read the release notes when you update.
Given PFFT's architecture, any change that introduces data collection would require a fundamental rewrite of the app — which we have no plans or incentive to do.
17. Contact
For privacy questions, concerns, or data requests:
- Email: privacy@pfft.app
- General: contact@pfft.app
- Website: pfft.app