Skip to content

Privacy Policy

Effective

1. Introduction

FluentBits is an iOS app for learning Mandarin Chinese. This page explains what data the app collects, how that data is used, and the choices you have about it. It is written in plain language. If anything here is unclear, email me at christophe.ha1+fluentbprivacy@gmail.com.

2. Who is responsible for your data

FluentBits is operated by Christophe Ha, an individual developer. For the purposes of the General Data Protection Regulation (GDPR), I am the data controller. For privacy questions or to exercise any of the rights described below, email christophe.ha1+fluentbprivacy@gmail.com.

3. Information we collect

3.1 Account information

When you sign in with Apple, FluentBits receives your Apple user identifier (a stable, anonymous ID from Apple) and, if you choose to share them on first sign-in, your email address and given/family name. These are stored in the iOS Keychain on your device and in our Postgres database so that we can authenticate you and restore your subscription across your devices. We also record when you last signed in and when you last used the app so we can keep your session valid and generate your daily picks at an appropriate time.

As part of Sign in with Apple, Apple also issues us a refresh token scoped to your account. We store this token in our Postgres database and use it for one purpose only: when you delete your account from inside the app, we send the token back to Apple to revoke our access to your Apple account so no trace of the FluentBits sign-in remains on Apple's side. We never use this token to read any data about you from Apple.

3.2 App usage data

When you look up a Chinese word, the text of the word is sent to our backend. On first lookup, our backend forwards the word text to Google's generative AI API to generate a rich definition; the resulting definition is then cached on our infrastructure so subsequent lookups of the same word are served without calling Google again.

We store two things as a result of lookups. First, a daily lookup count per user (to enforce the free-tier quota). Second, a shared dictionary cache containing every word any FluentBits user has ever looked up: the word itself, its pinyin, its English meaning, and its generated definition. The cache is content-addressed: it is shared across all users, and we do not record a per-user history linking any particular lookup back to you.

3.3 Subscription information

If you subscribe to FluentBits Premium, Apple provides us with your subscription transaction identifiers. We store the original transaction ID and product ID in our database so we can verify your subscription and restore it on new devices. We do not receive your credit card number or any other payment information. Apple handles payment.

3.4 Device and technical information

FluentBits generates a random device identifier (a UUID) stored in the iOS Keychain on your device. When you are signed out, we also store that identifier and a lifetime lookup count in our Postgres database so we can enforce the free anonymous-lookup limit. The device identifier is not linked to a name, an email, or any other personal data. Our backend infrastructure (Cloudflare Workers) briefly retains standard request metadata (timestamp, status code, IP address) for rate limiting and operational purposes. IP addresses are not stored in our database.

3.5 Learning profile and personalized daily picks (signed-in users)

If you are signed in, the app periodically sends a learning profile to our backend so we can generate a small daily list of recommended words to practice. This learning profile, stored on your account row in our Postgres database, contains:

  • The total number of words in your deck.
  • The distribution of those words by HSK level, by tag (topic / part of speech), and by mastery level.
  • Up to 1,500 simplified Chinese characters you have already added to your deck or marked as "known" (so recommendations avoid repeating them).
  • The 50 most recently added words, with their simplified form, mastery level, and tags (so recommendations build on the themes you are actively studying).

We also store, separately: the list of recommendations we generated for you on a given day, a snapshot of the learning profile used to generate them, and which recommendations you accepted. All of this is tied to your account and is deleted when you delete your account.

4. How we use your information

We use the information described above to:

  • Authenticate you and keep you signed in across devices
  • Serve Chinese word definitions, example sentences, and usage notes
  • Synthesize native audio pronunciation for words you request
  • Extract Chinese text from images you capture
  • Enforce daily lookup quotas on free accounts
  • Verify and restore your subscription with Apple
  • Generate your personalized daily picks (if signed in)
  • Diagnose crashes and performance issues via Sentry. When the app crashes, Sentry also captures a screenshot of the screen that was visible at the moment of the crash so we can reproduce the bug. We also enable Sentry's network breadcrumbs, which attach the URL paths and HTTP status codes of the app's outbound network calls to any event that Sentry records; request bodies and authorization headers are not included because we disable Sentry's default personal-data collection. Separately, our backend forwards its own server-side error reports to Sentry, both as log messages and as structured exception reports. These reports may include the specific word or pinyin string involved in a failed request (for example, a failed lookup, text-to-speech, or recommendation acceptance), so Sentry can receive the same word text described in Section 3.2 as part of a backend error report.
  • Improve the app by fixing bugs, shaping features, and understanding usage

5. Information processed but not stored

5.1 Text-to-speech

Text you request speech for is sent to Google's generative AI API for synthesis. The resulting audio is cached in Cloudflare R2 object storage by content hash, meaning two users requesting audio for the same word share the same cached file, and the cache key is not linked to you personally.

5.2 Image text extraction

When you capture an image to extract Chinese text, the image bytes are sent to Google's generative AI API for extraction. The raw image bytes themselves are not stored on our servers. The extracted text (Chinese sentences, pinyin, and English translations) is cached in Cloudflare R2 object storage by a content hash of the image bytes, so two users submitting the identical image share the same cached result, and the cache key is not linked to you personally. The extracted text is returned to your device, where you can choose to look up any word it contains.

6. Third parties we share data with

FluentBits relies on the following sub-processors. Each provides a specific service and receives only the data needed for that service.

ProviderPurposeData shared
AppleSign in with Apple, StoreKit, CloudKitApple user ID, optional email/name, subscription transaction IDs
CloudflareBackend hosting, edge network, object storage (R2), rate-limit state, Workers Logs observabilityAPI requests (including word text, TTS text, and image bytes in transit to Google), synthesized audio cached in R2 by content hash, extracted-text results cached in R2 by image-content hash, IP address for rate limiting, and backend operational log output via Cloudflare's built-in Workers Logs feature (which captures the same word text or pinyin that may appear in backend error messages).
NeonPostgres database hostingAccount records (including last-sign-in and last-activity timestamps and an Apple-issued refresh token used only to revoke access on account deletion), subscription records, daily lookup counts, the per-user learning profile described in 3.5, the shared dictionary cache (words looked up by any user), anonymous device IDs with lifetime usage counts, and daily recommendations and accepted-recommendation records.
GoogleGenerative AI (word definitions, text-to-speech, image text extraction)Word text, TTS text, image bytes
SentryCrash and performance telemetry (hosted in the EU, de.sentry.io)Apple user ID (for the signed-in crash context only), error and performance metadata, network breadcrumbs (URL paths and HTTP status codes of outbound network calls), a screenshot of the app screen visible at the moment of a crash, and backend server-side error reports (both log messages and structured exception reports) which may include the word text or pinyin involved in a failed request. Default personal data collection (`sendDefaultPii`) is disabled, so request bodies and authorization headers are excluded.
VercelStatic hosting for this website (the pages you are reading now)Standard HTTP request metadata (IP address, User-Agent, URL path) received by Vercel's web servers when you visit this site. This website does not set cookies and does not include any analytics or tracking SDKs.

7. iCloud sync (CloudKit)

If you are signed into iCloud on your device, FluentBits optionally syncs your word list, practice history, and review logs to your private iCloud container. This data lives on Apple's servers inside your personal iCloud account. FluentBits cannot read or access it. Only your devices can. You control it through the standard iOS iCloud settings.

8. Data retention and deletion

We retain your account record, subscription records, daily lookup counts, learning profile, daily recommendations, and accepted-recommendation records for as long as your account is active. You can delete your account from inside the app (Settings → Account → Delete account). When you do, every record tied to your account is removed and your subscription records cascade with them. On-device data is cleared at the same time.

Three categories of data are not account-scoped and therefore are not removed on account deletion: (a) the shared dictionary cache described in 3.2. It contains no per-user association and is retained for operational efficiency; (b) anonymous device-ID usage counts used to enforce the free-tier limit for signed-out users; and (c) subscription webhook records. Apple sends us server-to-server notifications about subscription lifecycle events (renewals, cancellations, refunds); we keep a processed-event deduplication log (automatically purged after 7 days) and a retry queue for events that failed to process. These records contain Apple-issued transaction identifiers and, for the retry queue, the raw signed payload from Apple. They have no direct user-account reference in our database and are retained for operational integrity. If you would like your anonymous device-ID record removed, email us at the address in section 13 and we will work with you to delete it.

9. International data transfers

Your data may be processed in regions other than your own, including the United States and the European Union, via Cloudflare's global network and the providers listed above. Where applicable, we rely on standard contractual clauses and on the providers' own transfer mechanisms.

10. Your rights

Depending on where you live, you may have the following rights with respect to your personal data:

  • Access: ask what data we hold about you.
  • Rectification: ask us to correct inaccurate data.
  • Erasure: ask us to delete your data (in-app account deletion is the quickest path).
  • Portability: ask for an export of your data.
  • Objection and restriction: object to processing or ask us to restrict it.
  • Withdraw consent: where processing is based on consent, withdraw it at any time.
  • Do-not-sell (CCPA): we do not sell your data. There is nothing to opt out of, but you have the right to confirm this.

To exercise any of these rights, email christophe.ha1+fluentbprivacy@gmail.com.

11. Children's privacy

FluentBits is not directed at children under 13 and we do not knowingly collect personal data from children. If you believe a child has provided us with personal data, email christophe.ha1+fluentbprivacy@gmail.com and we will delete it.

12. Changes to this policy

We may update this policy as FluentBits evolves. When we do, we'll update the effective date at the top of this page. If a change is material, we will prompt you in the app.

13. Contact

For any question about this policy or your data, email me directly: