A developer-API platform with live Trove game data, plus the website and backend behind Better Trove Tools.
Developers sign up in a browser portal, mint scoped, rate-limited API tokens, and call read-only data endpoints with them. Underneath is a reusable platform — accounts, tokens, rate limiting, usage metrics, email, and admin — with the Trove data surfaces layered on top.
Fully dockerized. MongoDB + Redis + PostgreSQL, all persisted to local folders.
| API | https://api.aallyn.net — /v1/* + /health |
| Developer portal | https://dev.aallyn.net — sign up, mint tokens, view usage |
| API reference | https://docs.aallyn.net — interactive docs · GET /openapi.json is always current · /llms.txt for AI tools |
| Showcase site | https://trove.aallyn.net — the Better Trove Tools website |
Python 3.13 · FastAPI + Uvicorn · Beanie/PyMongo on MongoDB 7 · Redis 7 · PostgreSQL (leaderboards, market, codexes) · Argon2 + JWT auth · hashed API tokens.
- Accounts — signup with captcha + anti-spam + breach check, email verification, password reset, GDPR export and account deletion.
- Sessions — short-lived access tokens with rotating refresh tokens and instant global revocation ("log out everywhere").
- API tokens — scoped (Discord-style bitmask), IP-allowlisted, expiring, rotatable.
Self-validating
kiwi_<body>_<checksum>format, and leaked tokens are auto-revoked via GitHub secret scanning. - Rate limiting — Redis sliding-window with per-endpoint overrides and standard
X-RateLimit-*/Retry-Afterheaders. - Usage & site analytics — every authenticated request is recorded and aggregated; cookieless page-view counts for the showcase site.
- Admin — superuser-only panel for users, tokens, usage, and feature toggles.
- Shared plumbing for new endpoints: cursor pagination, idempotency keys, request-id correlation, and one consistent error envelope.
Live Trove game data — all GET, read-only, with real-UTC unix timestamps. Most categories
are public (callable with no token at a stricter per-IP limit); a matching scope unlocks
the full per-token limit. See the API reference for every endpoint.
| Category | What it covers |
|---|---|
rotations |
server time, daily/weekly buffs, merchant timers, biomes, chaos chest, hourly challenge, calendar |
feeds |
Trove news, Twitch/YouTube/Bilibili, Trovesaurus events |
stats · gems |
stat tables and a stateless gem simulator / build optimizer |
leaderboards · activity |
hourly in-game boards (with cheat detection) and player/class activity estimates |
market |
in-game marketplace listings and price history |
codexes |
decoded game catalogs — allies, mounts, dragons, recipes, items, fish, badges |
updates |
archived game-update files, browsable and diffable per patch |
mods · modpacks |
.tmod tooling plus a hub for sharing and bundling mods |
events |
a live SSE stream so clients stop polling on the hour |
ocr |
reads a character stat sheet from a screenshot (self-hosted, no LLM) |
btt · misc |
Better Trove Tools release feeds, time tools, server status, supporters |
The API container also serves the Better Trove Tools website out of site/ — the landing
page, user manual, command reference, and live pages for leaderboards, player/class activity,
codexes, the mods hub, modpacks, the marketplace, game updates, server status, and server time.
Browsing is public; developing and publishing mods is a Discord-login action.
cp .env.example .env # then set SECRET_KEY, MONGO_*, REDIS_PASSWORD (+ captcha/SMTP)
docker compose up -d --build
curl http://127.0.0.1:15546/health # {"status":"ok"}Data lives under ./data (bind-mounted). There's no migration code: when a model's
indexes change, wipe the data dir and rebuild.
See .env.example for the full list.
| Variable | Required | Notes |
|---|---|---|
SECRET_KEY |
yes | JWT signing key — python -c "import secrets;print(secrets.token_urlsafe(48))" |
MONGO_ROOT_PASSWORD / MONGO_APP_PASSWORD |
yes | Mongo root + least-privilege app user |
REDIS_PASSWORD |
yes | Redis auth |
CAPTCHA_SECRET / CAPTCHA_SITEKEY |
prod | Captcha is enforced only when both are set |
ADMIN_EMAIL / ADMIN_PASSWORD |
no | Bootstraps the master superuser on startup |
SMTP_HOST (+ SMTP_*, MAIL_FROM) |
prod | Mail relay; if unset, email is logged instead of sent |
GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET |
no | Enables "Sign in with GitHub" |
All services bind to 127.0.0.1; the proxy routes api.aallyn.net → :15546 (only /v1/*
/health),dev.aallyn.net→:25470,docs.aallyn.net→:25468, andtrove.aallyn.net→:15546(all paths). Run Uvicorn with--proxy-headersso client IPs are correct for rate limiting. Bodies are capped at 8 MB, except the.tmodtools and bot cfg ingests (/v1/mods/*,/v1/leaderboards/insert,/v1/market/insert) at 20 MB — allow at least as much at the proxy.
Feature modules grouped by endpoint path; cross-cutting infrastructure in app/core/.
app/
├── main.py # app assembly: routers, middleware, lifespan
├── core/ # config, db, security, errors, redis, mailer, ratelimit, pagination, …
├── auth/ # signup, login, sessions, oauth, account
├── tokens/ # mint/list/edit/rotate/revoke API tokens
├── trove/ # the Trove data surfaces (rotations, leaderboards, mods, codexes, …)
├── site_auth/ # Discord login for the showcase site (mods/modpacks)
├── admin/ # superuser metrics, toggles, moderation
└── … # usage, pageviews, events, scanning
site/ # showcase website (templates + static)
portal/ # developer-portal SPA
docs/ # static docs (guide + Redoc reference + llms.txt)
The platform is built so a new endpoint is a small, consistent addition:
- Create
app/<feature>/withrouter.py(+models.pyif it stores data). - Gate it with
Depends(require_scope("<resource>:<action>"))and append the new scope toapp/core/scopes.py(bits are permanent — never renumber or reuse). - Use
paginate_newest_first(...)for lists; writes honourIdempotency-Keyautomatically. - Raise
APIError(status, ErrorCode.x, "msg")— never a bareHTTPException. - Register the router in
app/main.pyand any newDocumentinapp/core/database.py.
Every error uses one envelope (branch on code, not message); each carries a request_id:
{ "error": { "code": "rate_limited", "message": "…", "details": null, "request_id": "req_…" } }Default limits: signup 5/h/IP · login 10/5min/IP · API 120/min/token · token creation 3/day.
python -m venv .venv && . .venv/bin/activate # 3.11–3.13
pip install -r requirements-dev.txt
ruff check app tests # lint
pytest tests/unit # unit tests (no services needed)
pytest tests/integration -m integration # needs Docker (testcontainers spin up Mongo + Redis)scripts/backup-mongo.sh (gzip mongodump) and scripts/restore-mongo.sh (--drop
restore). Schedule the backup via cron and keep an off-box copy.