holdtab

What this site knows about you

Short version: the board you build never leaves your browser; what is kept about you is what Google (or Steam) hands over when you sign in, your address if you asked for the weekly note, and, if you play Your call, your holdiks, their journal and, once a qualifying Steam account is connected, a public profile. Analytics do not load at all unless you say yes. The long version is below, and it is specific because a vague privacy page is not worth reading.

Last updated 2 October 2026. Written by reading this site’s own source code; not reviewed by a lawyer.

Signing in

The board sits behind a sign-in. Every address except this page, the terms and the sign-in screen itself sends you to sign in first.

Besides Steam's own sign-in (the Steam part below), the way in is “Continue with Google”. Google is asked for nothing beyond the basic profile that button carries: no contacts, no calendar, no Drive, no permission to post anywhere on your behalf. Google returns a signed token, and that token is traded for a session with Supabase, which is where sign-in for this site lives.

What is kept about you is exactly what that token carried:

  • a Google account identifier;
  • your email address;
  • your name;
  • the web address of your Google profile picture.

Those four are read to put your name and picture in the top bar and your address in your own menu and settings, and to open the Your call account described below.

The session is held in cookies. It is what keeps you signed in, the board does not work without it, and so it is not something the site offers to switch off. It is refreshed while you are using the site and dropped when you sign out.

On the server side that record is one of three things the database holds about a person; the mailing list is the next section, and Your call the ones after it. Everything else there is match data (live games, drafts, series scores, market prices, the notes the draft analyst writes), and not one of those tables has a column for a visitor.

Asking for the weekly note

At the foot of the page there is one field, and it is the only thing this site asks a reader to hand over rather than works out for itself: an address for the weekly note. Filling it in is the whole of joining the list, and it has nothing to do with signing in: an account does not put you on the list, and a place on the list is not an account.

What is kept is the address, lowercased, and the moment it arrived. Not the address your request came from, not your browser, not the page you came from: the table has no columns for any of those, which is the durable version of that promise. And nothing in this site sends mail to this list. The route writes the row and stops there.

The address is the row’s own key, so pressing the button twice leaves one row rather than two, and an address already on the list gets exactly the same answer as a new one. That sameness is deliberate: otherwise posting somebody else’s address here and reading the reply would tell you whether they had subscribed.

Nobody reading this site can read that list. Row-level security is on and the table has no policies at all, so the key that ships inside the browser bundle matches nothing and sees nothing; the only key that can write to it or read it is the server key, which never leaves the server. The other tables a browser can reach answer any signed-in reader. This one answers none of them.

There is no unsubscribe link, and no route behind one to hang it on. Deleting the row is the whole of leaving the list, and there are two ways to reach it. Deleting your account is one, described at the foot of this page: it takes the row with it, so long as the address on the list is the one your account signs in with. An email is the other, and it is the only one for an address that never matched an account. The table holds a string somebody typed into the field above, and it has no way of knowing whose it is.

To come off the list, write to hello@holdtab.com.

Your call: holdiks and the journal

Your call is a game of calls on Dota 2 maps, played with holdiks. Holdiks are play points: they have no money value, cannot be bought, sold, cashed out or handed to anyone else, and there are no prizes.

The first time a page asks for your holdiks while you are signed in, an account is opened for you with 100 to start; pressing the daily claim adds 20 a day. What that account keeps:

  • an internal account number (the same one the sign-in record uses) and when the account was opened;
  • a public address of ten letters and digits, the one in holdtab.com/u/<address>;
  • whether the account has a public page, with a short reason when it has not, and whether an admin has frozen it;
  • the name and picture you chose (below);
  • a random secret of 32 bytes, used only to seal your journal lines (below).

Every holdik that moves is a line in a journal: the amount, the balance after it, what it was (the start, a daily, a call on a map, what came back, a bonus) and when, against your account number. Lines are never edited or deleted; a correction is a new line. Your calls and what you hold on each map are readable by you; what anybody else sees of them is the next section.

Once an hour the journal’s new lines are sealed into a Merkle tree, each with your account number hashed together with your secret. Only the tree’s root is published, a hash that says nothing about any line or anybody, and the same hash is sent to the OpenTimestamps calendars, which anchor it in Bitcoin. On the ledger check you can download the proof of any line of your own and recompute it in your browser; only you can make a proof, and anyone can check one.

Your call: what everybody can see

Only an account with a qualifying Steam account connected (below) has a public page. If you play with Google alone, nobody else sees your calls or your balance; your name and picture show only on your own invitation link, if you share it (below).

On your public page, anyone, signed in or not, sees:

  • your name and picture, and the day you joined;
  • how many maps you called and how many of them ended in profit;
  • your balance, leaving out what is in calls on maps that have not ended;
  • your calls on maps that have ended: the side, what you put in, what came back, and when.

A call on a map that is still being played is not shown (not the side, not the amount) until the map ends. The page only counts the maps whose trading is over and whose result is still awaited.

Never shown to anyone but you: your email address and your Google or Steam account numbers. Your email appears only in your own menu and your own settings. Your internal account number is not shown either, not even in the proof file of an invitation bonus line, which refers to the friend it was paid for only by a SHA-256 hash of their account number. The hash does not give the number away; we, holding every account number, can still tell whose it is.

Your call: names and pictures

You go by your Steam name or by a name of your own: 3 to 20 letters, digits, _ . -, nobody else’s whatever the case, not another public reader’s Steam name, changed at most once a week.

Your picture is your Steam picture, one of your own, or your initial. The Steam picture is copied into our own storage (Supabase), so the browsers of people looking at your profile never ask Steam for it; the copy is replaced when your Steam picture changes. Pictures there are public by their address and cannot be listed. The address of a Steam copy is a random name; that of a picture of your own carries your public address and nothing else.

A picture of your own is cut square, 256 by 256, in your browser before it is sent: the original never leaves your device. What arrives is sent to Google Cloud Vision’s SafeSearch to be checked. A picture it rates likely adult, violent or racy is refused and not kept; any other is stored in a folder named by your public address. Until a picture has passed (the check could not be asked, or did not answer), you see it and everybody else sees your initial, unless an admin passes it. At most 5 uploads an hour and 10 a day are taken.

Signed-in readers can report a public profile’s picture or name to us, with an optional line of up to 300 characters. A report keeps who sent it. An admin who takes a picture or a name down puts the initial or the Steam name back and locks the choice for a while; every such act is recorded with its reason.

A picture is deleted from storage when you replace or remove it, when an admin takes it down, and when you delete your account. A copy a browser has already cached may keep showing for a while after.

Your call: Steam

Connecting a Steam account, or signing in with one where that is open, goes through Steam’s own sign-in page, which tells us your Steam account number. The sign-in record then also holds that number, your Steam name and the addresses of your Steam picture and profile. A cookie, holdtab_steam_attempt, ties the trip to Steam and back to your browser for ten minutes and is cleared when you return.

Right after, the server asks Steam’s public Web API for your profile (name, picture, and when the account was made, which Steam shows only for a public profile) and OpenDota for how many Dota matches you have played, which it shows only if your match data is public. Kept from that: the Steam account number, the name, a fingerprint of the picture, when the account was made, the number of matches, and when they were linked and last read; and a history of which holdtab account carried which Steam account, and when.

That is what decides the public page: a Steam account at least 180 days old, or with at least 100 Dota matches. A Steam account that left another holdtab account less than 30 days ago counts for neither until the 30 days are up. What was once seen stays seen: an age read while a profile was public still counts after it is made private.

Connecting a Steam account in addition to the one you signed up with adds 100 holdiks once that Steam account meets the rule above: at once if it already does, otherwise on the day it does. It is added once per Steam account ever, and once per holdtab account. Connecting a Google account adds nothing. To keep that true, the journal line of the bonus carries a fingerprint of the account it was paid for (Google ones included, from before Google stopped adding anything), never the account number itself. The fingerprint is a keyed hash (HMAC-SHA256) whose key is a random secret kept encrypted in our database’s vault: a journal line, a copy of the journal or a downloaded proof does not name the account, and the number cannot be found by trying every possible one. It is not secret from whoever runs the database, though: with the key they can still check whether a given Google or Steam account was ever paid a bonus.

Your call: invitations

Your invitation link is holdtab.com/r/<your public address>. Whoever opens it sees your name and picture. Opening somebody’s link leaves a cookie, yc_ref, holding that address for 30 days (the first link opened wins), and it is handed over only when a new account is made by signing in, within that account’s first ten minutes.

For each friend who joined through your link, you see when they joined, how far they have got (whether they connected Steam, and how many settled maps they have called and on how many different days) and the holdiks it brought; their name and picture only if their profile is public.

So that nobody invites themselves, each account keeps a fingerprint of every Google and Steam sign-in it has carried, with the same keyed hash (HMAC-SHA256, with the secret key in our database’s vault) as the connection bonus above, never the account number. A Steam account that once qualified a friend is remembered by its Steam account number alone, with no holdtab account beside it, so that it counts only once; who invited whom is kept too.

The emails we send

Letters go to one address: the one your Google account gave us when you signed in, or one you add in your notification settings and confirm with the link we send to it. A new address takes over only once it is confirmed. An account that signs in with Steam alone has no address until you add one, and gets nothing until then.

  • The confirmation link, to the address you typed, only because you asked for it. We keep each request (whose, which address, when) for about a day, to limit how often a link can be sent: once a minute and five times a day per account, five times a day per address.
  • One welcome, once: when your Your call account is opened, or for an account without an address, when the first one is confirmed.
  • Notifications you chose to get by email in your notification settings. Until you choose, every kind comes by email, at the pace below.

The pace. Your calls on a series come in one letter once the series is decided. Most other notifications wait for a letter the next morning, or on Monday morning for the week. Nothing comes that you already read on the site, at most three letters a day, none between 23:00 and 09:00 in your time zone, and after 30 days away only the weekly letter and account notices. To keep to your clock we keep, with your account, the time zone you read the site in and when you were last here (to the hour); we keep each notification waiting for a letter until it is sent, and then for about 30 days.

They are sent through Resend, an email sending service, from holdtab.com, on Resend’s servers in the EU (Ireland). Resend gets the address, the letter, and whether it was delivered. Open and click tracking are off: no pixel in the letters, no rewritten links. When an address does not exist, or its mail service reports one of our letters as spam, Resend tells us, and we stop writing to it until you confirm an address again; your settings say why.

On our side, each letter is a row in the database: whose it is, what kind, when, and Resend’s id for it. A row that is done is deleted after about 30 days. Deleting your account deletes your rows at once, sent or waiting.

Every notification email has a link at the foot, and the header mail apps use for their own Unsubscribe button. Either turns email off for that group of notifications (from a morning or Monday letter, for all of them); they still reach your inbox on the site. You can turn email back on in the same settings.

Analytics, and how to refuse them

Analytics here are PostHog, on its European servers, and they exist to show which parts of the board get used.

They are off until you say yes. The notice at the foot of the screen is a real switch, not a formality: choosing “Essential only”, or simply not answering, means the PostHog script is never put on the page. Not loaded and silenced: not loaded. Nothing about your visit goes anywhere.

Your answer is remembered in your own browser, under holdtab.consent.v1. No answer counts as no.

If you choose “Allow analytics”, then from that moment PostHog records page views as you move around the site and, on its own default behaviour, interactions such as clicks, together with what any web analytics tool collects: the address of the page, where you arrived from, your browser and device, and an approximate location worked out from your IP address. The site adds no events of its own beyond the first page view.

When you are signed in and have allowed analytics, PostHog is also told who you are (your account identifier and your email), so that a returning reader counts as one person rather than five. Visitors who are not signed in get no profile there at all.

One honest gap: there is no button inside the site to take that permission back. Clearing this site’s data in your browser removes the stored answer, the notice asks again on your next visit, and from then on nothing new is sent. That does not remove what PostHog already received.

To have that removed as well, write to hello@holdtab.com.

What stays in your browser and never reaches us

The board is yours and it stays with you. Every prediction you make (the results you set, the teams you lock, the seeds you swap, the power dials you drag, the model settings you change) is written to your browser’s own storage under ti2026-predictor.v1. There is no code in this site that sends it anywhere. It is not synced, not backed up, and not visible to anyone but you.

Which has a consequence worth knowing before it surprises you: open the board in another browser, or clear this site’s data, and it is empty. Signing in does not bring it back, because signing in was never what was holding it.

Kept the same way, in the same place, and equally private:

  • the layout of your multistream tiles;
  • which side panel you left open on a match page;
  • which item of the rail you last picked;
  • the short trail of pages you walked in this tab, which ends when the tab does.
  • while you play Your call, the top bar’s copy of your name, picture and balances, kept for this tab only so a reload draws them at once.

Asking the analyst

The tournament analyst is answered by a language model. When you ask something, the question, the last few exchanges of that conversation, and a snapshot of the tournament’s own numbers are sent to Anthropic’s API, which writes the reply. Your name, email and account identifier are not attached to that request.

The conversation is not written to any database on this side. Close the tab and, as far as this site is concerned, it is gone. What Anthropic does with a request it received is governed by their terms, not ours.

Other people's code running inside these pages

  • Google’s sign-in script, loaded from Google, draws the sign-in button, so Google sees that a browser loaded a page carrying it.
  • The pictures of Your call, yours and other readers’, come from our own storage at Supabase, never from Steam; the top bar may still fetch your Google profile picture from Google’s servers while it loads. Behind the scenes, a picture you upload is checked by Google Cloud Vision, Steam’s Web API and OpenDota are asked about a Steam account you connect, and the OpenTimestamps calendars receive the journal’s hourly root, a hash.
  • Match pages and the multistream embed Twitch’s own player. When a stream loads, Twitch sees that request and applies its own cookies and policies. No stream, no Twitch.
  • The film behind the front page plays through YouTube’s no-cookie player.
  • The memes at the foot of the front page are still pictures fetched from YouTube’s image server, i.ytimg.com, so YouTube sees that request before you have pressed anything. Pressing a tile puts YouTube’s no-cookie player in the picture’s place.
  • The site is served by Vercel, the live data pipeline runs on Fly.io, and sign-in and the database are Supabase. Requests pass through their servers and appear in the ordinary logs any host keeps.

Most of what is here is not about you

Nearly everything on this site is public esports data, gathered from other people’s services on a schedule by a background worker rather than by anything you do: matches and drafts from OpenDota, live match state and hero and item art from Valve’s own services, prices from Polymarket, rosters and results and player photographs from Liquipedia, and which channel is broadcasting from Twitch.

All of it is about professional teams and professional matches. None of that is about the person reading the page. A reader turns up in this database in the places named above: the sign-in record, the row on the mailing list if you asked for the weekly note, and Your call if you play it.

Deleting what is held

There is a button, and it is yours to press without asking anyone. Your picture sits in the top right corner of every page: click it, open Settings, and “Delete account” is at the foot of your profile. It asks you to type your name and then to hold the button down, so it cannot happen by a slip of the hand.

Then it removes the record described at the top of this page (the Google account identifier, the email address, the name, the picture address) from the sign-in service outright. Nothing is kept aside, nothing is marked for deletion later: when the button comes back, the record is gone. You are signed out in the same breath and returned to the sign-in screen, because there is no longer an account to be signed into. Signing in again with the same Google account makes a new record, not the old one.

With Your call, the same button first deletes your pictures from storage (the ones you uploaded and our copy of your Steam picture) and then shreds your Your call account. Your public page goes, and your name and picture with it; the record of your Steam account is deleted; the history of which account carried it keeps the Steam account and the dates, without you; the fingerprints kept for invitations are deleted, and so are your invitation links: the invitations you made no longer say which link they came through; reports about your profile are deleted, and reports you made stay without your name on them.

And your secret is wiped. The sealed journal was hashed with it, so from then on what was sealed for your lines cannot be tied to your account number by anybody, us included, and no proof can be made for them any more. A proof you already downloaded stays whatever you did with it.

What stays, and why: the journal lines themselves, your calls and holdings on each map, because every market has to keep adding up for everybody else in it. They stay against your internal account number, which after the deletion belongs to nobody and leads to nothing. The same number stays on any admin act about your account and on who invited whom. And the lines of a connection bonus keep their fingerprint of the Google or Steam account they were paid for (see “Your call: Steam”), so that it is never paid twice: nobody can read the account off it, but whoever runs the database could still match it against a Google or Steam account they already know.

What the button does not do is touch your board, and it could not. Your predictions are in this browser’s own storage under ti2026-predictor.v1 and have never been on the server, so there is nothing there to delete. Clearing this site’s data in your browser is what removes them, and that is yours to do whenever you like, signed in or not.

The mailing list is the other half, and it is the half that goes first: the row is deleted before the sign-in record, so a press that fails part way is simply one to press again: the account that authorises the second press is still there. The row it looks for is the one keyed by the address your account signs in with, lowercased to match the way the footer stored it.

Which carries a limit worth stating rather than leaving you to assume past it: the list knows addresses and has never known accounts. Subscribe with one address, sign in with another, and the button does not find that row and does not touch it. Then the row stays, and leaving the list is the separate ask described above.

Two things also sit outside its reach, and they are worth naming rather than glossing: whatever PostHog already received if you allowed analytics, and the ordinary request logs kept for a while by the hosts listed above.

For those, or for any question the button does not answer, write to hello@holdtab.com.

Getting in touch

Questions about this page, or a request about what the site holds about you: hello@holdtab.com.