Skip to content
AallynReedPublic

About

The all-in-one Trove companion — leaderboards, market prices, mods hub, gems & planners — plus the Kiwi developer API that powers it.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

Kiwi API

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.


Links

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

Stack

Rust (native/): axum + tokio · MongoDB 7 · Redis 7 · PostgreSQL (leaderboards, market, codexes) · Argon2 + JWT auth · hashed API tokens · minijinja templates for the website.

Platform features

  • 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-After headers.
  • Usage & site analytics — every authenticated request is recorded and aggregated. The website counts page views with its own small script (no cookies, bots and prefetches left out) and groups them into visits at query time: channels and sources, entry and exit pages, sections, devices, browsers, languages, time on page and bounce rate, compared with the previous period, plus a live visitor count. Mod creators see the same for their own mods.
  • 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.

Data API (/v1)

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, player sigil images, and a stateless gem simulator / build optimizer (the game's own stat rolls, level-up odds and costs)
leaderboards · activity hourly in-game boards (with cheat detection) and player/class activity estimates
market in-game marketplace listings and price history
store the in-game cash-shop catalog — products, prices, sales, deals, lootbox odds
codexes decoded game catalogs — 18 collectible types, plus the relationships between them (what crafts what, what unlocks what), badge requirements and progression trees
updates archived game-update files, browsable and diffable per patch
mods · modpacks .tmod tooling plus a hub for sharing and bundling mods
embed short-lived preview tokens for the embeddable 3D/VFX viewer other sites can iframe
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

Showcase site

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.

Game data rebuilds itself after each patch. Everything under assets/gamedata/ that comes from the game files (class stats and abilities, allies, mounts, gems, rings, PvP curves, delve modifiers, companions, fish, mementos, badges, recipes, wings/boats/sails/auras/Mag Riders/flasks/tomes/fishing poles, costumes, bomb skins, styles, titles, NPCs, blocks and placeables, quests and adventures, worlds, subclass powers, daily bonuses, leaderboards, items, upgrade trees, mastery, class rewards, lootbox odds, PvP power-ups, chat commands, the Power Rank / Mastery sigil art, gear rarities, forge rules and banners) has a decoder in native/kiwi-server/src/decode/. After the archiver syncs a new live-us patch, each decoder reruns and writes to GAMEDATA_DIR (./.gamedata, shared by the api, web and bot containers), which readers prefer over the repo copy. A decoder also reruns when its code_version in decode/registry.rs changes: bump it by hand when a decoder's output changes. One that fails, decodes nothing, or shrinks its output by more than 20% keeps serving the previous file and shows up in the dev panel's Game data tab with the error, where it can be rerun.

Regional maintenance. The admin panel's Configuration → Regional access setting (geo_maintenance_countries) takes a list of country codes; website visitors from those countries get the maintenance page (assets/maintenance.html, its voxel art drawn by scripts/build_maintenance_page.py) instead of any page. The web container resolves the country from the visitor's IP with DB-IP's free database, downloaded monthly to ./.geoip; nothing about the visitor is stored. The API, bot and BTT app are unaffected.

Its 3D model and particle-effect viewers are also embeddable: another site can iframe trove.aallyn.net/embed/viewer to preview a mod it hosts, a mod on the hub, a dressed character, or anything in the live game — a creature by its own prefab name, or a single blueprint filename, which resolves into the whole assembled creature. Framing is allowed per-origin (embed.allowed_origins), and a mod a partner uploads to preview is held in Redis for minutes, never written to disk. Partner guide: docs/embed.html; code in native/kiwi-server/src/embed/.

Run with Docker

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.

The api, web and bot containers run kiwi-server, kiwi-web and kiwi-bot from trove-rust:latest, a slim Debian image with the binaries, assets/ and the few tools they shell out to (git, a JRE for FFDec). builder runs kiwi-builder from trove-builder:latest, the same image plus SteamCMD and the small Python TroveUI's release scripts need.

Key configuration

See .env.example for the full list.

Variable Required Notes
SECRET_KEY yes JWT signing key — openssl rand -base64 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"

Behind a reverse proxy

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, and trove.aallyn.net → :15547 (all paths). Set FORWARDED_ALLOW_IPS to the address the proxy connects from: kiwi-server then takes the client IP from the rightmost X-Forwarded-For entry the proxy added, so rate limits key on the real visitor and a client-sent header is ignored. Never set it to *, which trusts the client's own leftmost entry. Bodies are capped at 8 MB, except the .tmod tools and bot cfg ingests (/v1/mods/*, /v1/leaderboards/insert, /v1/market/insert) at 20 MB, and the Sound Studio + Mod Workshop builds (/site/sound-studio/build, /site/mod-workshop/*, which upload a whole bank or a whole mod at once) at 32 MB, and the Blueprint Editor (/site/blueprint-editor/*, whose save carries a per-voxel edit list and whose .qb import carries four voxel grids) at 64 MB, and the Unlock Debug patcher (/site/unlock-debug, which uploads a whole game executable so seven bytes of it can change) at 160 MB — allow at least as much at the proxy.

Project layout

assets/         # data the binaries read: game data baseline, rigs, locales, codebooks
builder/        # the five TroveAPI Python files TroveUI's release scripts import
native/
├── kiwi-core/  # pure logic and file formats (no server types)
├── kiwi-pil/   # Pillow-identical image encoding and text
├── kiwi-zlib/  # classic zlib, byte for byte
├── kiwi-server/
│   ├── src/bin/        # kiwi-web, kiwi-bot, kiwi-builder (kiwi-server is main.rs)
│   ├── src/routes/     # route registry; feature areas live beside it in src/
│   ├── src/middleware/ # security headers, CSP, CORS, gzip, idempotency, request ids
│   └── src/starlette/  # the frozen route table 404/405/307 answers come from
└── port/       # the port's parity specs and fixture generators (history)
site/           # showcase website (templates + static)
portal/         # developer-portal SPA
docs/           # static docs (guide + Redoc reference + llms.txt)

Adding an endpoint

  1. Add a module under native/kiwi-server/src/ with a router() and merge it in router() (src/routes/mod.rs); the website's pages go in src/web/.
  2. Gate it with RequireScope<BIT> (or Public<BIT> for tokenless reads) and append a new scope to src/auth/scopes.rs (bits are permanent — never renumber or reuse).
  3. Return ApiError for failures so every error keeps the one envelope.
  4. Unmatched paths are answered from src/starlette/routes.json; when a route is removed, remove it there too or its path answers 500 instead of 404.

Errors & rate limits

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. Fan-out surfaces (codexes, the mods hub, updates file serving) meter in their own widened buckets — see the *_rate_limit_multiplier settings in native/kiwi-server/src/config.rs.

JSON responses over 1 KB are gzipped when the client offers it; streams are excluded by content type so SSE stays unbuffered (middleware/compression.rs).

Development

cargo fmt --all --manifest-path native/Cargo.toml
cargo clippy --workspace --all-targets --manifest-path native/Cargo.toml -- -D warnings
cargo test --workspace --manifest-path native/Cargo.toml   # store tests are #[ignore]d: they need Mongo/Redis/Postgres
cargo run --manifest-path native/Cargo.toml --bin kiwi-server   # reads .env; KIWI_BIND defaults to 0.0.0.0:8000

Run the binaries from the repo root so assets/ and site/ resolve. scripts/ holds standalone helpers (static minify, font subsetting, i18n audit, the maintenance-page generator).

Backups

scripts/backup-mongo.sh (gzip mongodump) and scripts/restore-mongo.sh (--drop restore). Schedule the backup via cron and keep an off-box copy.

About

The all-in-one Trove companion — leaderboards, market prices, mods hub, gems & planners — plus the Kiwi developer API that powers it.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Sponsor this project

Packages

Used by

Contributors

Languages