Reliability and Privacy
What Kunai stores locally, what it never sends, how it limits third-party traffic, and how diagnostics stay redacted by default when you report a failure.
Kunai is designed to keep playback useful when providers, networks, Discord, or local files behave imperfectly. The default rule is simple: preserve the user's current context, keep durable user data safe, and make failures explainable.
What Kunai Protects
- Watch history, lists, playlists, queue sessions, notifications, followed titles, and completed download records are durable user data.
- Stream URLs, provider caches, recommendation caches, release schedules, source inventories, provider health, and trace rows are disposable cache data.
- Startup maintenance may prune expired cache rows. It must not delete durable history, playlists, download jobs, lists, follows, or notifications.
Recovery Behavior
Playback recovery is conservative:
recoverrefreshes the current playback intent after failure evidence.replayorrestartstarts the current episode from the beginning.- a failed fresh lookup can keep the current cached stream if it is still usable.
- provider fallback should explain what failed and what recovered.
Queue recovery is explicit. If a previous session crashed with pending queue items, Kunai can show a recoverable queue notice. Restoring that notice moves pending items into the current session, but it does not autoplay them.
Offline And Downloads
Downloads stay local-first:
/downloadsmanages queued, running, failed, and retryable jobs./libraryand/offlineshow completed local media.- completed artifacts are validated before Kunai marks them ready.
- when
ffprobeis available, Kunai stores local duration metadata after validation. - offline rows may show duration, resume position, percent watched, and watched state from local history.
Opening the offline library must not call providers. Repair, re-download, and online playback are explicit user actions.
Diagnostics Privacy
Diagnostics stay local unless you export or share them. Exported bundles are redacted and should not include raw stream URLs, cookies, authorization headers, signed query values, or private home-directory paths.
The fastest way to inspect reliability state is:
/diagnosticsFor a shareable local bundle:
/export-diagnosticsSupport bundles include category sections such as playback, provider, cache, presence, download, and offline. Each section includes the latest operation so a report can show whether a provider fallback happened, Discord failed to clear, or a download artifact was validated.
Usage analytics
Anonymous usage analytics are yours to turn on or off at any time, in Settings → General → Usage analytics.
On a fresh install, setup asks you directly. It recommends turning analytics on
and pre-selects that option; it counts unique installs and versions, never
people. Nothing is enabled until you confirm it on that screen. Skipping
setup leaves analytics off: no default, accept-all, or non-interactive path
can turn it on for you. Existing installs get one non-blocking recommendation
explaining the payload; that notice does not enable analytics, create an id, or
send anything. No analytics requests run before you enable it, in a
non-interactive terminal, or when DO_NOT_TRACK=1 or CI=true.
When on, Kunai sends at most one tiny ping per day:
{
"installId": "<sha256-hex>",
"version": "<semver>",
"os": "<platform>",
"arch": "<arch>",
"ts": 0
}installIdon the wire issha256of your local install id, not the id itself. The id is generated on your machine, stored only in your config, and never transmitted, so the value above is a 64-character hex digest, never a UUID- Never titles, queries, providers, stream URLs, or file paths
- Your IP address is never read. Not collected and discarded; the ingest has no code path that reads a client address at all, so there is nothing to log, rate-limit on, or hand over
- Turning it off deletes the install id from disk. The id exists only while analytics is enabled
- Settings → General → Rotate install id replaces the id with a fresh random one while you stay opted in. Earlier pings cannot be linked to the new id. The entry appears only while analytics is enabled, because disabling already clears the id
DO_NOT_TRACK=1andCI=trueblock sends regardless of the setting. A value of0,false, ornodoes not block- Without an interactive terminal (piped or scripted output), analytics stays off, because the notice could not be shown
- The ingest re-hashes that digest with an HMAC before storing it, alongside your platform, architecture, and version. It does not read your IP address at all
- Raw rows are deleted after 35 days. A permanent table keeps one hashed row per install with a first-seen date, which is what a lifetime count costs. Installs silent past the retention window fold into a retired count, and one that returns is counted again
- Public docs show yesterday's active installs, installs first seen each day,
a lifetime total, and version / platform / architecture breakdowns. The
chart's platform view splits each day into Linux, macOS, and Windows where
each is large enough to name. The
lifetime total counts installs ever observed: rows retired for long
silence stay counted, so one that returns is counted again. It is not a
unique-install figure. Any group with fewer than 5 installs is
reported as
other. This is per-breakdown small-cell suppression, not a joint anonymity guarantee. Those numbers can be inflated by abuse; they cannot expose what you watch. See the live usage analytics page. - The page also shows npm's own download counter for the package, on a
separate card because it is a different kind of number: every tarball fetch
counts (reinstalls, upgrades, CI), installs made through
install.shor GitHub binaries never reach it, and npm's days are UTC. It is fetched from npm when the page revalidates, not sent to or stored by Kunai's ingest. - A day ends at midnight IST (18:30 UTC) from 15 September 2026. Days before that end at midnight UTC, and 14 September 2026 is a shorter changeover day whose count reads low. The "updated" time on the analytics page is shown in your own clock; the day totals are not re-cut to your local midnight, because only daily totals are kept and the individual pings behind them are not.
Preview the exact JSON with /analytics show.
If you turn analytics on, pings go to analytics.kunai.kitsunekode.in. Point
them somewhere else by setting a non-empty KUNAI_ANALYTICS_URL or
analyticsEndpoint. That address must use https; http://localhost, 127.0.0.1, and [::1]
are the only exceptions, for testing your own ingest. Any other
cleartext address is refused and nothing is sent at all; your pings are never
silently rerouted to the built-in URL. An empty analyticsEndpoint (the shipped
default) means “use the built-in URL”, not “disable”. Turn analytics off in Settings, or set
DO_NOT_TRACK=1, to send nothing. Leaving analytics off, running without a
terminal, or setting DO_NOT_TRACK means nothing is ever sent, whatever the
endpoint says.
Release confidence
Contributor release checks (fmt, lint, typecheck, test, build) live in Contribute. Live provider, Discord, and real mpv checks stay manual.
Last updated on