Ports & Endpoints
Local ports
| Port | Service | Start with |
|---|---|---|
| 3000 | web (Next.js) | pnpm --filter @playpals/web dev |
| 4000 | api | pnpm --filter @playpals/api dev |
| 4005 | anansi | pnpm --filter @playpals/anansi dev |
| 4006 | igdb-heartbeat | pnpm --filter @playpals/igdb-heartbeat dev |
| 4010 | esu | pnpm --filter @playpals/esu dev |
| 5173 | docs | pnpm --filter @playpals/docs dev |
| 5432 | PostgreSQL | docker compose up -d db |
| 6379 | Redis | docker compose up -d redis |
| 7880 | LiveKit HTTP | docker compose up -d livekit |
| 7881 | LiveKit RTC/TCP | |
| 7882/udp | LiveKit RTC/UDP |
ogun has no port — it is a queue worker.
bash
lsof -i :4000 # find what is holding a portProduction hosts
| Host | Backs onto | Port |
|---|---|---|
playpalz.gg | playpalz-web | 3000 |
www.playpalz.gg | playpalz-web | 3000 |
api.playpalz.gg | playpalz-api | 4000 |
socket.playpalz.gg | playpalz-esu | 4010 |
ws.playpalz.gg | LiveKit droplet | 7880 |
media.playpalz.gg | Spaces CDN | — |
grafana.playpalz.gg | loki-stack-grafana | 80 |
anansi, ogun, and igdb-heartbeat have no ingress — they are cluster-internal only.
Cluster-internal DNS
| Name | Service |
|---|---|
playpalz-api:4000 | api |
playpalz-esu:4010 | esu |
playpalz-anansi:4005 | anansi |
playpalz-redis:6379 | redis |
loki-stack:3100 | Loki |
Health endpoints
| Service | Endpoint | Healthy |
|---|---|---|
| api | GET /health | {"status":"ok"} |
| esu | GET /health | {"ok":true} |
| anansi | GET /health | Health payload |
| igdb-heartbeat | GET /health | {"status":"ok","service":"igdb-heartbeat"} |
| web | GET / | 200 |
| ogun | — | No HTTP surface |
/health sits outside /api/v1 on the API, so it is not rate limited.
API base
| Environment | Base URL |
|---|---|
| Local | http://localhost:4000/api/v1 |
| Production | https://api.playpalz.gg/api/v1 |
Everything in the API Reference is relative to that.
Webhook URLs
| Provider | URL |
|---|---|
| RevenueCat | https://api.playpalz.gg/api/v1/webhooks/revenuecat |
| Mux | https://api.playpalz.gg/api/v1/webhooks/mux |
| Stripe | https://api.playpalz.gg/api/v1/webhooks/stripe (handler is empty) |
Locally, expose them with a tunnel:
bash
ngrok http 4000Admin surfaces
| Surface | Where | Auth |
|---|---|---|
| esu interservice | POST {esu}/admin/emit/* | X-Realtime-Admin-Token |
| esu authz callbacks | GET {api}/api/v1/{dm,room}/:id/authz | The user's own bearer token |
| anansi payouts | {anansi}/admin/payout/* | Authorization: Bearer <ADMIN_SECRET> |
| Socket.IO Admin UI | admin.socket.io → socket.playpalz.gg | Basic auth, disabled by default |
| Grafana | grafana.playpalz.gg | grafana-secrets |
Data store connections
| Store | Local | Production |
|---|---|---|
| PostgreSQL | localhost:5432 (postgres/postgres) | Managed cluster on :25060, private VPC hostname |
| Redis | localhost:6379 (password playpalz) | playpalz-redis:6379 in-cluster |
| Spaces | — | nyc3.digitaloceanspaces.com, bucket playpalz-media |
The production database listens on 25060, not 5432 — DigitalOcean's managed Postgres default.
