Day One — Developer Setup Runbook¶
Welcome to Evospin. By the end of this page you will have the entire platform running locally, a test bet placed, and a trace visible in Jaeger. Every command is copy-pasteable.
1. Prerequisites¶
Install these before you begin:
| Tool | Version | Why |
|---|---|---|
Docker (with the compose plugin) |
24+ | The unified compose file boots the entire stack |
| Node.js | 22 LTS | NestJS 11 + Prisma 7 require Node 22 (≥20.19 minimum); used for host-side tooling |
| pnpm | 9.11.0 | Package manager for ebit-fe and ebit-admin-fe |
| npm | 10+ | Package manager for ebit-api (do not use pnpm there) |
| git | 2.40+ | Three sibling repos |
| AWS CLI v2 | latest | ECR pulls when working against perf infra |
| Doppler CLI | latest | Secrets fetch (optional — see §3 Option B if you don't have access) |
| k6 | 0.50+ | Smoke perf test on week 1 (curriculum.md §"Operational toolkit") |
| jq | latest | JSON wrangling in verification scripts |
| gh (GitHub CLI) | optional | Convenient for PR workflows |
Hardware: 16 GB RAM recommended (8 GB absolute minimum). First build peaks at ~7 GB; steady state is ~3.7 GB. You need at least 40 GB free disk for images + Postgres volume growth.
Verify everything is installed (each line should print a version, not "command not found"):
docker compose version # must show v2.x
docker info | grep "Total Memory" # confirm >= 8 GiB
node -v # v22.x
pnpm -v # 9.11.0
npm -v # 10+
aws --version
doppler --version
k6 version
jq --version
git --version
2. Clone the workspace¶
The workspace is three sibling repos under one parent directory, plus shared infrastructure files at the root:
cd /home/ubuntu
git clone <your-ebit-api-repo-url> ebit/ebit-api
git clone <your-ebit-fe-repo-url> ebit/ebit-fe
git clone <your-ebit-admin-fe-repo-url> ebit/ebit-admin-fe
The root-level files (docker-compose.yml, .env.example, README.md, observability/, docs/, tests-e2e/) live in the parent ebit/ directory. If you received the workspace as a single archive, everything is already in place.
3. Environment setup¶
Option A: Doppler (internal team)¶
If you have Doppler access:
doppler login # opens browser
doppler projects # confirm "ebit" appears
doppler configs --project ebit # confirm "dev_perf" appears
doppler secrets --project ebit --config dev_perf | head -5 # confirm read access
export DOPPLER_TOKEN=$(doppler configs tokens create dev-local --max-age 0 --plain)
If doppler projects doesn't list ebit, workspace membership has to be granted manually — ping your team lead. You can fall back to Option B below for local-only work, but you'll hit a wall on week 1 when perf scripts run.
Option B: Plain .env (external hires / no Doppler)¶
The compose file works without Doppler. Copy the example and you are done:
The .env.example ships with safe local defaults: JWT_SECRET, ADMIN_DEFAULT_PASSWORD=admin, placeholder reCAPTCHA keys, and all host port mappings. Review README.md for the full variable reference.
The NestJS backend also reads ebit-api/.local.env — this file is already committed and requires no changes for local development.
4. Build and boot¶
First build takes 12-15 minutes cold (layer cache cuts subsequent rebuilds to ~1 minute per image). Boot proceeds in stages enforced by depends_on — verify each stage reached (healthy) before expecting the next one to work.
Stage 1 — Infrastructure¶
Postgres, Redis (cache), Redis (bot), RabbitMQ.
docker compose ps ebit-db ebit-redis ebit-redis-bot ebit-rabbitmq
# STATUS column should read "(healthy)" for all four
Stage 2 — Prisma migrate + seed¶
ebit-prisma-migrate runs prisma migrate deploy && prisma db seed with DEBUG_SEED_LOCAL=true, then exits with code 0. This creates the schema, seeds house games, permissions, site config, and the local test accounts.
docker compose logs -f ebit-prisma-migrate # tail until container exits
docker compose ps ebit-prisma-migrate # STATUS should read "Exited (0)"
Stage 3 — NestJS apps¶
ebit-api (:4000), ebit-rt (:4001), ebit-bj (:4002), ebit-bo (:4003), ebit-speed-roulette (:4004) start in parallel.
docker compose logs ebit-api | grep "Nest application successfully started"
docker compose ps ebit-api ebit-rt ebit-bj ebit-bo ebit-speed-roulette
# all five should be "(healthy)"
Stage 4 — Frontends¶
ebit-fe (:3000) and ebit-admin-fe (Vite SPA — host :5173 / compose :3003) start once ebit-api is healthy.
Stage 5 — Observability¶
otel-collector, jaeger, prometheus, loki, grafana boot alongside the apps.
All five stages reaching (healthy) typically takes 3-5 minutes after the build completes. If any service is stuck unhealthy, see the Troubleshooting section below.
5. Verify the stack¶
Open each URL and confirm it loads:
| What | URL | Expected |
|---|---|---|
| REST API | http://localhost:4000/swagger | Swagger UI with all endpoints listed |
| Public site (dropbet) | http://localhost:3000 | Casino homepage with game tiles |
| Admin panel | http://localhost:3003 | Login page (Vite SPA from compose) |
| Jaeger (traces) | http://localhost:16686 | Service dropdown lists ebit-api after first request |
| Grafana (dashboards) | http://localhost:3003 | Login: admin / grafana — note port collision with admin-fe compose; use only one at a time, or run admin-fe on host with pnpm dev at :5173 |
| Prometheus | http://localhost:9090 | Query interface |
Known sharp edge — admin panel sign-in. The admin-fe has four stacked integration bugs that block dashboard rendering after sign-in (cookie-name mismatch
access_tokenvsjwt_access_token, missing OTel instrumentation, nopropagateContextUrls, hard-coded API host). You'll see a blank page after sign-in. The API works fine — use Swagger (http://localhost:4000/swagger) for admin operations until the integration bugs are fixed. Full details:docs/flows/admin-sign-in.md.
If any service is unhealthy, check its logs:
docker compose logs --tail=50 ebit-api # or any service name
docker compose ps # shows restart count + health status
See the Troubleshooting section in the workspace README for common issues (CORS errors, port conflicts, OOM during build).
6. Seeded test accounts¶
The Prisma seed (libs/_prisma/src/seed/index.ts) creates these accounts when DEBUG_SEED_LOCAL=true (set automatically by the compose migrate job):
Public site — dropbet (http://localhost:3000)¶
| Field | Value |
|---|---|
local@example.com |
|
| Password | password |
| Username | local |
| Starting balance | 1,000 DBC |
| KYC level | LEVEL_0 |
| Email verified | yes |
Nine additional accounts (local-1@example.com through local-9@example.com, same password) are also seeded for multi-user testing.
Source: libs/_prisma/src/seed/debug/rich-user.ts:5-42 (seedRichUser).
Admin panel (http://localhost:3001)¶
| Field | Value |
|---|---|
| Username | admin |
| Password | admin (from ADMIN_DEFAULT_PASSWORD in .env) |
A second admin account with 2FA enabled is also seeded:
| Field | Value |
|---|---|
admin-1@admin.com |
|
| Password | admin |
| Username | admin-1 |
| TOTP secret | O4JWQM2YBARTYJBZ |
| Role | SuperAdmin |
Source: libs/_prisma/src/seed/debug/rich-user.ts:44-80 (seed2faAdminUser).
Resetting corrupted seed data¶
If you accidentally ban the test user, corrupt the password hash, or otherwise break the seeded state:
This drops all tables, re-runs migrations, and re-seeds. All data is destroyed — only use this when you need a clean slate. For a lighter touch, restart just the seed:
The seed uses upsert with update: {}, so re-running it is safe — existing accounts are left untouched, missing ones are created.
7. Place your first bet¶
This walkthrough proves the full stack is working: frontend, API, Prisma, Redis, BullMQ, and OTel traces.
-
Sign in — open http://localhost:3000, click Sign In, enter
local@example.com/password. -
Navigate to Dice — from the homepage, find the Dice game tile (under Original Games or Casino) and click it.
-
Place a bet — set a small bet amount (e.g., 1 DBC), choose your threshold and direction (over/under), and click Roll.
-
Check the result — the UI shows your roll outcome, multiplier, and updated balance.
-
Find the trace in Jaeger — open http://localhost:16686:
- Service dropdown →
ebit-api - Operation dropdown →
POST /casino/games/house/dice/bets - Click Find Traces, open the most recent one
The full span waterfall:
- HTTP middleware + guards (~4 ms)
- @PlaceBetLock Redis mutex acquisition
- prisma:client:operation — balance deduction + bet INSERT inside a transaction
- popUserSeed — fairness seed retrieval
- Dice RNG computation + settlement
- bet_settled_queue BullMQ enqueue (ioredis EVALSHA span)
Known trace gap.
ebit-bjandebit-speed-rouletteare called via Nest's Redis pub/sub transport (@ExternalControllerClient) — that transport doesn't propagate W3Ctraceparent, so callee spans appear as orphan roots, not children of the originating request. Don't panic: this is documented (AF-2 indocs/architecture.md) and used as a triage cue in the incident runbook.
If the trace is missing entirely, follow docs/runbooks/trace-missing.md.
- Check Grafana — open http://localhost:3003, navigate to the "Service Overview" dashboard. Your bet should appear as a data point on the ebit-api rate/error/duration panels.
For a deeper understanding of the bet pipeline, read docs/flows/dropbet-bet-place.md.
8. Explore the architecture¶
Now that you have a working stack and a traced bet, read the architecture doc for the full picture:
-
docs/architecture.md — C4 diagrams (context, container, component), runtime scenario index, data model overview, observability catalog, and aggregated known weaknesses.
-
docs/observability.md — how traces, metrics, and logs are produced, correlated, and queried.
-
docs/flows/ — 15 individual flow docs, each with sequence diagrams, Jaeger trace IDs, span timings, and failure-mode analysis. Start with:
dropbet-sign-in.md— auth flow, cookie contract, 2FAdropbet-bet-place.md— the shared bet pipeline you just exercisedrt-websocket.md— socket.io real-time events
9. Sharp edges to remember beyond day one¶
You met the admin-fe sign-in issue (§5) and the orphan-trace gap (§7) inline. A few more that will bite you later in week 1 if you're not warned:
The "users online" counter is intentionally inflated¶
The websocket broadcast UsersOnlineUpdated adds a fakeUserOnline padding (starts at 500, drifts by ±5 every 10 seconds, floor 180) to the real Redis ONLINE_USERS_KEY zcard. The number in the UI is cosmetic, not operational. See docs/flows/rt-websocket.md §6.3.
Package manager discipline¶
| Repo | Use | Do NOT use |
|---|---|---|
ebit-api/ |
npm install |
pnpm install (corrupts lockfile) |
ebit-fe/ |
pnpm install |
npm install (corrupts lockfile) |
ebit-admin-fe/ |
pnpm install |
npm install (corrupts lockfile) |
Prisma commands need env-cmd¶
Never call npx prisma directly in ebit-api/. Always use the npm scripts (npm run db:migrate:dev, npm run db:seed, npm run db:reset, npm run prisma:generate) — they wrap prisma with env-cmd -f .env so the database URL is set correctly.
RabbitMQ is running but unused¶
The ebit-rabbitmq container starts and passes healthchecks, but receives zero traffic. The Fast Track integration module is stubbed with disabled = true. This is intentional — see the comment block above the service in docker-compose.yml and AF-6 in docs/architecture.md.
10. Exit checklist¶
You are done with day-one setup when all of these are true:
- [ ] All required CLI tools installed (
docker,node 22,pnpm 9.11,aws,doppler,k6,jq,git) - [ ] (Option A only)
doppler projectslistsebit,dev_perfconfig is readable - [ ] All three repos cloned under
~/ebit/ - [ ]
docker compose psshows all services as(healthy)with zero restarts - [ ] Swagger loads at http://localhost:4000/swagger
- [ ] Dropbet loads at http://localhost:3000 and you can sign in with
local@example.com/password - [ ] You placed a bet and saw the balance update
- [ ] The bet trace appears in Jaeger at http://localhost:16686 under
ebit-apioperationPOST /casino/games/house/dice/bets - [ ] Grafana loads at http://localhost:3003 (
admin/grafana) - [ ] You have read
docs/architecture.mdsections 1-4 (context, containers, components, runtime scenarios)
Next step: the two-week curriculum — its "Operational toolkit" subsection covers the week-1 perf/Grafana/outage-drill work that previously lived in handover/onboarding-day-7.md.