$ cat privacy.md

PRIVACY

This site is a brag wall for numbers, not a surveillance surface. The exporter fails closed: if a field isn't on the allowlist, it does not leave your machine.

allowlist-only fail closed opt-in

The whole allowlist

A public snapshot may contain only these fields — this is the complete list, enforced by schemas/snapshot.schema.json and scripts/validate_snapshots.py, both of which reject unknown keys:

fieldcontents
schema_version, exporter_versionversion strings
aliasa display name you choose (max 32 printable chars)
public_idrotating pseudonym or a fixed public ID you choose
provenancetier label
window_days, window_end_utc7 or 30, plus a coarse (day-precision) UTC date
metrics.*fifteen numeric aggregates (token sums, counts, derived rates)
daily[]per-UTC-day numeric aggregates: date, gross, fresh, api calls

Forbidden, everywhere, forever

The following never appear in generated JSON, fixtures, HTML, logs, or the repository history — the validator scans for them and the build fails if one slips through: prompts, completions, reasoning text, session titles, message bodies, tool arguments or results, email addresses, account numbers, subscription identifiers, API keys, tokens, cookies, credentials, filesystem paths, hostnames, IP or MAC addresses, Discord/chat/user/channel identifiers, Git identities, database identifiers, the model or provider roster, quota reset timestamps, browser data, and machine fingerprints.

The validator additionally rejects: unknown keys (fail closed), numbers outside sane bounds, nesting deeper than the schema allows, markup or control characters in any string, UUID-shaped or snowflake-shaped strings, long hex or base64 blobs, and URLs containing credentials. When it rejects a value, its error output names the JSON path and the rule — never the value itself, so a rejected secret is not re-leaked by the error message.

The rotating pseudonym

public_id is derived as HMAC-SHA256(local_secret, "tokenmaxxing-pseudonym-v1|YYYY-MM"), truncated. The secret is 32 random bytes created locally with file mode 0600, outside any git repository, only when you explicitly ask (--create-secret). Consequences:

  • The pseudonym rotates monthly, so long-term tracking across windows requires your cooperation (keep the same alias) rather than being free.
  • It is derived from randomness you hold — never from hostnames, MAC addresses, IPs, usernames, account IDs, chat IDs, file paths, machine IDs, or Git identity. There is nothing to reverse.
  • You may instead choose a fixed public ID (as this site’s own entry, tmx-herman-primary, does) — that is an explicit, visible choice.

Reading your database, safely

The exporter opens your Hermes state.db with the SQLite URI flag mode=ro — it is physically unable to write. It executes only aggregate SUM/COUNT queries over numeric columns, validates the result against the allowlist before writing, writes atomically, and never prints raw rows. It is ~300 lines of stdlib-only Python; audit it in one sitting: scripts/export_anonymous.py.

Deletion and opt-out

Submissions are opt-in by merge request. To remove one, open an issue or merge request deleting your file from data/submissions/ — removal is honored without questions, and the leaderboard is rebuilt without the entry. Because the site is static and the data is aggregate-only, removal from the repository is removal from the site at next deploy. Public web archives are outside anyone’s control; the allowlist is designed so that even an archived snapshot contains nothing personal.

What this site’s analytics do

Page analytics run on a self-hosted Matomo instance under the site operator’s control, configured without cross-site tracking. With no analytics ID configured, the pages ship zero tracker code.