Privacy
Last updated: August 19, 2026
This page is the long version, on purpose. It lists every field the app can send, what it is for, how long we keep it, and how to erase it, so you can decide before you are asked rather than after.
What “100% local” covers
“100% local” is a claim about two things: your credentials and your store data. Your App Store Connect key, your Google Play service account, and everything they fetch (listings, builds, reviews, screenshots, sales) stay on your machine, and the app uses them to talk to Apple and Google directly. They are never sent to us: no Storedeck server ever receives them, so none can hold them. That part has no setting and no exception; nothing further down this page changes it.
Here is what does reach us, so you never have to guess. On this website: the email address you type before downloading, and nothing else. In the app: the usage statistics described below, and only if you say yes to them. The app also checks a static update feed at updates.storedeck.app, the same way any downloader asks whether a newer build exists; that request carries no identifier of you.
This website
Before the download starts, we ask for your email address. We store it with the version you downloaded, and nothing else. The app has no account, so this is our only way to reach you: we use it to announce releases, fixes and new platforms. It is never tied to what the app does on your machine. This site runs no analytics, no tracking pixels, no cookies and no third-party scripts.
To get off the list, write to hello@storedeck.app and we delete your address.
Usage statistics, if you say yes
The app asks once, on a screen you cannot miss, before you connect a single store account. It sends nothing before you answer: no pre-ticked box, no “OK” that is really a yes, no first event slipped out while the question is still on screen. If you never answer, it never sends anything.
Development builds are silent by construction: they cannot send anything, whatever you answered. Only a packaged, released build ever sends an event.
Each installation draws a random identifier, a UUID v4 tied to nothing: not your hardware, not your network, not an account, because Storedeck has no accounts. The app writes it in plain text in its data folder, on purpose, so you can open the file and read it yourself. It is a pseudonym, not an identity: it is stable across launches, which is the only reason we can tell “someone came back” from “someone new”. It carries no personal data: no name, no email, no machine name, nothing that points at you.
Exactly what is sent
The list below is not a summary of what we collect. It is the whole of it. The app can only encode these fields, because the shape is a closed type: the build checks it, then the server checks it again and refuses any payload that carries a field it does not expect. There is no “other” field, no free-text field, no attached context, no blob.
Every event carries the same seven-field envelope:
| Field | Example | What it is for |
|---|---|---|
installationId | 9f2c1e4a-7b3d-4c8e-9a15-2d6f0b7e8c31 | The random per-installation identifier. Without it, “came back” cannot exist. |
localDate | 2026-08-16 | The local day, never a time. A precise time plus a time zone reports your working hours. |
version | 0.0.82-nightly | Which build you are on, so a drop in usage can be pinned to a release. |
channel | nightly | Pre-release or not. |
os | darwin | One of darwin, win32, linux. Which platform deserves the next build. |
arch | arm64 | One of arm64, x64. |
locale | fr | One of en, fr, es. Which translation to prioritise, on evidence rather than a hunch. |
And one of five events, each with at most two extra fields:
| Event | Payload | Sent when | What it answers |
|---|---|---|---|
app_opened | hasGplay, hasAsc | When you open the app, at most once per day. | Whether people come back, and whether the dual-store promise is used at all. hasGplay and hasAsc are booleans: “this machine has a Google account connected”, never how many apps it manages. |
account_linked | store | When you connect a store account to a space. | How many installations get past the credential setup. store is gplay or asc. |
account_link_failed | store, reason | When connecting a store account fails. | Why people give up before seeing the product. reason is one of the app's own error categories (auth, not-found, timeout…): no message, no stderr, no file path. |
screen_viewed | screen | When you navigate to a screen. | Which screens nobody ever opens. screen is one of a fixed list of route names, never a string built at runtime. |
write_confirmed | family | When you confirm a write to a store. | Which kinds of change are worth building. family is the kind of change, never its target and never its content. |
A complete event, verbatim. This is the whole of it:
{
"installationId": "9f2c1e4a-7b3d-4c8e-9a15-2d6f0b7e8c31",
"localDate": "2026-08-16",
"version": "0.0.82-nightly",
"channel": "nightly",
"os": "darwin",
"arch": "arm64",
"locale": "fr",
"event": "app_opened",
"hasGplay": true,
"hasAsc": false
}Settings → Privacy also shows the exact JSON of the last event that left your machine, verbatim. Not a description of it, not a category: the payload itself, exactly as it left.
How long we keep it
A daily sweep deletes every row that carries your installation identifier once it is 12 months old. We keep a daily rollup without a time limit, but it carries no identifier at all: it is (date, event, version, count) and nothing else, so nothing can trace it back to an installation and it is no longer personal data. That rollup is what lets us tell, years later, that a release broke something, without tracking anyone.
Erasing it
Settings → Privacy has a “Delete my data” button. It erases every row tied to your installation, right away, and it works even when the setting is switched off: you never have to turn statistics back on in order to erase them. It does not touch the identifier-free rollup above. The identifier itself stays on your machine, because switching the setting off or erasing does not regenerate it: a new identifier every time would quietly turn one returning user into ten new ones.
If you say no
If you decline, the question never comes back. Not at the next launch, not after an update, not if we later decide to collect something different. Declining is permanent, and it costs us the measurement on your machine for good. That is the point. If you accepted and we ever want to collect something outside the list on this page, the app asks again, and it only ever asks the people who accepted.
A decline sends nothing at all, not even a “declined”. So we cannot count how many people said no, which makes every number we have a floor rather than a measurement. We read them as trends, and mostly in the negative: “nobody opened this screen in three months” is the kind of answer they are good for.
What we can never know
| Question | Why it stays unanswered |
|---|---|
| Who you are | No email, no account, no name. The identifier links to nothing else we hold. |
| How many apps you manage | Business counts are outside the ceiling: they are your customers' data, not ours. |
| Which apps | No package name, no bundle id, no store identifier. Not in any event. |
| What you wrote in a listing | A confirmed write reports the kind of change, never its content. |
| What hours you work | The local day only. There is no field that can carry a time. |
| Where you are | No event carries your IP address, and there is no geolocation of any kind. The endpoint keeps a short-lived counter keyed on it to fend off floods; a daily sweep deletes it. |
| Whether you said no | A decline sends nothing, not even a decline. The silence is total. |
Questions
Anything unclear, or anything you want erased: hello@storedeck.app. A real person reads it.