A player account and leaderboard backend for an Unreal Engine 5 hack-and-slash prototype. The UE 5.8 client submits combo runs over HTTPS to a FastAPI service on AWS ECS Fargate, backed by PostgreSQL on RDS. Everything is provisioned with Terraform and deployed through OIDC-federated GitHub Actions.
Status: In progress — see Roadmap.
Real-time gameplay never touches a database, but almost everything around it does: accounts, progression, leaderboards, telemetry. This project builds that "around it" layer the way a live-service studio would, scoped down to one feature so every piece can be done properly instead of half-done.
The game side is a Narrative Pro / Gameplay Ability System melee prototype I'm building separately. The max_combo field in the schema comes straight from the GAS combo-counter attribute, so the leaderboard reflects real gameplay data rather than mock values.
flowchart LR
subgraph Client
UE[UE 5.8 Client<br/>HTTP + Json modules]
end
subgraph AWS["AWS (us-east-1)"]
ALB[Application Load Balancer<br/>HTTPS / ACM]
subgraph VPC
subgraph Private Subnets
ECS[ECS Fargate<br/>FastAPI]
RDS[(RDS PostgreSQL 16)]
end
end
SM[Secrets Manager]
ECR[ECR]
CW[CloudWatch Logs + Alarms]
end
subgraph CI["GitHub Actions (OIDC)"]
CI1[ci.yml<br/>lint · test · build]
CI2[deploy.yml<br/>push ECR · migrate · roll ECS]
CI3[infra.yml<br/>terraform plan / apply]
end
UE -->|POST /runs<br/>GET /leaderboard| ALB --> ECS --> RDS
ECS -.pulls secrets.-> SM
ECS -.logs.-> CW
CI2 --> ECR --> ECS
CI3 --> AWS
Request flow: the client logs in and receives a short-lived JWT. At the end of a run it POSTs score, max_combo, and duration_ms. The leaderboard is a Postgres view returning each player's best run, read by the main-menu widget.
| Layer | Choice | Why |
|---|---|---|
| Game client | Unreal Engine 5.8, C++ UGameInstanceSubsystem |
Native HTTP/Json modules, no third-party REST plugin |
| API | Python 3.12, FastAPI, SQLAlchemy 2, asyncpg | Async, typed, fast to test |
| Database | PostgreSQL 16 on RDS (db.t4g.micro) |
Industry default for game backends |
| Auth | JWT (HS256), bcrypt password hashing | Simple and sufficient for scope |
| Compute | ECS Fargate | No cluster to manage; complements the EKS work in WellWatch |
| Infra | Terraform, S3 + DynamoDB remote state | Reusable modules, plan-on-PR |
| CI/CD | GitHub Actions with OIDC (no long-lived AWS keys) | Same federation pattern as WellWatch |
| Observability | CloudWatch Logs Insights, CPU / 5xx / DB-connection alarms | Structured JSON logs from the API |
| Security scanning | checkov in CI |
Catches IaC misconfigurations before apply |
combat_ledger/
├── api/ FastAPI service, tests, Dockerfile
├── db/ Numbered SQL migrations
├── infra/ Terraform — envs/dev, reusable modules/
├── unreal/ C++ subsystem, USTRUCTs, widget screenshots
├── docs/ Diagram source, demo GIF
└── .github/ ci.yml · deploy.yml · infra.yml
Requires Docker and Docker Compose.
git clone https://github.com/huicodes/combat_ledger
cd combat_ledger
docker compose up --buildThis starts Postgres 16, runs migrations, and serves the API at http://localhost:8080. Interactive docs at http://localhost:8080/docs.
Quick smoke test:
curl -X POST localhost:8080/auth/register \
-H 'Content-Type: application/json' \
-d '{"username":"henry","password":"hunter2"}'Point the Unreal client at localhost:8080 via Project Settings → Combat Ledger → Base URL.
| Method | Path | Auth | Description |
|---|---|---|---|
| POST | /auth/register |
— | Create account, returns JWT |
| POST | /auth/login |
— | Returns JWT |
| POST | /runs |
Bearer | Submit a completed run |
| GET | /leaderboard?limit=50 |
— | Best run per player, ranked by score |
| GET | /players/me |
Bearer | Current player profile |
| GET | /healthz |
— | ALB health check |
- Bootstrap remote state (
infra/bootstrap/) — one time. - Open a PR touching
infra/**;infra.ymlposts the Terraform plan as a comment. - Merge → apply runs behind a manual environment approval.
- Push to
main→deploy.ymlbuilds the image, pushes to ECR tagged with the commit SHA, runs migrations as a one-off ECS task, and forces a new service deployment.
Roughly $20–40 / month while up (Fargate 0.25 vCPU task + db.t4g.micro + ALB, no NAT gateway — VPC endpoints for ECR, Secrets Manager, and CloudWatch instead). terraform destroy when not demoing.
- Client-authoritative scoring. The client reports its own score. A production game validates runs on a dedicated server — see roadmap.
- Single region, single environment. No blue/green, no multi-AZ RDS.
- HS256 JWTs with a shared secret. Fine here; a real deployment would use RS256 or a managed identity provider (Cognito).
- Schema and migrations
- FastAPI service with tests and Docker Compose
- Terraform: VPC, RDS, ECS Fargate, ALB, Secrets Manager
- CI/CD workflows with OIDC
- Unreal HTTP subsystem and leaderboard widget
- Observability and
checkovhardening - Demo GIF and cost numbers
- Stretch: Linux dedicated server on GameLift Anywhere with server-authoritative scoring
- Stretch: Redis-backed leaderboard cache
- WellWatch — multi-cloud IaC (AWS + Azure, EKS, Ansible)
- TradeWatch — production support and trade analytics pipeline
