Privacy
What the app sends, field by field
Last updated 2026-09-02 · applies to v2.8.2 and later
Filthy Net Deck works completely with no account, and in that state it uploads nothing about you or your matches. Everything below is opt-in, off until you switch it on, and reversible.
This page lists the fields in full rather than describing them, because the list is short
enough to publish and a summary you cannot check is worth very little. The tables match
the source exactly — src/services/cloud/healthPing.ts,
src/services/cloud/matchSync.ts and
src/services/cloud/backupSync.ts in the
public repo.
Two rules hold no matter which toggles you turn on.
1. Another player's identity never leaves your machine. Your opponent's Arena name and the specific cards they revealed to you are never uploaded — not hashed, not "anonymised". Your app works out what deck they were on locally, and only that label is sent. An Arena handle identifies a real person who agreed to nothing.
2. The payload is an allowlist, not an exclusion list. Uploads are built by naming each field explicitly, never by serialising an internal object. A field added to the tracker later cannot start uploading itself by accident, and an automated test asserts the exact set of keys.
1 · Parser health ping
Default: off. Settings → Data & privacy. Sends at most one record per day, on launch. It exists so that when an Arena update breaks log parsing — which it can do without notice — that shows up as a spike across many installs within hours, instead of arriving as a bad review a week later.
| Field | Example | Why |
|---|---|---|
installId | random UUID | Counts unique installs rather than launches |
appVersion | 2.8.2 | Which build is affected |
parserVersion | 1 | Attributes a fault to a specific parser |
os | windows | Platform only — never the build number |
logFound | true | Did the app locate Arena's log |
detailedLogs | true | Is Arena's Detailed Logs setting on |
parseErrors | 0 | The actual early-warning signal |
matchesLast24h | 7 | Detects recording stopping population-wide |
Being straight about the tradeoff: installId is a persistent random identifier
for a machine, so it is pseudonymous, not anonymous — it makes "this
install has been active 60 days" knowable. That is precisely what makes counting installs
possible at all. It is tied to no account, no name, and no IP-derived location. Switching
the ping off deletes the identifier locally; switching it back on mints a brand new one.
2 · Match sharing and cloud sync
Default: off. Requires a free account and the single "Sync & community data" toggle. This is what makes community matchup rates possible: they are built from players who chose to contribute, which is why the app can show real ladder winrates instead of scraped tournament results.
| Field | Example | Notes |
|---|---|---|
client_hash | sha256 digest | A dedupe key. Arena's own match id is hashed and never sent |
started_at / ended_at | timestamps | When the match ran |
format | standard | Standard or Pioneer |
best_of | 1 | Bo1 or Bo3 |
ranked | true | Ladder event or not |
rank | Diamond 1 | Your rank at match start |
season_ordinal | 42 | Which Arena season |
my_deck_hash | digest | Groups your own matches by list |
my_archetype | standard-azorius-control | Your deck's archetype label |
opp_archetype | standard-mono-red | A label, worked out on your PC |
opp_confidence | 0.82 | Lets thin reads be down-weighted or rejected |
result | win | Win, loss or draw |
games | [{onPlay, won}] | Play/draw and result per game |
Decklists
Decklists ride the same single toggle — one switch, by design, and the consent line
in Settings says so out loud, because a backup you did not know you turned on is not
consent. A deck row is deck_hash, name, format, and
the Arena card ids for main and sideboard. It is worth having because Arena's logs rotate:
once they do, the 75 cards a deck was are gone from your PC and Arena will not hand them
back.
Changed in v3.2.0 — two corrections worth stating plainly.
Until now the app worked out a match's format by looking for
"ladder" in Arena's queue id. Historic_Ladder contains
"ladder", and so do the Alchemy and Timeless queues, so games from formats
this app does not cover were being uploaded to the community matchup data
labelled Standard, and decks from those queues were backed up and published with
a "standard" chip. Both are fixed:
-
Fewer matches are uploaded. Only genuine Standard and Explorer/Pioneer
matches reach
shared_matches. Historic, Alchemy, Timeless, Brawl, draft and any queue Arena did not name are now skipped outright rather than relabelled. Rows uploaded under the old behaviour are already there; ask and they will be removed. -
Decks are backed up under their real format.
formatcan now sayhistoric,alchemy,timelessorbrawlas well asstandardandpioneer, so a deck you built in Historic is filed as a Historic deck. Draft pools are not backed up at all. No new field is sent — the row is the same four things it always was.
Exporting your decklists to a folder on your own PC — Export decklists in My Stats — uploads nothing and needs no account. It reads the same local history the app already has and writes files.
Your match history (new in v3.4.0)
Match sharing sends a narrow slice of a match to the community data. That slice is not enough to give you your history back on a second machine — it is Standard and Pioneer only, and it drops the queue, the deck name and the per-game detail. So the same toggle now also keeps a private backup of your own history, in a separate table that nothing aggregate ever reads. It is what makes signing in on a new PC show the matches you played on the old one.
| Field | Example | Notes |
|---|---|---|
match_id | sha256 digest | Same hashing as above — Arena's own match id is still never sent |
started_at / ended_at | timestamps | When the match ran |
event_id | Brawl_Ladder | Arena's queue, so a Brawl game comes back as a Brawl game |
best_of | 1 | Bo1 or Bo3 |
my_team_id | 1 | Which seat was yours — without it, won games restore as losses |
games | [{onPlay, mulligans, …}] | Your own per-game detail: play/draw, mulligans, first land turn |
result / result_reason | win | Includes unfinished matches, so the count matches the PC it came from |
deck_name / deck_id / deck_hash | My Brew | Your own labels for your own deck |
deck_main / deck_side | Arena card ids | The list you registered for that match |
my_rank / season_ordinal | Diamond 1 | Your rank and which season |
Your opponent is not in that table. Their name, the cards they revealed, their basic land types and their platform stay on the PC that saw them — the same rule as everywhere else on this page, and it is not relaxed just because these rows are private to you. The visible cost is stated in the app rather than hidden: a match restored onto another machine shows your side of it only. Your own Arena name is not sent either.
The backup is deleted along with everything else when you turn the toggle off. Deleting a single match, or clearing your history, removes it from the backup too — otherwise the next machine you signed in on would hand it straight back.
Public profile and friends
A profile page at /u/<handle> is published only when you choose to publish
it, and it carries your handle, display name, and season record. Friend codes are private
tokens you hand out yourself; a friendship is two account ids and a timestamp. All of it
can be switched back off.
Changed in v3.1.8: publishing a deck to that page now also publishes
the decklist, as Arena import text, at
/u/<handle>/<deck> — so anyone with the link can copy it into
Arena. That is the point of the feature, and it is a per-deck decision you make with the
Publish decklist button on that deck. Before v3.1.8 a published deck showed
only its name, format, size and last-played date; decks you published back then keep showing
exactly that until you publish them again. Taking a deck down deletes the published list,
and so does making your profile private. What is never published either way is the
match history behind the deck, or anything about the opponents you played it into.
3 · Never uploaded, under any setting
- Your opponent's Arena name (
opponentName) - The cards your opponent revealed (
opponentSeen) — the single most useful field a crowd meta could have, and deliberately withheld - Your opponent's platform (
opponentPlatform) - Your own Arena name (
myPlayerName) - Arena's raw match id — hashed before it is used
- Your collection, wildcards, gems, gold, or anything else from Arena's inventory data
- File paths, IP-derived location, contacts, or browsing of any kind
- Anything at all if you are signed out with the ping off — which is the default
4 · Turning it off, and deleting what is there
Every toggle is in Settings → Data & privacy and every one is reversible. Switching the health ping off removes the local install id. Switching cloud sync off stops uploads. Deleting your account removes your rows — matches, decks and profile — because they are foreign-keyed to it and cascade on delete.
Raw shared matches are dropped after 120 days. Being honest about the reason: that window is about storage cost and unbounded growth, not about protecting you. The anonymous aggregate counts computed from them are what stays.
5 · Third parties
Card names, text and images come from Scryfall, requested by your app as you browse cards. Deck data comes from MTGGoldfish, MTGO, magic.gg, Melee and Untapped, and is fetched by a build pipeline on a server — not by your PC. Accounts, storage and the community aggregates run on Supabase. The site and downloads are hosted on Netlify, whose analytics count page requests without cookies. The optional feedback form is posted to FormSubmit, which emails it to the maintainer. There is no advertising, no third-party tracker, and nothing is sold or shared with anyone.
6 · Checking any of this yourself
The app is open source under the MIT licence. You do not have to take this page's word for
it — read the repo. The two files
that build every upload are src/services/cloud/healthPing.ts and
src/services/cloud/matchSync.ts, and both are short on purpose.
Questions, or want your data removed? Use the feedback form or open a GitHub issue.
7 · Feedback form (this website)
Separate from anything the app can upload. The Suggest / Report form on this site is optional and sends only what you type: whether it is a feature idea or a bug, the message, an optional contact, and (if you opened it from the app) the app version. FormSubmit receives that POST and emails it to the maintainer. It is never mixed into match uploads, and you do not need an account.
← Back to Filthy Net Deck