This is the public repository for Realmdex.com.
RealmDex tracks server status, player counts, and uptime for RotMG private servers and Realm-Like games.
The site runs on Cloudflare Workers (Rust + Leptos SSR) backed by Cloudflare D1. Every minute, a Cron Trigger fans out parallel polls to each listed server and writes the results to D1. The page renders server-side with the latest data and refreshes on a 30s client-side timer.
To get your server on RealmDex, you need to provide two things:
- A player count API endpoint
- Server metadata (name, icon, links)
RealmDex polls your server every 60 seconds. Your endpoint must:
- Return the current online player count as a plain integer in the response body (e.g.
42) - Return HTTP 200 when the server is online
- Any non-200 response (or timeout after 10 seconds) is treated as offline
Example response:
HTTP/1.1 200 OK
Content-Type: text/plain
42
| Field | Description | Example |
|---|---|---|
| Name | Your server's display name | Valor |
| Icon | Square image (PNG or WebP, recommended 100x100+) | valor.webp |
| Link | Discord invite or homepage URL | https://discord.gg/valormc |
| Endpoint | Full URL to your player count endpoint | https://yourserver.com/api/players |
| Category | pserver (private server) or realm-like (Realm-Like game) |
pserver |
- Online/Offline status — based on whether your endpoint returns HTTP 200
- Current player count — parsed from your endpoint's response body
- 24-hour peak players — highest player count in the last 24 hours
- Last-hour sparkline — small per-card chart of recent player count
- Uptime history — daily uptime percentage over the past 14 days
If your server is still in development, it can be listed with a WIP (Work in Progress) status. WIP servers appear in a separate section and are not polled.
- Rust (stable). Install with rustup.
- The
wasm32-unknown-unknowntarget:rustup target add wasm32-unknown-unknown. - Node 20+ (for
wranglerandsass). worker-build(installed automatically byscripts/build-worker.shon first run).
Note on PATH: if you have both Homebrew Rust and rustup installed, make sure
~/.cargo/bincomes first on yourPATHso the rustup toolchain wins. The build script already handles this when invoked viawrangler dev, but for directcargoinvocations you may need to prepend it yourself. See the comment block at the top ofwrangler.toml.
First-time setup:
npx wrangler d1 migrations apply realmdex --local
./scripts/build-styles.shThen start the dev server:
npx wrangler dev --localOpen http://localhost:8787 (or whichever port wrangler reports).
If you want to develop against the real server list rather than the dev seed:
# Drop legacy/uptime.db (or uptime.db.live) into legacy/, then:
./scripts/import_legacy_db.shThis clears the dev seed and replaces it with the real production list.
cargo test --workspacerm -rf .wrangler
npx wrangler d1 migrations apply realmdex --localcrates/app/— Leptos app: components, types, server functions, D1 query helpers (thedbmodule is ssr-gated).crates/worker/— Cloudflare Worker entrypoints: thefetchhandler for SSR + JSON API routes, thescheduledhandler for the per-minute poller and the daily rollup. Worker-rs 0.8.migrations/— D1 migrations applied withwrangler d1 migrations apply.public/— static assets (CSS, images, favicon) served by Workers Assets.scripts/—build-worker.sh(used bywrangler.toml's build command),build-styles.sh,import_legacy_db.sh.
Leptos's #[server] macro is not currently usable on Cloudflare Workers because worker-rs's D1 futures are !Send while the server-fn machinery requires Send. As a result, /api/list_servers and /api/server_sparkline are exposed as plain async fns with a hand-rolled JSON dispatcher in crates/worker/src/lib.rs, and the client-side live-update layer is an inlined vanilla JS controller rather than a Leptos reactive layer. The initial page render is real SSR with D1 data baked in via Leptos context. See the comments in crates/app/src/server_fns.rs for the swap-back plan if this gap closes upstream.
See:
docs/setup/cloudflare-oidc.md— GitHub Actions → Cloudflare auth setup using OIDC (no long-lived API token in repo secrets).docs/setup/rate-limiting.md— edge rate-limiting rule for/api/*.docs/setup/production-migration.md— one-shot procedure for the PHP → Workers cutover.
- The deploy workflow (
.github/workflows/deploy.yml) only runson: pushtomain. Fork PRs cannot trigger it. - The
productionGitHub Environment requires reviewer approval. Every deploy pauses for a human click, even legitimatemainpushes — defense against compromised accounts and accidental merges. - Authentication to Cloudflare is via GitHub OIDC. No long-lived
CLOUDFLARE_API_TOKENlives in repo secrets. - Third-party actions are pinned to commit SHAs (not tags) to defend against tag movement.
- Branch protection on
main: required PR review, required CI status check, no force pushes, no admin bypass.
- Repository contributors.
- RotMG Wallpaper by Bohrokki
