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:
| field | contents |
|---|---|
schema_version, exporter_version | version strings |
alias | a display name you choose (max 32 printable chars) |
public_id | rotating pseudonym or a fixed public ID you choose |
provenance | tier label |
window_days, window_end_utc | 7 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.