Skip to content

Architecture

timeout edited this page Jul 7, 2026 · 1 revision

Mirrors docs/architecture.md in the repo.

Architecture

BotFleet is a single Next.js 16 (App Router) application backed by PostgreSQL via Prisma. There's no separate backend service yet - the admin dashboard, the admin API, and the customer API are all routes in the same app.

Layers

app/admin/**             Server-rendered dashboard pages (auth-guarded in
                          app/admin/layout.tsx)
app/api/admin/**          Admin API - every route calls requireAdmin()
app/api/customer/**       Customer portal API - every route scopes to the
                          signed-in user's own Customer/Bot rows
app/status                Public, unauthenticated status page
app/setup                 First-run setup checklist (public until an owner exists)
auth.ts                   NextAuth v5 config (Discord OAuth + Prisma adapter)
lib/db.ts                 Prisma client singleton (driver adapter: pg)
lib/crypto.ts              AES-256-GCM token vault
lib/plans.ts               Plan tier limits (free/starter/pro/enterprise)
lib/runner/*               RunnerAdapter interface + PM2/Docker stubs
lib/rebalance.ts           Pure worker-rebalancing recommendation algorithm
lib/worker-assignment.ts   Keeps Bot.workerGroupId, Worker.currentBots, and
                          WorkerAssignment in sync in one transaction
lib/security-checks.ts     Real, dynamically computed security report
lib/alerts/discord-webhook.ts   Discord embed alerts (mass mentions disabled)
prisma/schema.prisma       The full data model

Data model

  • User / Account / Session / VerificationToken - the standard Auth.js Prisma adapter shape, extended with discordUserId and role (owner / admin / member).
  • Customer - owned by a User (ownerUserId). Any signed-in user who owns a Customer row can access that customer's bots through /api/customer/*.
  • Bot - belongs to a Customer, optionally assigned to a Worker. Holds tokenEncrypted (never returned by any API), plan, guild limit, shard count, and status.
  • BotHealth - one-to-one with Bot: guild count, ping, memory, restart count, last safe error.
  • Worker / WorkerAssignment - a worker can run multiple bots (default 3-5, configurable via maxBots); WorkerAssignment is the join table, kept in sync with Bot.workerGroupId by lib/worker-assignment.ts.
  • Shard - per-bot shard rows (only populated once a bot needs sharding).
  • AuditLog - every admin/customer action that mutates state.
  • Alert / WebhookDestination - alert records and where they're posted.
  • Deployment - a record of what's deployed (not yet wired to an actual deploy pipeline).

Why bot start/stop/restart don't control a real process yet

lib/runner/types.ts defines a RunnerAdapter interface with start/stop/restart. pm2-adapter.ts and docker-adapter.ts both implement it today by updating Bot.status/BotHealth directly - they don't spawn or control anything. Every method has a TODO(real-runner) comment describing exactly what a real implementation needs (a worker process that decrypts the token in-memory and either runs it under PM2 or inside a Docker container). The rest of the product - dashboard actions, audit logging, alerts, rebalancing recommendations - is built against the interface, so wiring in a real runner later doesn't require changing any caller.

Worker rebalancing

lib/rebalance.ts is a pure function: given the current workers and bots, it recommends (a) assigning any unassigned bot to the least-loaded worker with spare capacity, and (b) moving bots off any worker that's over its maxBots. It never moves anything itself - an admin applies a recommendation from the bot's detail page, which calls PATCH /api/admin/bots/:id with a new workerGroupId.

Auth model

  • Admin/owner: promoted via BOTFLEET_ADMIN_DISCORD_IDS on first Discord sign-in (see auth.ts's session callback). Can use /admin and every /api/admin/* route. Owners can promote/demote other users' roles at /admin/users (the last remaining owner can never be demoted).
  • Any other signed-in user: can create/own Customer rows and manage their own bots via /api/customer/*, but has no access to /admin or /api/admin/*.

Clone this wiki locally