# Privacy Navidrome Stat stores listening activity so it can build a shared dashboard across Navidrome servers, clients, and devices. This page describes what the application handles and what operators should protect. ## Data stored The SQLite database can contain: - Navidrome usernames; - track, artist, and album IDs, together with titles, artist names, and album names; - client names and transcoding status; - playback timestamps and recorded listening duration; - Navidrome server identifiers and display names; - counted plays and below-threshold playback attempts; - retention settings, internal session checkpoints, and stable record identifiers used to make imports idempotent. Cover art requested through the application is cached on disk inside the container or host volume, in a size-capped directory. The cache holds only artwork fetched for the dashboard and can be cleared by removing that directory; it is not part of the SQLite database or its backups. These fields can reveal personal listening habits when combined, even when the media metadata is otherwise public. ## Navidrome credentials Credentials supplied through `NAVIDROME_URL`, `NAVIDROME_USER`, and `NAVIDROME_PASS` remain in the process environment and memory. Credentials saved from **Settings > Connections** or through the compatible fallback `/api/source/config` endpoint are encrypted at rest with AES-256-GCM. The per-installation key file, `secret.key`, is stored beside the SQLite database and must be backed up with it; restoring the database without that key leaves saved passwords unavailable until they are entered again. The settings APIs return configured URLs and usernames to administrator sessions, but never return saved passwords. Connection tests and `/api/diagnostics` return stable failure categories rather than upstream exception text. The diagnostics response contains aggregate connection, collector, and history counts but omits URLs, usernames, passwords, source IDs, and playback metadata; it is protected whenever either dashboard token is configured and is unavailable to viewer sessions. Protect the database, Docker volume, `.env` file, and backups as credentials. On Unraid, environment values entered in Docker Manager, including dashboard tokens, are also stored in its saved container XML and Docker configuration. Password masking only hides the input on screen; it does not encrypt those stored values. Protect saved templates and flash backups as credentials. The [Unraid template](unraid.md) configures Navidrome connections through the application after login, with saved passwords protected by the appdata key described above. Subsonic authentication uses token and salt query parameters. The application avoids logging upstream request URLs, but reverse proxies, network tools, and the Navidrome server may have their own access logs. Configure those systems so authentication query parameters are not retained or shared. ## Retention and data controls Listening records are retained permanently by default. Saving a finite retention policy of 1–360 days authorizes the service to delete older records during startup and periodic background maintenance. The settings page previews the affected records and asks for confirmation before saving a finite policy. Its separate **Apply now** action has its own preview and confirmation before running the purge immediately. That action is bound to the policy used for its preview; if another session changes the saved policy, no records are deleted until the preview is refreshed. Retention cleanup deletes rows but does not run SQLite `VACUUM`. Deleted pages remain inside the database file for SQLite to reuse, so the preview reports affected records and estimated deleted record payload without claiming that the file or operating-system disk usage will shrink. Per-user controls support JSON export, import, and deletion. Format v5 exports include each row's stable record ID, a SHA-256 fingerprint of its normalized contents, and the duration-quality claim used by detail views, and structured artist credits when available. These values let repeated imports distinguish duplicates from conflicting rows and keep reported, estimated, lower-bound, and missing durations consistent after restore. They are not credentials, but they remain part of the sensitive listening-history export and can link copies of the same exported record. Formats v1 through v4 remain importable; v3 keeps its signed confidence value and original comparison rules but cannot recover session/finalization evidence that it never stored, while v1 and v2 receive deterministic identities during import. Deleting a user discards that user's active in-memory sessions and suppresses writes already queued for those sessions. Playback activity collected after deletion starts a new session normally. Deletion from the application database does not remove exports or copies already present in backups or external storage. The database retains a history-import checkpoint and a deletion cutoff after deletion so history and playlist imports cannot restore older records. Their keys use SHA-256 digests of the source ID and username. The history checkpoint records the next offset, completion status, failure count, and next retry time; the deletion cutoff records the UTC deletion time. Neither value contains the cleartext username or listening metadata. Removing the application database also removes these markers. SQLite uses write-ahead logging. The database file, `-wal` and `-shm` files, volume snapshots, and backups can all contain the same sensitive data. Stop the application before taking a simple file-level backup, as shown in the [operations guide](operations.md#backup-and-restore). ## Browser and network behavior The daily-average preference is stored in browser `localStorage` as `navidrome-daily-average-mode`, with `calendar` (default) or `active` as its value. It is not sent to the server or included in shared URLs. The backend returns both calculations from the same authorized records; changing the display preference does not write listening data. Individual listening history exposes the same authorized timestamps, users, clients, sources, track metadata, and duration claims as existing detail views. Search terms are sent in an authenticated GET request and may appear in access logs; the page does not persist them in browser storage or its shareable URL. Pagination cursors encode a timestamp and a database row ID, are not access credentials, and never bypass viewer scope restrictions. Review highlights use existing records and do not capture additional personal data. Collection status returns only a state and a mixed-collection warning, without credentials or connection addresses. The collaborating-artist preference is stored in browser `localStorage` as `navidrome-artist-mode`. Its value, `combined` or `separate`, is sent with statistics requests and included in shared dashboard and review URLs. Missing artist lists may be fetched from the configured Navidrome server with `getSong`, using its existing credentials. Each collector keeps a bounded in-memory cache of track IDs and artist credits. These lookups do not contact MusicBrainz or ListenBrainz. The optional artist backfill command prints proposed artist metadata and affected record counts; treat its output as listening data. Applying it changes only missing artist lists in existing records. Language, theme, timezone, and reduced-motion preferences are stored in browser `localStorage`. The theme runtime recognizes four keys: `navidrome-theme-mode` and `navidrome-theme-palette` hold the current choices, `navidrome-theme` is read and maintained as a compatibility value for earlier releases, and `navidrome-theme-customizations` contains versioned color overrides keyed by built-in theme ID. These values contain only appearance identifiers and hexadecimal colors, not listening history or Navidrome credentials. Theme JSON import is read locally by the browser and is not uploaded; export creates a local download containing the selected preset ID and six colors. System theme mode reads the browser's `prefers-color-scheme` media query and cannot read or change operating-system settings. Theme preferences are not sent to the server. The selected timezone is sent with statistics requests to calculate local date and hour buckets. Frontend assets are served by the application. Normal dashboard use does not load JavaScript or CSS from a public CDN, and the project does not include usage analytics or telemetry. The published container disables Uvicorn request access logs because application URLs can contain usernames, source identifiers, dashboard filters, and shareable artist or album detail names. Operators using another application server or reverse proxy should apply an equivalent logging policy. Client detail views are not shareable URLs. The browser sends the client name in a POST body and keeps that detail state out of browser history and query strings. Client names remain visible in authorized dashboard responses and on-screen charts because they are part of the stored listening data described above. `STATS_API_TOKEN` grants administrator access to all stored listening data and configured connection identities, settings and connections, and export, import, retention, and deletion controls. `STATS_READ_ONLY_TOKEN` grants a viewer session access to the dashboard, Listening Review, and related cover art. Viewer sessions cannot open settings or invoke connection, source, retention, privacy, deletion, OpenAPI, or protected metrics routes. Operators may bind all viewer sessions to a single stored source and/or username with `STATS_READ_ONLY_SOURCE_ID` and `STATS_READ_ONLY_USERNAME`. The backend applies that scope to statistics queries and rejects conflicting query parameters or POST bodies. A username restriction also removes server options with no matching history and prevents cover-art requests to those sources. The fixed username is not added to cover-art URLs. Fixed controls are hidden in the browser, but enforcement does not depend on the frontend. Administrator, viewer, and ListenBrainz ingestion tokens must use separate random values. The application validates this at startup and stops before opening the database when two configured tokens match. Without either dashboard token, dashboard data and all application APIs, including administrative and deletion operations, are available to anyone who can reach the service. The application does not provide TLS; use a trusted network or an HTTPS reverse proxy with appropriate access control. The ListenBrainz-compatible receiver is disabled unless both `LISTENBRAINZ_INGEST_TOKEN` and `LISTENBRAINZ_INGEST_USERNAME` are configured. It accepts the token in the `Authorization` header and checks it before reading the request body. Permanent submissions store the configured username and source plus track metadata and timestamps, including submitted artist names and corresponding IDs when available; `playing_now` messages are not stored. Listening duration and transcoding status remain unset. Exact retries are deduplicated by source, username, timestamp, recording, and release identity. Other collection methods keep separate records. ## Operator checklist - Tell affected users what listening activity is collected and why. - Set `STATS_API_TOKEN` and, when needed, a distinct `STATS_READ_ONLY_TOKEN` when access is not limited to a trusted network. - Give the administrator token only to people who may change settings and delete data; bind viewer scope when a viewer should see only one server or user. - Restrict access to `.env`, SQLite files, Docker volumes, exports, and backups. - Review proxy and Navidrome logs for authentication query parameters. - Choose a retention period and backup policy appropriate for the deployment. - Use synthetic or redacted data in issues, logs, screenshots, and test fixtures.