Privacy Policy
Last updated: 2026-08-06
This is a draft prepared for review. It has not been reviewed by a qualified lawyer.
By default, your training data never leaves your device. Not encrypted-and-uploaded — not uploaded. There is no copy of your practice history on our servers unless you choose to create an account and switch sync on.
The trainer stores everything in your browser's own database and keeps working with the network off.
Contents
- What "local-first" means
- What is stored on your device
- What is sent if you create an account
- What we never collect
- Website analytics
- Cookies and browser storage
- Who else touches the data
- Export and deletion
- Your rights (UK/EU GDPR)
- How long we keep things
- Security
- Children
- Changes to this policy
- Contact
1. What "local-first" means
The CountLab trainer runs inside your browser. Your progress is written to IndexedDB — a database the browser provides, stored on your own machine — using a library called Dexie. There is no background upload. After the first visit the app keeps working offline, because everything it needs is already on your device.
What that means in practice:
- We cannot see your practice history. Not "we choose not to look" — there is nothing to look at, because it was never sent.
- Your data is tied to that browser on that device. Using a different browser or a different computer starts fresh.
- If you clear your browser's site data, uninstall the browser, or use private/incognito mode, your progress goes with it. We cannot recover it, because we never had it. Export is how you keep a backup.
2. What is stored on your device
The browser database holds:
- Your decisions. One row per hand you play: the skill it belongs to (for
example
bs:hard:16:10), the dealer upcard, the action you chose, the correct action, the EV cost of the difference, how long you took in milliseconds, a timestamp, which session and rule set it belonged to, and whether you have flagged it for review. - Your skill states. One row per decision cell: the spaced-repetition state (difficulty, stability, when you last reviewed it), a short list of recent attempts, your curriculum stage, an EV-impact estimate, and when the cell is next due.
- Sessions — start and end times and the random seed used to deal, which is what makes a session reproducible.
- Rule sets you have configured or saved, and the strategy tables computed for them (a cache — regenerable, and left out of exports for that reason).
- Preferences and profile — drill timing, your active rule set, and a marker that you have finished the first-run walkthrough.
- Session goals you set, while they are open.
- Sync bookkeeping, only if you create an account and sign in: a device identifier this app generates for itself, a refresh token, sync cursors, and the last sync time. If you never sign in, none of these rows exist.
None of this is sent anywhere by the app on its own.
3. What is sent if you create an account
An account is optional, and local-only remains the default: the trainer is fully usable, forever, without one. It exists for one reason — to sync progress between your own devices. It unlocks no features and carries no plan. If you never create an account, nothing in this section applies to you, and nothing about your training exists on our server.
The tables below are transcribed from the server's database definition, not summarised from memory. If you do create an account, this is the complete list of what is stored on our server — nothing else.
| Field | What it is |
|---|---|
| The address you sign up with, normalised to lower case. This is the only contact detail we hold. | |
| password_hash | A scrypt hash of your password, stored together with the salt and cost settings it was made with. We do not store the password itself, and the hash cannot be turned back into it. See section 11 for what that means if you forget it. |
| id, sync_seq, created_at, updated_at | An internal account identifier, a counter used to order sync updates, and timestamps. |
There is no name field, no address field, no date of birth, no payment field, and no plan, subscription or entitlement field — not "left blank", but absent from the database. Your email address is the only thing here that identifies a person, and the only contact detail we hold. An account is a sync key, not a licence: nothing on our server records what you are entitled to use.
| Field | What it is |
|---|---|
| device id, last_seen_at | An identifier your own app generates for each device (not a hardware ID, not an advertising ID), and when it last synced. |
| token fingerprints, expiry and timestamps | SHA-256 fingerprints of your access and refresh tokens — never the tokens themselves — plus when a session was created, last used, expires, and whether it was revoked. |
| Field | What it is |
|---|---|
| skill_id, category, stage | Which cell (e.g. hard 16 vs 10) and where it sits in the curriculum. |
| fsrs | Scheduling state: difficulty, stability, last review time. |
| recent_attempts | A short rolling list: correct or not, latency in milliseconds, and when. |
| ev_impact, due_at | How costly this cell's errors are, and when it is next due. |
| client_updated_at, version, server_seq | Bookkeeping used to merge changes from two devices without losing practice. |
| Field | What it is |
|---|---|
| skill_id, period | The cell, and the calendar month in YYYY-MM (UTC). |
| attempts, correct | How many attempts that month and how many were right. |
| ev_cost_total | Total EV cost of the mistakes in that bucket. |
| latency_buckets | A histogram of response times — counts per band, not individual timings. |
| device_id, client_updated_at, version, server_seq | Bookkeeping, as above. |
Your individual hands never leave your device. There is deliberately no table on the server for raw decisions. The monthly rollups above are the aggregate the sync feature needs; the hand-by-hand history stays local. This is enforced in code — the sync protocol has no message that could carry a raw decision, and an automated test fails the build if a migration ever adds such a table.
Three things worth knowing before you create one
- Resetting your password emails you a link, through Resend. The address on your account and a one-time link are the only things Resend sees; it never receives your password, your practice data, or anything else about your account. Section 11 says more, in the security context.
- Signing up tells us an email address exists. If you try to register with an address that already has an account, we say so, which means someone who guesses your address can learn that you have an account here. Signing in leaks nothing: a wrong address and a wrong password produce the same answer, in the same time.
- Sync is a separate switch. Creating an account does not upload anything on its own; the app pushes only the skill states and monthly rollups above, and only once sync is on.
Why we are allowed to hold it. The lawful basis for everything in this section is performance of a contract — you asked for an account and for sync between your devices, and this is the minimum needed to provide them. Nothing in this section is used for advertising, profiling, or any purpose other than making sync work. The full list of bases is in section 9.
4. What we never collect
- No money, ever. CountLab has no wagering and no real currency. We do not ask how much money you have, do not record any bankroll in currency, and hold no record of any gambling you do.
- No sale of data. We do not sell, rent, or trade your data. There is no version of this business where we do.
- No advertising trackers. No ad networks, no advertising pixels, no remarketing tags, no data brokers.
- No cross-site tracking, no fingerprinting, no session recording, and no third-party analytics beyond the one tool described below.
- No casino or operator integrations. No affiliate links, no referral revenue, no operator partnerships — so there is nobody to share data with even in principle.
- No unnecessary personal details. No name, no address, no date of birth, no phone number, no payment card details held by us.
5. Website analytics
We use Cloudflare Web Analytics on the CountLab website to see whether the site is working and where visitors come from. Enabled at launch
What it is, accurately:
- Cookie-less. It does not set cookies and does not store or retrieve anything on your device to identify you.
- No cross-site tracking and no per-person profile. It does not follow you to other websites and does not build a profile of you.
- Aggregate only. It reports counts: page views and visits, referring sites, country, browser and device type, and page-load performance. We see totals, not people.
- Limited by design. The data is sampled over longer time ranges and retained for around six months. It supports no custom events, no funnels, and no individual-level retention analysis — and we are not adding a second tool to work around that today.
We do not use Google Analytics, Meta pixels, heatmaps, or session-replay tools. If we ever add in-product analytics, this policy is updated first and the entry will say exactly what is measured.
6. Cookies and browser storage
We use no tracking cookies. That is why there is no cookie banner — and it is the honest reason, not a technicality. Under UK/EU rules, consent is required for storage that is not strictly necessary for a service you asked for. Our analytics sets nothing at all, and the only storage we use is the app's own database, which exists solely to deliver the trainer you opened.
What the app does store on your device:
- IndexedDB — your training data (section 2). Strictly necessary: without it the trainer cannot remember anything, and it is on your machine, not ours.
- Cache storage / service worker — the app's own files, so it loads fast and works offline.
- Sign-in tokens, only if you sign in, kept in IndexedDB rather than cookies. The short-lived access token is held in memory only and is never written to disk.
You can clear all of it at any time through your browser's site-data controls.
7. Who else touches the data
Only the suppliers needed to run the site, each acting on our instructions under a data processing agreement:
- Cloudflare — hosting and content delivery for the website and app, plus the analytics described above. As with any website, connection information such as your IP address is processed by the hosting layer to deliver pages and to block abuse.
- Neon — the managed Postgres database that stores account and sync data, if you create an account. If you have not created one, nothing of yours reaches it.
- Resend — sends the one email a password reset generates. It receives the email address on the account and the one-time reset link; it never receives your password, your progress, or anything else CountLab holds.
- A payment processor — only if and when paid plans launch. Card details would go to them and never to us. Not in use
On IP addresses specifically: when you try to sign in or register, our API uses the address Cloudflare reports for your connection, together with the email address being tried, purely to count attempts and slow down guessing. It is held in memory for the length of the counting window, it is never written to our database, and it is never attached to your account or to anything about your practice. Nothing else in the product looks at your IP address at all.
We will also disclose data where the law genuinely requires it. We have never received such a request; if that changes, we will tell affected users where we are permitted to.
The site is served by Cloudflare from its global edge network, so the copy of these pages you receive is delivered from a location near you rather than from a single country. If you have an account, your account and sync data is held in Neon's managed Postgres. Data may therefore be processed outside your country, including in the United States. Where that happens it relies on the safeguards those providers have in place. Cloudflare certifies to the EU–US, Swiss–US and UK extension of the Data Privacy Framework and incorporates the European Commission's standard contractual clauses in its data processing addendum. Neon (now part of Databricks) provides a data processing agreement that likewise incorporates the 2021 standard contractual clauses. Resend (Plus Five Five, Inc., based in the United States) is certified to the EU–US Data Privacy Framework and its UK extension, and its data processing agreement likewise relies on the standard contractual clauses for transfers from the EEA.
8. Export and deletion
Export Built
In the app's Progress area you can export:
- Decisions as CSV — every hand you have played, one row each.
- Everything as JSON — decisions and skill states together. Cached strategy tables are excluded because they are regenerable from your rule set.
Both are plain files saved by your browser. No account, no request to us, no waiting.
Deleting local data Built
Your data is on your device, so you delete it there: clear site data for CountLab in your browser's settings, and the database goes with it. We are not involved and cannot undo it.
Deleting a server account
If you have an account, deleting it removes your account row and, by database cascade, every device record, sign-in session, skill state and monthly rollup attached to it. There is no soft-delete, no thirty-day grace period and no archived copy: the rows are gone, immediately and irreversibly, and signing in with those credentials afterwards fails exactly as it would for an address that never registered. Deleting the server copy does not touch the copy on your own device — your practice history stays in your browser, and you can keep training or create a fresh account.
If you have not created an account, there is nothing on our side to delete, and section 8's local instructions above are the whole of it.
9. Your rights (UK/EU GDPR)
Who the controller is. CountLab is run by one person — an individual sole trader based in the United Kingdom, not a company. That person is the data controller for the limited processing described above, and can be reached at countlab@gmail.com.
No postal address is published, and that is a decision rather than a gap. A sole trader with no registered office has no business address to publish that is not a home address, and putting a private home on a public page is a worse privacy outcome than the disclosure buys. What the law asks for is an identifiable controller and a contact channel that works: the controller is named above as a UK sole trader, and the email address above is read by that person. Every right in this section can be exercised through it, and most of them can be exercised without it at all. If you would rather raise something with a regulator than with us, the Information Commissioner's Office takes complaints directly.
If you have not created an account — which is the default, and stays the default — the only personal data processed for us is what any website necessarily involves: connection information such as your IP address, handled by the hosting layer (section 7), and the aggregate, cookie-less visit counts in section 5. Neither identifies you, and neither is joined to anything about your practice, because your practice never leaves your browser.
If you have created an account, we additionally hold exactly what section 3 lists — your email address, a scrypt hash of your password, and the skill states and monthly rollups you have chosen to sync. That is the whole of it.
Lawful bases we rely on:
- Performance of a contract — holding your email address, password hash, and synced progress so that the account and sync features you asked for can work.
- Legitimate interests — aggregate, cookie-less visit statistics to understand whether the site works, and security measures such as rate limiting. We have weighed these against your interests; the data is minimal and identifies nobody.
- Legal obligation — where we must keep records, for example tax records once paid plans exist.
Your rights. You can ask for access to your data, correction of it, erasure of it, a portable copy, restriction of processing, or object to processing based on legitimate interests. Because the trainer is local-first, you can exercise access, portability, and erasure yourself and immediately — export and clear, above — without asking anyone's permission. If you have an account, deleting it in the app is a complete erasure of everything we hold (section 8), again without asking anyone. For anything those controls do not cover, write to countlab@gmail.com and a request will be answered within one month, which is the deadline the law sets.
If you are unhappy with how we handle a request, you can complain to your data protection authority — in the UK that is the Information Commissioner's Office (ico.org.uk); in the EU it is your national supervisory authority.
10. How long we keep things
- Local data — until you delete it. There is no expiry and no automatic clean-up by us.
- Account and synced data — until you delete your account. We do not expire inactive accounts or delete dormant data on our own initiative; deletion is your decision, and it is one click in the app.
- Sign-in sessions — an access token is valid for 15 minutes and a refresh token for 30 days, and both are revoked when you sign out. To be precise about what "revoked" means here: the row itself is kept, holding only the SHA-256 fingerprints and timestamps in section 3, because recognising a stolen token being replayed requires remembering that it once existed. Those rows accumulate while an account is active and are removed when the account is deleted. There is no separate expiry job today; adding one is known work, not a hidden gap.
- Password-reset tokens — stored the same way, as a SHA-256 fingerprint with a 30-minute expiry, and kept rather than deleted once used or expired, for the same replay-detection reason as sign-in sessions above. Removed when the account is deleted.
- Rate-limiting counters — the IP address and email combination in section 7 lives in memory only, for the length of the counting window, and is never written down.
- Website analytics — aggregate figures, retained by Cloudflare for roughly six months.
11. Security
The measures actually in place: passwords are stored as scrypt hashes — a deliberately slow, memory-hard function, at parameters taken from the OWASP Password Storage guidance — and never in readable form; sign-in tokens are stored only as SHA-256 fingerprints, so a copy of our database contains no usable token; refresh tokens are grouped into families so that replaying a stolen token revokes the whole family at once; sign-in and registration attempts are rate-limited; and everything travels over HTTPS. The short-lived access token stays in memory on your device rather than being written to disk.
No system is perfectly secure, and we do not claim otherwise.
If you forget your password
Use the reset link on the sign-in page. We send one email, through Resend, to the address on the account: a link that works once and expires after 30 minutes, stored on our server only as a SHA-256 fingerprint — the same protection sign-in tokens get, above. Completing a reset signs the account out on every device it was signed in on. Asking for a reset answers identically whether or not that address has an account, the same protection signing in already gives (section 3). We hold no recovery question and no second address: the link to the account's own inbox is the only path back.
Concretely: while a password is forgotten and before it is reset, the synced copy of your progress cannot be reached, by you or by us. What is not lost is the copy in your own browser — that is the primary copy and always has been, and it needs no password. Carry on training with it and export it (section 8) while you sort the password out.
12. Children
CountLab is for adults only — 18+, and 21+ where that is the legal gambling age. It is not directed at children, we do not knowingly collect data from anyone under 18, and if we learn that we have, we will delete the account and everything attached to it. To report a concern of this kind, email countlab@gmail.com.
13. Changes to this policy
If what we do changes, this page changes first — before the new behaviour ships, not after. The "last updated" date at the top always reflects the current version. For significant changes we will post a notice on the site, and email account holders.
14. Contact
Email countlab@gmail.com for anything in this policy, including any of the rights in section 9. It reaches one person directly, so a reply may take a few days.
You do not have to wait for a reply to exercise any right in this policy: your training data is in your own browser, and section 8 shows how to export it and how to delete it yourself.
See also the Terms of Service.