The short version
- No email address. Signing in with Discord does not ask Discord for your email, so there is none here to leak, sell or lose.
- No password. There is no password field anywhere in this app. Discord does the signing in.
- No analytics, no trackers, no advertising. The site loads no third-party scripts of any kind.
- Nothing is stored about you until you sign in. Playing needs an account now, but reading this page, the code of conduct and the front page does not, and no row exists for you until you choose to create one.
Signing in with Discord
The only way to sign in is Discord. The app asks Discord for the identify scope and nothing else, which returns your Discord user id, your display name and your avatar. It does not include your email address, and this app never requests, receives or stores one.
Discord is a separate company with its own privacy policy, and signing in tells Discord that you used this app. What Discord does with that is between you and Discord.
What is stored on the server
Once you sign in, one row exists for you, plus one row for each deck you save, one for each match you play, and one for every move made in it. The table below is every column of all four of those tables — not a selection from them:
| What | From | Why it is kept |
|---|---|---|
| Discord user id | Discord | It is the only thing that says which decks are yours. |
| Display name | Discord | So the app can greet you by name instead of by number. |
| Avatar URL | Discord | The picture beside your name. It is a link to Discord, not a copy of the image. |
| Village | You pick it | The colours your side of the table is dressed in. |
| Language | You pick it, from settings | Which of the game’s four languages it speaks to you. Empty until you choose one, which also means your browser’s own language keeps deciding. |
| The deck you play | You pick it | Which deck you play, chosen from the menu beside your name. It is the id the deck was built with — the same id “Decks you save” below is stored under — and not the name you typed, nor a second copy of the deck itself. A practice game deals whatever it names; an online table deals it only when it names one of the decks the game ships, and a starter otherwise. Empty until you choose one, which also means you play a starter deck. |
| Ladder rating | The database | Your position on the ranked ladder. Every account starts at 500; once ranked games are running it moves by a formula like chess Elo when one finishes. |
| Games played | The database | A count of your finished ranked games. It is kept beside your rating and is shown with it; in this season it does not change how your rating moves or which rank you are shown as — everyone is rated at the same rate, and you have a rank from your very first game. |
| When rating last changed | The database | Set only when your rating changes, not every time you change something else. Used to break ties on the standings. |
| Which season your rating belongs to | The database | A number naming the run of the ladder your rating was earned in. It is 0 for everyone today, because Season 0 is the first one. It is stored so that a future season can start people afresh without throwing away what this one recorded. |
| The date your row was created | The database | Written once, the first time you sign in. It is how old your account is. |
| The date your row last changed | The database | The database advances this every time the row is written, and signing in writes it — so in practice it is the date you last signed in. That makes it the one thing here that records when you were around, which is why it is a row in this table rather than a detail left out of it. Nothing in the app shows it to anyone, including you. |
| Decks you save | You build them | So they follow you between devices, which is the reason accounts exist here at all. Each one is stored whole, under your Discord id and the id it was built with. |
| When each deck was saved, and last changed | The database | Your deck list comes back most-recently-saved first, and these two dates are what put it in that order. Nothing else reads them. |
| Each match, and the code that names it | The database | Starting a match creates a row for it; joining one takes the second seat in a row that already exists. The code is the six characters that name the room — short enough to read out to the person you are playing, and the only name a match has that is not a machine id. |
| The name you gave the room | You choose it | Whatever you typed when you opened the room, so the people you invite can tell it apart from anyone else's. Empty until you give it one. |
| Whether the room needs a password, and not the password itself | You choose it | Set a password on a room and what is stored is a scrambled, one-way hash of it — never the password you typed. The row can tell whether an attempt matches; it cannot be read back into the word you chose, by us or by anyone else. |
| Who sat in the two seats | Discord, through your account | Your Discord user id and your opponent's, one per seat. This is what makes a match say who played it, and it means the person you play can be identified from the row as surely as you can. |
| What the game was dealt from | You choose it | The shuffle number, the two villages, and the two decks. A deck that ships with the game is stored by name; a deck you built is stored as a COPY of its card list, taken when the match was created. The copy is what lets a finished game still be replayed after you have edited or deleted the deck you played it with, and it is deleted with the match and with your account. |
| Whether the match is over, who won, and why it ended | The database | Waiting, being played, or finished — and once finished, why it ended: the game itself, a clock running out, someone conceding, or a rules change. Which seat won is stored beside it, and it is left empty where nobody did — an ending that is nobody's win has no winner to record. |
| The two clocks | The database | Fifteen minutes each. What is stored is the time each of you has left, which seat the clock is currently running against, and the moment it started running. Three facts rather than a number counting down, which is what lets a clock be a row in a table at all. |
| The clock on the opening decision | The database | One number, not one each. Both of you decide whether to keep the hand you were dealt at the same time, against three minutes that belong to neither of you, so what is stored is a single remaining time — and when it reaches zero, whoever has not decided keeps the hand they were dealt and the game begins. Those three minutes are not taken out of the fifteen above. |
| Whether the match counted | The database | Whether this game was played on the ranked ladder. Only a match stored as ranked moves either player's rating when it ends; a quick match and a private room are stored as unranked and move nothing. It is written when the match is created and never changes afterwards, so a game cannot be made to count — or stop counting — after it has been played. |
| The rules the match was played under | The database | Which version of the game's rules your moves were recorded against. A list of moves only means anything under the rules it was made under, so the version is kept with it rather than assumed — the same moves replayed under changed rules are a different game. |
| Whether the match may be watched | You choose it | Answered when the match is created and never assumed — the column has no default, so every match states it at the one moment somebody knows it. It is the record of whether anyone other than the two of you may watch. |
| When each match started, and when it last changed | The database | Two dates on every match row. Between them they say when you were playing, so they are listed here for the same reason the sign-in date above is: a timestamp that records when you were around is not a detail to leave out. |
| Every move made in a match | You play them | A match is not stored as a picture of the board. It is stored as the moves that produced it, in order, and the board is rebuilt by replaying them — so every card you play and every choice you make in a match is written down and kept for as long as the match row is. |
| When each move was made | The database | One timestamp per move. It is what makes the log above a minute-by-minute record of a game rather than just a list of what happened in it. |
That is the whole of it rather than a summary of it: every column of all four tables listed above appears as a row, the dates and the move log included.This page used to say there was no match history, and now there is one. Matches are stored, with every move in them. That is a change in what this app keeps about you, and it is written here as one rather than left to be noticed. Beyond that there is no other profile, no page-view log, no IP log kept by the app itself, and no field waiting to be filled in later.
Deck names and any other text you type are stored as you typed them. They are readable by the person who runs the site, and other players see them when you play. Do not put anything in one that you would not want read.
What is stored in your browser
Separately from any account, the app keeps a small record in your browser's own local storage, under two keys: chuunin.profile — a village, two table preferences, and a name field the app no longer lets you change — and chuunin.decks, which holds decks you have built. Nothing sends that record anywhere on its own. Clearing this site's data in your browser erases it, and no copy of it exists elsewhere unless you signed in and saved.
Signing in sets four cookies, and three of them are set before your browser has even left for Discord — so if you open devtools partway through a sign-in you will count three, and four once you are back:
__Host-authjs.csrf-token— proves the sign-in being completed is the one this site's own page started, and not one another site posted on your behalf.__Secure-authjs.callback-url— which page to put you back on when Discord returns you.__Secure-authjs.pkce.code_verifier— a one-time secret that ties the trip out to Discord to the reply that comes back, so an intercepted reply is useless on its own. It is created with a fifteen-minute life and is spent on the way back.__Secure-authjs.session-token— the session itself, set last, when Discord returns you. This is the one that keeps you signed in between page loads.
All four are set by this site's own server in a response header, are marked HttpOnly (no script on the page can read them, this app's own included) and SameSite=Lax, and each exists to make the sign-in itself work. Once you are signed in three remain, because the code verifier has been spent. The __Host- and __Secure- prefixes are a browser rule that only applies over HTTPS: a copy of this app running on plain http://localhost sets the same four cookies under the same names without them.
There are no analytics, advertising or tracking cookies, because there is no analytics and no advertising, and none of the four above is one.
One more cookie exists, and it is not one of the four above because it is not set by the server at all: chuunin.lang, written by this app's own script in your browser when you choose a display language on the settings screen, so that the server can render the next page in it. It holds nothing but that choice — one of English, Spanish, Portuguese or French — is not HttpOnly, since the page that reads it back is this app's own, and is not an analytics or tracking cookie either.
The front page
The front page has nothing to fill in. It used to carry an email box for beta announcements; that box is gone, and no address given to it was ever stored by this site. The only thing the page asks of you is to sign in, and signing in happens on Discord, on the terms above.
Where the site runs, and what it loads
- Railway hosts the app and the Postgres database the rows above live in. Like any web host, its servers see the requests your browser makes.
- Cloudflare Pages serves the card art, when card art is switched on. Your browser fetches images from it directly, so that host sees those requests the way any image host does. No account data is attached to them: the app holds no credential for it and sends none. When no art host is configured, your browser never contacts one at all and cards fall back to plain text frames.
- Discord handles the sign-in itself, and serves the avatar image your browser loads beside your name.
- Fonts are served from this site, not from Google. They are downloaded once when the app is built and shipped from this origin, so loading a page makes no request to a font host.
That is the complete list of hosts a page on this site talks to. There is no tag manager, no analytics service, no advertising network and no embedded third-party script anywhere in the app.
Deleting your data
Revoking the app in Discord is not deletion. Removing Chuunin Exam under Discord's User Settings → Authorized Apps stops it signing you in again, and it is worth doing — but Discord cannot reach into this database, so the row stays until it is deleted here.
There is no self-serve delete button yet. Ask, and the row goes, and with it every deck attached to it and every match you played, move logs included — every column in the table above. Nothing is kept back, and there is no soft-delete copy.
One consequence is worth stating rather than discovering. A match has two players in it, so deleting your account takes the other player's copy of those matches too, including one still being played. Their account and their decks are untouched; the games you played together are not theirs alone to keep.
This copy of the site has no contact address configured, so there is no address to print here and no way for you to ask for your data to be deleted from this page. That is a gap in the deployment, not a policy: if you are reading this on the live site, it is a bug, and the address belongs in this paragraph.
One honest caveat: the database host takes its own routine backups, so a deleted row can survive in those until they age out. Nothing reads them except a restore after a failure.
Children
Signing in requires a Discord account, and Discord's own terms set the minimum age for that. This site is not directed at children, and it collects nothing from anyone who has not signed in.
Security, stated plainly
This is a hobby project run by one person, and it makes no security claims it cannot keep. What it does instead is hold as little as possible: no password to steal, no email address to leak, and no payment details. What it holds is not one row — it is your account, the decks you save, the matches you play, and every move made in them — but every column of all of it is printed in the table above, and there is nothing held that is not. The safest data is the data that was never collected.
Changes to this page
It changes when the app does. There is no mailing list to notify you through — the app holds no email addresses, which is the point — so the date below is how you tell.
Last updated 26 August 2026