Skip to content

Service Index ​

Eight deployable apps and five shared packages. Each service page covers what it owns, how to run it, its configuration, and how it fails.

Services ​

ServicePackagePortTypeImage
api@playpals/api4000Express RESTregistry.digitalocean.com/playpalzproduction/api
esu@playpals/esu4010Socket.IO…/esu
ogun@playpals/ogun—BullMQ worker…/ogun
anansi@playpals/anansi4005Cron + admin HTTP…/anansi
igdb-heartbeat@playpals/igdb-heartbeat—Cron…/igdb-heartbeat
web@playpals/web3000Next.js 15…/web
admin@playpals/admin3001Next.js 16…/admin
mobile@playpals/mobile—Expo / React NativeEAS build
docs@playpals/docs5173VitePress…/docs

What each one owns ​

api — the centre of gravity. Every REST endpoint, all authentication, all authorization, and the only writer for most domains. If you are unsure where logic belongs, it belongs here.

esu — holds WebSocket connections and fans out events. Stores nothing, decides nothing. Named for the Yoruba deity of language and exchange.

ogun — consumes the media-processing queue: image variants with Sharp, video assets with Mux. No HTTP surface at all. Named for the deity of iron and transformation.

anansi — calculates creator earnings, writes the double-entry ledger, and executes Stripe payouts on a two-phase monthly cycle. Named for the trickster spider associated with webs of connection.

igdb-heartbeat — keeps the game catalog in sync with IGDB on a schedule, and mirrors cover art into Spaces.

web — the public marketing site. Minimal; the product is the mobile app.

mobile — the product itself. Expo SDK 54 with expo-router.

Shared packages ​

PackagePurpose
@playpals/dbPrisma schema, generated client, and query helpers. Imported by every backend service.
@playpals/queueThe BullMQ queue definition, so producer and consumer cannot disagree on its name or retry policy.
@playpals/typesShared types, including the socket event contract between the app and esu.
@playpals/socket-clientTyped Socket.IO client plus the useRealtime React hook the mobile app uses.
@playpals/transactionalEmail templates. Not yet wired to a provider.
@playpals/tsconfigsShared TypeScript configurations.

Choosing where code goes ​

The work is…It goes in
A new REST endpointapi
A new realtime eventpackages/types + esu, triggered from api
Slow, retryable, or CPU-heavyogun (or a new worker on the same queue)
Money, ledger, or payout relatedanansi
Scheduled and independent of user requestsA cron service, following igdb-heartbeat
Reusable across two or more servicespackages/*

Adding a whole new service is rarely the answer. The existing five cover HTTP, sockets, queues, and cron; most features are a new module inside one of them.

Health endpoints ​

ServiceEndpointHealthy response
apiGET /health{"status":"ok"}
esuGET /health{"ok":true}
anansiGET /healthHealth payload (unauthenticated)
webGET /200
ogun—No HTTP surface; liveness is inferred from logs and queue drain
igdb-heartbeatGET /health200

ogun has no health probe

The deployment cannot tell Kubernetes whether the worker is actually consuming jobs. A wedged worker looks identical to a healthy one. Adding a small HTTP server that reports queue connectivity would make it restartable on failure.

Internal documentation — PlayPalz platform