Architecture
The backend is one Bun process with MongoDB and Redis beside it. A question goes through it like this.
#A question's way
- The plugin posts to
/api/v2/chat.src/features/auth/caller.tsidentifies the server by its license orPLUGIN_TOKENand names its tenant;src/features/chat/validates the body and opens the SSE stream. src/features/billing/gate.tsdecides who pays: the plugin's own key, a license with money left, or the backend's key on a self-hosted backend. Limits count per tenant.src/agent/assembles the prompt fromsrc/prompts/*.md, the player's memories, and the session, then runs the Mastra agent.- When the model calls a game tool, the bridge in
src/features/chat/sends atool_callevent and waits for the plugin'sPOST /api/v2/chat/{run}/tools/{call}. - Server tools (
search_web,exact_calculations,remember,forget) run insrc/features/tools/. - The answer streams back. Afterwards the session is saved to Redis, folded into a summary when it grows too long, and the run's numbers land in MongoDB.
The bridge keeps pending calls in memory, so the backend runs as one instance.
#The code
| Path | What it does |
|---|---|
src/application/ |
Entrypoint, routes, shared dependencies |
src/features/chat/ |
POST /api/v2/chat: the SSE stream and the tool bridge |
src/features/ask, src/features/askx |
v1 endpoints; their request and response shapes stay |
src/features/tools/ |
Client tool definitions (run by the plugin) and the server tools |
src/features/users/ |
Sessions and memories for /allay new, /allay memory, /allay forget; a player's export and erasure |
src/features/auth/ |
Who sent a request, and its tenant |
src/infrastructure/database/migrations.ts |
What runs on start, and the migrate command |
src/agent/ |
The agent runner, prompt assembly, the session summarizer, askx command validation |
src/prompts/*.md |
The system prompts, bundled into the build |
src/infrastructure/ |
Redis sessions, memories in MongoDB, the OpenRouter provider, traces |
src/features/billing/, src/infrastructure/billing/ |
Who pays, plans, licenses, budget windows, payments |
src/features/telegram/ |
The payment bot: Stars invoices, payments, renewals, refunds |
src/features/settings/ |
Every setting with its type, bounds, and default (settings.definitions.ts) |
src/features/admin/, src/infrastructure/admin/ |
The admin panel's API, accounts, sessions, the audit log |
admin/ |
The panel: React, Vite, shadcn/ui, TanStack Router and Query |
docs/protocol-v2.md |
The contract with the plugin |
#Prompts
The prompts live in src/prompts: minecraft-ask.md, minecraft-askx.md, minecraft-task.md for automation agents, and telegram.md. They ship inside the build. Each run appends who asks, their language, and the server version.
#Settings
A setting is read through settings.get(...) where it's used: the environment first, then MongoDB, then the default. A save in the panel applies to the next run. Only what the backend needs to start reads process.env directly.
#Storage
- MongoDB: memories (by tenant and player), licenses and payments, admin accounts and the audit log, stored settings, the
runscollection with its rollups (players as pseudonyms), the player directory that names pseudonyms for the panel, and Mastra's traces. - Redis: chat sessions and their summaries, public and task threads, all keyed by tenant first, and admin panel sessions.
Next: Building from source