# Google Play Data Safety — the answers, and what they are based on The Play Console asks, for every category of data, whether the app **collects** it, whether it is **shared**, whether collection is **required or optional**, and *why*. This file holds the answers for the Runic Gateway Android app, generated from the same inventory the published privacy policy renders — see `/privacy`, section 2. > **This is not a filled-in form.** Play’s definitions change and no check here can read them. Every answer below is a fact about the code with the reasoning attached; read the console’s current wording against them when you fill the form. What this file exists to prevent is somebody answering from memory about what the app stores. ## The premise every answer rests on We operate **no server the app talks to.** The app ships pointed at nothing: its first screen asks for the address of a Runic Gateway deployment and validates it before anything else in the app runs. That deployment belongs to whoever runs that community. Data therefore travels from the device to *their* server, and there is no endpoint of ours anywhere in the path — not for content, not for telemetry, and not for crash reports, of which there are none. That is why nearly every answer below is "not collected", and it is also the answer most likely to be questioned in a review. The supporting facts are in the table: each row names the file it was read out of. Where the console offers free text about security practices, two things are worth saying: credentials are held in Android’s encrypted storage (AES-256-GCM via Jetpack Security), and push notifications carry **no content** — a relay receives a stream name and a reference, and the app fetches the actual message over its own authenticated connection. ## Data types | Category | Data type | Collected by us | Shared by us | Answer | |---|---|---|---|---| | Personal info | User IDs | No | No | Not collected by us. | | Personal info | User IDs | No | No | Not collected by us. | | App info and performance | Other app data | No | No | Not collected by us. Stored on the device only. | | Messages | Other in-app messages | No | No | Not collected by us. Declare the relay hop in the console’s free-text security section if it asks. | | Messages | Other user-generated content | No | No | Not collected by us. | | Messages | Other in-app messages | No | No | Not collected by us. Stored on the device only. | | Device or other IDs | Device or other IDs | No | No | Not collected. | ## Each answer, and why it is the truthful one ### Your sign-in tokens **Personal info → User IDs.** Not collected by us. When you sign in to a deployment, the app keeps the access and refresh tokens it was issued, plus the username, role and account id they belong to. They are held in encrypted storage on the device (AES-256-GCM through Jetpack Security) and are sent to exactly one place: the deployment that issued them. - **Why that answer:** The credentials are issued by, and returned to, a server the user nominated. Nothing reaches an endpoint under our control, because we run none. - **Retention:** On the device until you sign out - **In detail:** Signing out clears them; uninstalling the app removes them with it. - **Read from:** `core/auth/EncryptedTokenStore.kt` ### The trusted-device token, if you asked for one **Personal info → User IDs.** Not collected by us. Ticking “trust this device” during two-factor sign-in stores an opaque token so the deployment can skip the second factor next time. It lives in its own encrypted store, deliberately separate from the session, because it has to outlive a sign-out to be worth anything — and the deployment holds only a hash of it, so the copy on your phone is the only usable one. - **Why that answer:** Same as the session tokens: minted by the user’s deployment, stored on the device, presented back to that same deployment. - **Retention:** On the device until it expires or you revoke it - **In detail:** Thirty days, and revocable at any time from the deployment’s Trusted Devices screen, which is also where it can be revoked if the phone is lost. - **Read from:** `core/auth/EncryptedTrustTokenStore.kt` ### The address of the deployment you chose **App info and performance → Other app data.** Not collected by us. Stored on the device only. The app ships pointed at nothing and asks for an address on first run. That address is stored in ordinary preferences rather than encrypted storage — it is not a secret, it is the equivalent of a bookmark — and it is what every other screen in the app talks to. - **Why that answer:** It never leaves the phone. It is the destination of requests, not the contents of one. - **Retention:** On the device until you change it or uninstall - **Read from:** `core/prefs/ServerPreferences.kt` ### Push registration, if you turn notifications on **Messages → Other in-app messages.** Not collected by us. Declare the relay hop in the console’s free-text security section if it asks. Push is off until you enable it. When you do, the app mints a random, unguessable topic name on the notification relay the deployment nominates, and registers that topic’s URL with the deployment so it has somewhere to send a nudge. What actually travels through the relay is content-free — a stream name and a reference, never the message — and the app then fetches the real content over its authenticated connection to the deployment. A leaked topic name therefore reveals nothing, which is the reason the relay needs no account and holds nothing about you. - **Why that answer:** The notification passes through a relay chosen by the deployment, and it carries no content — the app pulls the content itself, authenticated. Neither hop reaches a server we operate. - **Retention:** Until you turn push off, sign out, or uninstall - **In detail:** Signing out or disabling push unregisters the device with the deployment and discards the topic. The relay retains whatever its own operator configures it to; if the deployment points at a relay it does not run, that relay is a third party to both of us, and it still only ever sees a tickle. - **Read from:** `core/push/NtfyTopic.kt, core/push/PushPreferences.kt` ### Everything you read and post in the app **Messages → Other user-generated content.** Not collected by us. Forum posts, Team activity, character and shard information, notification preferences: all of it is a live read or write against the deployment. Apart from the notification snapshot described in the next entry, nothing is cached for offline use and nothing is duplicated anywhere else — the app with no signal is an app with almost no content, which is a limitation and also an accurate description of where the data lives. - **Why that answer:** Content is written to the community’s own installation. We have no copy, no access and no way to obtain one. - **Retention:** Held by the deployment, under its operator’s policy - **Read from:** `PLAN.md §9 section 2` ### A snapshot of your notifications, so the inbox opens without a signal **Messages → Other in-app messages.** Not collected by us. Stored on the device only. The app keeps the most recent notifications it has already fetched — at most thirty, and only the first page — on the device, so opening the inbox shows you what you had rather than a spinner. It is a copy of what the deployment already sent you and it is refreshed from there; nothing is written here that was not read from your own account. It is scoped to the account that fetched it, so a second person signing in on the same phone is never shown the first one’s messages. - **Why that answer:** The snapshot is written on the phone from data the deployment had already delivered. It is not uploaded anywhere, and no server we operate is on either end of it. - **Retention:** Until you sign out, or the thirty are pushed out by newer ones - **In detail:** Signing out deletes the snapshot outright. It lives in the app’s ordinary preference store rather than the encrypted one — sign-in tokens are the thing that store is for — which is worth stating plainly: on a device where someone has root, these are readable, and they are notification bodies rather than credentials. - **Read from:** `core/inbox/DataStoreInboxCache.kt, data/repository/AuthRepository.kt` ### No analytics, no crash reporting, no advertising **Device or other IDs → Device or other IDs.** Not collected. There is no third-party SDK in the app at all — no Firebase, no Crashlytics, no advertising identifier, no measurement library. That is checkable rather than claimed: it is what the dependency list and the manifest say, and a build that gained one would gain permissions with it. - **Why that answer:** No advertising ID, no analytics identifier, and no library that would generate one is linked into the build. - **Retention:** Nothing to retain - **Read from:** `app/build.gradle.kts, app/src/main/AndroidManifest.xml` ## The rest of the listing - **Privacy policy URL:** `/privacy` on this site. It is the URL Play is given, and section 2 of it is about the app specifically. - **Target audience:** adults. The beta is stated as **18 or older** (D31); the app contains no content directed at children and no age verification. - **Account deletion:** the app creates no account with us — an account belongs to the deployment the user chose, and is deleted there. The only list we hold is the beta signup, which is erased on request; `/privacy` section 4 says how to ask. - **Data deletion request URL:** the contact address published on `/privacy`, which is read from the mounted `brand.json` rather than typed anywhere in the source (D13). ## What the website collects, for the same reviewer Not part of the Data Safety form — that form is about the app — but a reviewer who follows the privacy policy URL lands on a page covering three things, so it is worth knowing which of them the site itself is responsible for: - **Your email address** — Until the beta ends, or until you ask. - **The wording you agreed to, and when** — For the life of the row. - **A one-way hash of your IP address — never the address** — With the row; the rate-limit log is pruned after 48 hours. - **Your browser’s user-agent string, truncated** — With the row; blanked on removal. - **The web server’s access log** — Short-term operational retention, then rotated away. Last generated from data dated 2026-09-01. Regenerate with `npm run play:datasafety` after any change to what the app stores.