LDS Stake Portal

Architecture

How the three services fit together and how requests flow through the system.

The portal is composed of three independent services. Each runs in its own process and communicates over HTTP or WebSocket — there is no shared in-process state between them.

ServiceStackPort
FrontendReact 19 SPA + Express proxy3100
BackendFastAPI REST API8000
Discord botdiscord.py + FastAPI (HTTP server)8001

Request flow

Browser
  └─▶ React SPA (port 3100)
        └─▶ /api/* ▶ Express proxy ▶ FastAPI (port 8000)

Discord API ◀▶ Discord bot (port 8001) ◀▶ FastAPI (port 8000)
Architecture diagram

Express proxy

The Express server is a pure proxy. It does no session management, no authentication, and no business logic. Every request that matches /api/* is forwarded to FastAPI with the /api prefix stripped. The React SPA never calls FastAPI directly — all API calls go through the proxy so that cookies are scoped to the same origin.

express-session, passport, and connect-pg-simple appear in package.json but are not used. They are legacy from an earlier design. Do not add Passport-based auth.

FastAPI backend

FastAPI owns all business logic, authentication, and database access. It exposes a REST API on port 8000. In production the proxy forwards requests to it; in development it can also be accessed directly at http://localhost:8000/docs for Swagger UI exploration.

Discord bot

The Discord bot runs as two concurrent processes in the same service: a Discord.py gateway client (receiving events from Discord) and a FastAPI HTTP server on port 8001 (receiving webhook calls from the main backend). The bot notifies approvers via Discord DM when calling proposals advance stages, handles building reservation notifications, and exposes slash commands for database backups. It communicates with the main backend via HTTP using the BACKEND_URL and BACKEND_TOKEN environment variables.


Local development startup order

Start services in this order to avoid connection errors:

cd backend
uv run python main.py

The backend must be up first because the frontend proxy will immediately begin forwarding requests to it.

cd frontend
npm run dev

Opens the React SPA and Express proxy on port 3100.

cd discordbot
uv sync
uv run python main.py

Only needed when working on Discord bot features. The main backend must be running first.


Service boundaries

Each service has its own dependency management and is started independently:

  • Frontend uses npm (Node.js). Run all frontend commands from frontend/.
  • Backend uses uv (Python). Run all backend commands from backend/. Never use python, pip, venv, or conda directly.
  • Discord bot uses uv (Python). Run all Discord bot commands from discordbot/. The virtual environment is separate from backend/ — a uv sync in one does not affect the other.

The backend and discordbot both use uv (Python), but their pyproject.toml files and virtual environments are completely separate. A uv sync in backend/ does not affect discordbot/ and vice versa.

  • Frontend — React SPA conventions: routing, data fetching, and component library
  • Backend — FastAPI routers, the CallingUser dependency, and startup sequence
  • Authentication — two-token JWT strategy
  • Manual Deployment — running services locally from source

On this page