Privacy
What Kingfisher collects — and what it does not.
Kingfisher is local-first. Your studies, repertoire, training, notes, recent work and preferences are stored in the browser profile or in the desktop application's local profile directory. Signed out, nothing about you or your work leaves your machine; signed in to the optional account, a copy of your authored work is kept there for your other devices. When you use a feature that talks to a third party — Lichess, Chess.com, the reference-data mirror — this page tells you what is sent and why.
Short version
- No account required. Everything works without signing in, and signed out Kingfisher does not know who you are. There is no mailing list and no email capture. An optional account keeps your work on several devices; Optional account says exactly what it stores and where.
- Page views and load times are counted, and that is all. The website uses Vercel Web Analytics and Vercel Speed Insights. For each page view it records the page path, the referrer, and what the request already carries — the country the connection comes from, the browser and operating-system family, the device class; for each page load it records the timings your browser already computes (the Core Web Vitals) with the connection type and device class. Neither sets a cookie or stores an identifier on your device; Vercel derives a per-day visitor hash on its side and discards the address. Query strings and fragments are stripped before anything is sent, so a position in a URL never leaves your browser. Nothing about your chess — positions, studies, games, engine lines — is ever part of either. There is no error-reporting service, no session replay, no “is the user still here?” ping. The Mac application loads none of this.
- Third-party services are asked for, never assumed. Lichess and Chess.com only see requests that come from a feature you opened, signed in or queried; the reference mirror only fetches chunks when a pack needs them; the account service (Supabase) and Google are reached only when you sign in or are signed in. Each call is described in Network requests.
- Feedback is user-initiated. The in-app Feedback button is a deliberate channel you open yourself. Nothing leaves the browser until you click Send. See Feedback for what the submission carries and where it goes.
- No cookies. The web build does not set any cookie. Application state lives in
localStorageand IndexedDB, scoped to the origin. - No advertising. Nothing on the page or in the application is, was, or will be an advertisement.
- No client-side fingerprinting. No canvas fingerprint, no font enumeration, no hardware concurrency probes, no timezone sniff, no IP-to-country map, no “we know its you because of your machine.”
Optional account
You may create an account with an email address and password (and with Google where the sign-in page offers it). Then, and only while you are signed in:
- Stored in your account: your email address (and, with Google, the name Google supplies), and a copy of your authored work — studies and chapters, unsaved analysis, repertoires, training progress, preparation, opening files, journal and review, saved searches, pinned lines, stored engine results, puzzle history, the positions you analysed — with your display, board and engine preferences.
- Only if you switch it on: your newest 2,000 games in My games, and the newest 2,000 across the collections you made in this browser with their names, when you turn on Sync My games and collections on this device on the Account page. It is off by default, and set separately on each device.
- Never stored there: SQLite collections held by the companion, reference packs, engines, API tokens and keys, and settings that describe your machine.
- Who can read it: no other account. The database itself refuses every other account (row-level security), not just the application. As with any hosted service, the person who runs the Kingfisher project has administrative access to the database; it is not read, analysed or shared, and it is used only to keep the service running.
- Where: Supabase, which runs the account database, in its Seoul, South Korea region (
ap-northeast-2). - Account emails — confirming your address, resetting a password — are sent through Resend from accounts@kingfisherchess.app. Resend receives your email address and the message, nothing else.
- Your session is kept in local storage, not in a cookie.
- Deleting the account (Account → Delete account) removes it and every copy of your work stored in it, at once. Work on your own device stays there. Nothing on a device is uploaded the first time until you say so.
Local storage
User-authored data — your studies, chapters, repertoire moves, training items, model games, recent positions, notes, the display name you picked, and preferences — is stored in the browser or in the desktop profile. It is held under the application's own storage key and never read by another origin. The structure is documented in AGENTS.md and the schema is versioned.
A localStorage entry holds the small key/value preferences (theme, piece set, board theme, whether move sounds play, last-active route). The contents are visible in the browsers developer tools and are an inert JSON object with no PII beyond what you yourself typed (a study name, a repertoire name, your display name). In Safari, dismissing the note that Safari may delete this site’s data writes the time you dismissed it, kingfisher.storage-risk.dismissed, so it is not said again for a month.
The landing page at / reads two further localStorage entries on this origin, so that a browser which already uses Kingfisher is offered Continue in Studio rather than the introduction: kingfisher.studio.visited, which the application writes when it opens, and kingfisher.landing.auto-open-studio, which is written only if you tick Open the Studio straight away next time and is removed when you untick it at /?stay. A first visit writes neither: there is nothing to read, and the landing stores nothing until the application has been opened. Both are flags on this origin, not identifiers, and neither leaves the browser.
The bulk of your work is in IndexedDB. IndexedDB is origin-scoped, so a profile on kingfisherchess.app is not the same database as one on localhost, in another browser, or in another browser profile. Signed in to the optional account, your work follows you between them. Signed out, it does not: the way to move it is the Settings → Database → Export backup… / Choose backup to restore… flow, which produces and consumes a versioned JSON file under your control.
Collections you make on the Databases page are IndexedDB databases of their own in the same browser, listed in a small one named kingfisher-collections. They are sent to your account only with the switch above on, and a backup carries them only when it includes games.
Reference cache
When you use the Explorer or a pack query, the application caches the response bytes in IndexedDB so a second query of the same shard does not redownload. The cache is byte-budgeted and is cleared on a fixed schedule. The cached bytes are not attributed, are not exported with your work and are not considered user data. They are technical infrastructure for the product.
The cache is the same origin-scoped database as everything else; it does not sync.
Network requests
The web and desktop builds do not phone home. The only outbound network calls the application makes are the ones the product needs:
- Lichess (
lichess.org), used when:- you sign in to Lichess from Kingfisher (OAuth with PKCE, no scopes requested; the token is kept in memory for the session, in
localStorageonly while Remember is on, and never leaves the device); - you ask for games from a Lichess username via Settings → Accounts → Add an account → Lichess, or via Databases → Download online games. The username and the games it returned are recorded in your local collection; nothing else is sent. A download keeps no record of the account.
- you query the Lichess-hosted Explorer or tablebase. Each call is the literal question you asked — a position, or the board state for a tablebase probe — and Lichess's answer. Nothing about your studies, your repertoire, or your account on Kingfisher goes with it.
- you turn on Lichess cloud in the engine panel. While it is on, each position you visit is sent to
lichess.org/api/cloud-evalto ask for the analysis Lichess has stored for it. It is off at the start of every session and stays off until you turn it on.
- you sign in to Lichess from Kingfisher (OAuth with PKCE, no scopes requested; the token is kept in memory for the session, in
- Chess.com (
api.chess.com), used only when you ask for games from a Chess.com username via Settings → Accounts → Add an account → Chess.com or Databases → Download online games. The username and the games it returned are recorded in your local collection. Chess.com never sees a Kingfisher identifier or any other identifier of yours. - Wikimedia (
wikidata.org,commons.wikimedia.organd Wikimedia's image hosts), when a preparation report or player page is opened for a titled player the roster names: the player's Wikidata item is asked for its photograph, and Commons for the file and its credit. The photo is shown with its author and licence, held in memory for the session, and never stored. Nothing about you or your work goes with the request. Settings → Database → Player photos turns this off, and then nothing is asked of Wikimedia. - The public data mirror at
mardakurt.github.io/kingfisher-datafor reference-pack manifests and chunks. Every chunk is verified against the manifest's SHA-256 before it is used. A failed verification is reported and the bytes are discarded. - unpkg.com (
unpkg.com/stockfish@18.0.8), used only when you choose Stockfish 18 (full network) in the engine selector: the 113 MB evaluation network is fetched from that npm package mirror the first time and kept by your browser afterwards. The request carries no identifier of yours, and the browser refuses the bytes unless they hash to the SHA-256 recorded in Kingfisher (docs/ENGINES.md). The standard Stockfish 18 build is served from this origin and needs nothing else. - A service you configure yourself. If you give Settings → Assistant an endpoint, the evidence you ask it to explain is sent there, and so is a score-sheet photo you choose to have read. Nothing goes to it unless you set it up and ask, and Kingfisher does not choose or know what that service is.
- The account service — Supabase (
*.supabase.co), only when you sign in or are signed in: signing in and out, and copying your authored work to and from your account, including a live connection that tells this device when another one changed something. Signed out, nothing is sent there. - Google (
accounts.google.com), only when you choose Continue with Google: Google asks you to approve sharing your email address and name with Kingfisher, and nothing else is requested. - The application's own origin for the static assets (the standard Stockfish WASM, piece art, the marketing/landing assets, the app code itself).
The Content-Security-Policy in vercel.json names these hosts, but it also allows any https: host, because two of the calls above choose their host at run time (the full-network engine's mirror and a service you configure). So it is the code, not the policy, that limits where requests go — and the code makes no call beyond this list. Scripts, styles and frames are restricted to this origin. The desktop companion's loopback server is a separate trust boundary with its own authentication.
Cookies and trackers
The web build sets no cookies in the strict sense: no Set-Cookie response header, no document.cookie writes, no httpOnly session. The application uses localStorage and IndexedDB instead. The browser may still hold its own state (service worker cache, IndexedDB) which is required for the product to work across reloads.
No advertising tag and no cross-site tracker is loaded. The only measurements are Vercel Web Analytics and Speed Insights, described above, served from this origin. A network panel open during a normal session will show this origin (including /_vercel/insights/view, the page-view beacon, and /_vercel/speed-insights/vitals, the load-timing beacon), Lichess (if you have signed in or queried Lichess), Chess.com (if you have queried Chess.com), the data mirror (if you have used a reference source) and the account service (if you are signed in). Nothing else.
Hosting
The web build is hosted on Vercel. Vercel sees every request the way any hosting provider does, and the request log will contain the IP address you connected from, the URL you requested and the user agent your browser sent. Vercel's own data-handling is described in their privacy policy; the Kingfisher project does not put anything additional in those logs. The optional account is kept by Supabase (see Optional account), and in-app feedback passes through this site's own endpoint (see Feedback); nothing else runs on a Kingfisher server.
Account status
A Kingfisher account is optional. Without one there is no sign-in and no profile on a server; with one, what it stores is described under Optional account above. The local profile that holds a display name and your player names is one row in your own IndexedDB or desktop profile directory, and syncs only when you are signed in. The Accounts section of Settings records Lichess and Chess.com usernames so their games can be pulled into your local collection; that linkage lives in your IndexedDB and desktop profile, and is the only thing that connects a Lichess username to a Kingfisher install.
Sync status
Signed in, your work is copied to your account and follows you to every device you sign in on. Signed out, it lives on the machine you created it on and Kingfisher pushes it nowhere. Either way you can move it yourself: Settings → Database → Export backup on one machine and Choose backup to restore… on the other. The backup file is portable JSON, held on disk under your control.
Feedback
The in-app Feedback dialog sends a single submission only when you press the Send feedback button. Nothing is uploaded automatically, and the form does not open a connection on its own.
The submission carries:
- The category you picked (one of five).
- The message you typed, up to 4000 characters.
- The current board position (FEN) — only when you tick Include current position. The default is off.
- A short technical-information block (app version, surface, browser, viewport, storage state) — only when you tick Include technical information and preview it before sending.
The submission never carries your games, studies, chapters, repertoire, training items, notes, preferences, Lichess or companion credentials, or filesystem paths.
Where it goes: the submission is sent to this site's own server, never from your browser to anyone else, and the server forwards it. Today it forwards it to ntfy.sh, a public notification service, under a topic only the maintainer knows, where the maintainer reads it; it is also written to the server log. The server can instead be set to open an issue in a private GitHub feedback repository (with a token scoped to that repository alone), or to keep submissions in its log only — in which case the dialog also offers an “Open GitHub feedback” button that opens a pre-filled issue in a new tab. If the destination changes, this paragraph changes with it.
The endpoint enforces same-origin requests, a 64 KB body ceiling, a per-IP rate limit, a minimum form-fill time, and a honeypot field the dialog never fills. The renderer never sees the GitHub token. The fallback link is the user's explicit choice, not an automatic redirect.
Children
Kingfisher is not directed at children. Signed out, it collects nothing from anyone; the optional account stores an email address and the work its owner syncs, and Kingfisher does not knowingly hold an account for a child.
Changes
If a future Kingfisher change affects this policy, the change will be listed in CHANGELOG.md and this page will be updated before the change ships.
Contact
To delete your account and everything stored in it, use Delete account on the Account page; nothing else needs to be asked for. There is no Kingfisher-controlled inbox for privacy requests. For a security issue, see Security. For a non-security question, open an issue on GitHub.