Colophon
What runs xmr.club, why we picked it, and what we deliberately rejected. Public so anyone auditing the site (or wanting to fork it) doesn't have to guess.
Runtime
- Cloudflare Workers — request-time SSR + JSON APIs. Chosen for first-byte latency at the edge (every public page rendered server-side; SPA hydrates after).
- Cloudflare D1 — SQLite at the edge. Holds providers, audit log, reviews, sponsorships, onion-probe results. Read-replicated; writes serialised through the primary.
- Cloudflare KV — small hot caches (rate-limit counters, idempotency keys, /ask classifier cache). Eventually-consistent; we never store anything we can't rebuild.
- Cloudflare R2 — provider logos + OG images + the public
/data.jsonsnapshot. CC-BY-4.0; mirror-friendly. - Tor hidden service — independent Docker container with a host-matched Caddy site block proxying to
https://xmr.cluband adding anX-Onion-Originheader so the worker can rewrite outbound links to provider .onions. Fingerprint published at /transparency.
Frontend
- SSR-first. Every public page renders meaningful HTML before any JS executes. Tested with Tor's safest mode + Lynx + curl. AI-surface map exposes the plain-text twin of every page.
- React + react-router-dom for interactive surfaces (admin, /q filters, /search). SSR is the floor; SPA is the upgrade. Pages that work better without JS (glossary, transparency, peers, this page) are SSR-only — the SPA shim hard-navs back to the server render so we never replace good HTML with empty client markup.
- No CSS framework. One hand-rolled stylesheet at
/src/styles.css. Monochrome on bg + a single accent. Three themes: light / dark / auto, switchable via cookie-based/toggle-themeroute so noscript clients can flip too. - No analytics, no fingerprinting. No GA, no Plausible, no Cloudflare Web Analytics. We measure traffic via Cloudflare's request logs (already collected for the worker; not user-attributable).
- Service worker registers on idle for network-first cache fallback. Failures are silent; the site works fine without it.
Data + content
- Provider records live in D1, edited via the admin worker. Every state change writes an audit row (visible at /audit) and busts the relevant edge cache key.
- Long-form guides are TypeScript modules under
worker/guides.ts. We picked code over Markdown to get free type-checked cross-links, schema.org HowTo blocks, and per-guide TypeScript JSON-LD generation without a build step. - Logos are SVG where available, PNG fallback. Stored on R2, served with long Cache-Control + immutable hash in path.
- Onion verification runs on a daily cron — fetches each listed provider's Onion-Location and diffs against our record. Drift is surfaced at /onion-audit.
- Open data.
/data.json+ per-feed JSON twins. CC-BY-4.0. We test our own ingest path against it.
AI-engine surface
- /llms.txt + /llms-full.txt — RFC-style index of every public surface for LLM crawlers.
- Per-page plain-text twins at
/llm/<page>.txt— same content, no chrome, structured for parsing. - JSON Feed v1.1 at /feed.json alongside Atom — preferred by modern crawlers.
- Speakable schema on long-form pages — voice-assistant friendly summarisation hints.
- Design choice: every assistant-cited surface points back to a canonical HTML URL and a plain-text twin, so we can be re-checked without scraping.
Editorial bot
- @xmrclub_bot on Telegram — separate Cloudflare Worker at
bot.xmr.club, no relation to the public site beyond a one-way notify webhook. DM forwards into a private support group; replies relay back. The curator's personal Telegram is not exposed. - Submissions, corrections, and sponsorship inquiries all flow through the bot or /submit. Both write to D1; the bot pings the support group via a shared-secret
X-Bot-Notifyheader.
What we deliberately rejected
- No paywall, no signup, no email gate. Authority comes from being public-checkable. A gated directory cannot be cross-verified.
- No third-party tracking. No GA, no Hotjar, no Sentry session-replay. The price of a tracker is a privacy directory that doesn't believe its own thesis.
- No client-side framework lock-in. SSR is the source of truth; the SPA is an enhancement we could rip out tomorrow without losing pages.
- No build-step CMS. No Astro, no Next, no Gatsby. Content edits land in D1 and appear within one edge-cache TTL. Static rebuilds don't scale to per-provider audit timing.
- No "AI-generated reviews". Every review on this site is hand-written by the curator. We use AI tools to draft schemas and audit code; we do not let an LLM grade a provider.
Stack we'd recommend to a fork
- If you're starting from scratch and want the same shape: Cloudflare Workers + D1 + KV. ~$5/month all-in until you cross 10M req/day.
- If you don't want Cloudflare: Bun + SQLite + Caddy works identically. SSR-first means edge isn't load-bearing — it's a latency optimisation.
- If you want a static fork (no D1): export /data.json, render with anything. License is CC-BY-4.0 with attribution.
Operating cost (transparency)
Approximate monthly cost as of 2026-05: Cloudflare Workers + D1 + KV + R2 ≈ free tier (well under the 100k req/day limit). Onion infrastructure marginal cost is the hidden-service container (~$0). Domain and the .onion vanity prefix are the only material spends. See /transparency for funding model.
Credits
See /heroes for the open-source projects and ecosystem actors xmr.club depends on. See /peers for the independent directories we cross-reference against.