learnCN / fluentCN — Chính sách quyền riêng tư

Bản tiếng Việt là bản chính thức. Bản tiếng Anh bên dưới chỉ để tham khảo.
Áp dụng cho ứng dụng iOS/Android fluentCN (bundle id app.learncn.mobile).
Có hiệu lực từ 07/11/2026 (phiên bản 1.0).

Mọi câu trong tài liệu này đều đối chiếu được với mã nguồn; bảng đối chiếu ở
Phụ lục A (tiếng Trung, dành cho nội bộ).

---

1. Tóm tắt trong 30 giây

không bản ghi âm, không văn bản. Chỉ có tải tệp nội dung xuống (mục 7).

App Store — có thêm một kênh số liệu ẩn danh (chỉ loại sự kiện và số đếm). Chi tiết ở mục 5.

---

2. Ai chịu trách nhiệm

Ứng dụng do nhà phát triển độc lập vận hành (sau đây gọi là "chúng tôi"). Liên hệ: TODO(owner) (email hỗ trợ — phải điền trước khi phát hành).

Ứng dụng không có máy chủ nghiệp vụ: không có đăng nhập, không có đồng bộ tài khoản, không có cơ sở dữ liệu người dùng trên đám mây. Hạ tầng duy nhất mà app chạm tới là CDN phân phối âm thanh (mục 7) và — chỉ ở bản nội bộ — máy chủ nhận số liệu ẩn danh (mục 5–6).

---

3. Dữ liệu học của bạn

Dữ liệu Nơi lưu Rời khỏi máy? (bản App Store)
Tiến độ bài học, bài đang học, cấp độ HSK đã chọn SQLite trên máy Không
Thẻ ôn tập (SRS), lịch đến hạn, số lần quên SQLite trên máy Không
Sổ lỗi (âm tiết đọc sai, câu nghe nhầm) SQLite trên máy Không
Chuỗi ngày học (streak), mục tiêu, thời lượng mỗi ngày, giọng đọc đã chọn SQLite trên máy Không
Điểm từng lần đọc theo, đường thanh điệu đo được SQLite trên máy Không

Cơ sở dữ liệu nằm trong vùng lưu trữ riêng của ứng dụng và chỉ app này đọc được. Gỡ ứng dụng = xóa toàn bộ dữ liệu học. Chúng tôi không giữ bản sao nào để khôi phục. Bản sao lưu iCloud/Google của thiết bị (nếu bạn bật) do hệ điều hành quản lý, không phải chúng tôi.

Xóa âm thanh đã tải (Cài đặt → Quản lý dung lượng) chỉ xóa tệp âm thanh, không xóa tiến độ, SRS hay sổ lỗi; tải lại là khôi phục.

---

4. Micrô và bản ghi âm

trên máy, rồi bị bỏ ngay — không ghi ra tệp, không vào thư viện ảnh, không vào bản sao lưu.

Câu hệ thống hiển thị đúng như trong Info.plist: «Mic chỉ dùng để chấm điểm phát âm của bạn ngay trên thiết bị. Âm thanh không bao giờ được tải lên.»

---

5. Số liệu sử dụng ẩn danh — chỉ có ở bản nội bộ

Bản phát hành trên App Store không có kênh này. Endpoint nhận số liệu bị bỏ khỏi bản phát hành (app.config.ts, profile release), và app không xếp sự kiện nào vào hàng đợi. Vì vậy trên App Store, App Privacy được khai là không thu thập dữ liệu.

Bản nội bộ (TestFlight, dùng để thử nghiệm trước khi phát hành) có kênh số liệu ẩn danh, mặc định bật, tắt được trong Cài đặt. Mục đích: biết phần nào hỏng, phần nào bị bỏ qua. Không dùng để quảng cáo, không dùng để dựng chân dung người dùng.

Gửi gì (chỉ các trường dưới đây, đã lọc ở phía app):

Sự kiện Trường dữ liệu
session_start / session_end dấu thời gian, số thứ tự phiên trong ngày
plugin_enter / plugin_complete mã plugin (nghe / đọc theo / SRS / biến điệu), số giây
read_submit độ dài bản ghi (giây), số âm tiết
read_verdict đạt/không đạt, danh sách mã lỗi thanh điệu, tỉ lệ âm tiết đúng
listen_answer loại câu, đúng/sai, cặp âm đối lập
srs_review số thẻ đã ôn, tỉ lệ "quên"
backend_profile / app_update_seen mức hiệu năng thiết bị, phiên bản

Không gửi: bản ghi âm, văn bản tự do, tên, email, số điện thoại, ID thiết bị, ID quảng cáo (IDFA), vị trí, danh bạ, ảnh, lịch sử duyệt web.

Phía máy chủ (Cloudflare Worker): sự kiện được lưu theo lô cùng hash SHA-256 của địa chỉ IP có muối xoay theo ngày — chỉ để chặn spam, không lưu IP thô; giới hạn tần suất nằm trong bộ nhớ và tự mất khi worker khởi động lại.

Tắt: Cài đặt → «Gửi thống kê ẩn danh». Tắt là không còn sự kiện nào được ghi vào hàng đợi.

---

6. Hiệu chỉnh giọng đọc — chỉ có ở bản nội bộ, mặc định tắt

bạn tự quyết định gửi cho ai.

---

7. Tải âm thanh và mô hình từ CDN

loại thiết bị trong header. Chúng tôi không gắn dữ liệu này với tiến độ học của bạn và không đặt cookie theo dõi.

không tải thì app vẫn chấm điểm, chỉ dùng cách chia âm tiết gần đúng thay vì canh theo mô hình.

---

8. Chia sẻ với bên thứ ba

Chúng tôi không bán, không cho thuê, không trao đổi dữ liệu. Không có bên thứ ba nào nhận dữ liệu học từ app: không có SDK quảng cáo, không có SDK phân tích, không có công cụ báo lỗi từ xa (Sentry hay tương tự).

Nhà cung cấp hạ tầng duy nhất: Cloudflare (R2 cho tệp; Workers chỉ nhận số liệu ẩn danh ở bản nội bộ), chịu ràng buộc bởi chính sách riêng của họ.

---

9. Trẻ em

Ứng dụng hướng tới người học từ 13 tuổi trở lên và không dành cho trẻ dưới 13. Chúng tôi không cố ý thu thập dữ liệu của trẻ dưới 13 tuổi; app cũng không có tài khoản, không có hồ sơ cá nhân, nên không có kênh nào để thu thập.

---

10. Quyền của bạn

Vì dữ liệu học nằm trên máy bạn, bạn kiểm soát trực tiếp:

(chỉ có thể là sự kiện ẩn danh ở bản nội bộ), hãy viết tới địa chỉ ở mục 2; vì không có định danh người dùng, chúng tôi không thể truy ngược một bản ghi về một người cụ thể — và đó chính là lý do thiết kế như vậy.

---

11. Thay đổi chính sách

Khi hành vi của app thay đổi (ví dụ bật một kênh mới trong bản phát hành), tài liệu này được cập nhật trước khi bản phát hành tương ứng lên cửa hàng, kèm ngày hiệu lực mới. Ngày hiệu lực luôn ở đầu tài liệu.

---

English (reference only)

The Vietnamese text above is the binding version.

Summary. No account. The App Store build sends no data at all — no events, no recordings, no text; the only network traffic is downloading content files (§7). Study data (progress, spaced-repetition cards, error book, streak, settings) lives only in a local SQLite database inside the app's own container. The microphone is used only while you press the record button; audio is streamed as in-memory 16 kHz PCM, scored on-device by a local model, then discarded — never written to a file and never uploaded, in any build. There are no ads, no third-party analytics SDKs and no tracking identifiers.

Internal (TestFlight) builds only. The pre-release build used for testing keeps an anonymous usage channel, on by default and switchable off in Settings. It carries enums and counts only: session timestamps and sequence numbers, plugin ids and durations, recording length in seconds, syllable count, pass/fail, tone error-code enums, per-syllable correct ratio, and device-class / app-version tags. No audio, no free text, no name, no email, no device or advertising identifier, no location. The receiving Cloudflare Worker stores the batch with a daily-salted SHA-256 hash of the client IP (anti-abuse only; the raw IP is not stored) and keeps rate-limit counters in memory. The App Store build ships without the endpoint (app.config.ts, profile release), so no event is even queued.

Optional calibration (internal builds only, off by default). Only aggregate pitch metrics, error codes and model version; no audio, no text. An internal-only manual export shares the same JSON through the OS share sheet.

Audio and model downloads. Sample audio and an optional speech-recognition model (SenseVoice-small INT4, ~123 MB) are fetched from Cloudflare R2 over a public host. The CDN sees what any web server sees (IP, time, requested file, user agent). It is not linked to your study progress, and no tracking cookies are set. Downloads default to Wi-Fi only. The model is optional: without it, scoring falls back to an even-split syllable approximation.

Third parties. None beyond Cloudflare (R2 for files; Workers for anonymous event intake in internal builds only). No data is sold. No advertising SDK, no analytics SDK, no remote crash reporter.

Children. 13+; no account and no profile means there is no collection channel for children.

Your controls. Inspect everything in-app; delete downloaded media in Settings without losing progress; delete all data by uninstalling. The App Store build collects nothing to switch off; internal builds expose the switch.

Contact. TODO(owner) — support email, to be filled before release.

---

Phụ lục A — 对源码的依据(内部审计用,不对外)

声明 代码位置
无账号、无登录 app/ 无 auth 路由;.env 的 EXPO_PUBLIC_GATEWAY_* 为空;docs/DEVICE-BUILD-20261107.md
学习数据只存本机 SQLite @repo/mobile-core LocalDbPort → Documents/SQLite/webfactory.db(表 docs(collection,id,json,updated_at))
进度/SRS/错题/streak 集合 实测 DB collection:course_progress srs_cards tone_read_errors memory_reviews user_profile app_settings
录音内存 PCM、不落盘、不上传 packages/mobile-core AudioPort(expo-audio,16kHz float32,recordStop → PCM);startFileRecordingAsync 未被调用
麦克风文案 apps/mobile/app.config.ts NSMicrophoneUsageDescription
Release 版无遥测端点 apps/mobile/app.config.ts BUILD_PROFILE(默认 release → telemetryEndpoint: null、ingestKey: null);src/platform.ts getTelemetryEndpoint();src/telemetry/use-telemetry.ts endpointUrl() === null → false(不入队)
Internal 版遥测默认开、可关 use-telemetry.ts(enabled !== false)、src/telemetry/dictionary.ts(事件白名单,仅枚举/计数)
隐私清单「不收集数据」成立 app.config.ts NSPrivacyCollectedDataTypes: [];Release profile 无 endpoint
服务端只存日盐 IP 哈希、内存限流 calibration-worker/src/index.ts ipHash()(salt = YYYY-MM-DD,SHA-256)、rateLimited()
校准默认关、只发聚合 src/calibration/queue.ts(loadCalibrationOptIn)、src/calibration/upload.ts(skipped: "opt-out")
反馈自由文本通道已删除 app/feedback.tsx、src/feedback/ 已删除(2026-11-07);dictionary.ts 的 content_feedback 一并退役;worker 本无 /v1/feedback 路由
音频从 CDN 下载 src/download/expo.ts + .env EXPO_PUBLIC_DISTRIBUTION_BASE_URL=https://pub-…r2.dev
模型 ~123 MB、按需下载 src/download/expo.ts EXPO_PUBLIC_MODEL_PACK_DIR ?? "sensevoice-small-int4";R2 models/sensevoice-small-int4/manifest.json = 2 文件 129,258,743 bytes
仅 Wi-Fi 默认 src/download/prefetch.ts readDownloadWifiOnly()
无第三方 SDK package.json 依赖清单:无 Sentry、无 Firebase、无广告 SDK

Phụ lục B — 发布口径(已定,2026-11-07)

不入队、不发请求;App Privacy 标签 = 不收集数据;Settings 内两个网络开关不显示。

校准默认关(可 opt-in),用作发布前唯一真实使用信号。

RELEASE-READINESS-20261107.md §6。