Privacy
Tập Nhạc is local-first. Your library, your passages and your practice history live on your own device, and nothing leaves it unless you sign in and use a feature that needs the cloud.
This page describes what the app does today. It is written from the code, not from a template.
What stays on your device
Everything by default. Scores, passages, practice history, settings, fingerings, collections and editor drafts are stored in your browser's IndexedDB database named primavista, through Dexie. A small amount of interface state (theme, remembered username, which tours you have seen, the welcome flag) is kept in localStorage.
Some things never sync at all: the thumbnail cache rebuilt from your scores, the instrument-pack registry, the three settings that describe one device (MIDI device, latency offset, keep awake), editor drafts and versions, the soundfont and engraving caches, and the service worker cache.
You can use the whole practice loop (import a score, pick a passage, drill it) with no account and no connection. Once the app has loaded it works offline.
Optional account
An account is optional and is only needed for sync, sharing, classes and Compose with Claude. It has a username you choose and a passkey (WebAuthn). There is no email address, no password and no phone number.
The server stores the username, the public half of the passkey and its signature counter, and a one-time challenge while a sign-in is in progress. A successful sign-in sets a session cookie named pv_session: httpOnly, SameSite=Lax, 30 days, and Secure when served over HTTPS. That cookie is the only cookie the app sets.
Cloud sync and backups
When you are signed in, your device pushes the rows it changed and pulls everything newer than its last cursor. What is uploaded:
- Pieces, including their score files and metadata.
- Passages (the bar ranges you practise) and their progress.
- Practice history, packed one row per piece and day rather than one row per note.
- Practice sessions, fingerings, collections and your own bar notes.
- Settings, except the three device-only fields listed above.
Rows are stored in Cloudflare D1. Score files are stored in Cloudflare R2 by content hash, under your user id. Every read and write on the server is scoped to the signed-in account; the server validates each row and never inspects musical content.
Deleting. Deleting a piece, passage or other synced row records a tombstone, so the delete reaches your other devices. A daily job at 03:17 UTC removes tombstones older than 90 days, and removes score files that no live piece references once they are more than 30 days old. A device that has been offline for longer than that resyncs from scratch.
Signing out leaves your cloud copy in place. You can also tick "Also remove my library from this device" to wipe the local copy. There is no delete-account button in the app today, so signing out (even with the local library removed) does not remove the cloud copy.
Older versions of the app also uploaded one whole-library backup file per account. The current version reads it once to migrate and no longer writes it.
Compose with Claude
The editor's "Compose with Claude" panel needs a connection and a signed-in account. Nothing is sent anywhere unless you use it.
When you do, your prompt and the score text the model reads are sent from the browser to the app's own Worker, which forwards them to Anthropic's Messages API. The API key stays on the server: it never reaches the browser and is never written to logs (a failed upstream call logs only its status).
Tập Nhạc keeps no copy of the conversation. It stores only a call count in D1, used for the daily limit of 60 calls per user. Anthropic's handling of the request is covered by Anthropic's own terms.
No ads, no tracking cookies
There is no advertising and no cross-site tracking. Page views are counted with Cloudflare Web Analytics, which sets no cookies and does not follow you across sites. The only cookie the app sets is the session cookie described above, and only while you are signed in.
When the app crashes, it sends one small report: the error text, the route pattern (ids and invite tokens are stripped), the app version and your browser family (for example "chrome"). It carries no account, no IP address and nothing from your scores, and it is stored only as a count per error and hour.
Fonts, the piano soundfont and the bundled scores are served from the app itself, not from a third-party CDN. localStorage holds interface preferences and flags, not identifiers used to follow you.