Architecture
PlayerPal is a privacy-preserving loyalty and analytics sidecar for operators, game providers, affiliates and reward partners. It maintains a persistent player account, loyalty ledger, campaign progress and analytics, and does not store a player's email address, name or telephone number.
This page is the system overview. Identity and authentication detail: spec/PLAYERPAL_AUTH_IDENTITY_SPEC.md (wins on conflict). Topic pages under docs/ are the day-to-day source of truth.
Design principle
ONE PRIVATE PLAYER IDENTITY
MANY AUDIENCE-SPECIFIC EXTERNAL ALIASES
NO PLAYER EMAIL STORED BY PLAYERPAL
- Players may register directly, from an operator, or from a provider when the provider is the integration entry point.
- A successful authentication resolves to one private PlayerPal player. The player can see
pp_plr_.... Operators and providers never receive it. - Connecting an operator issues
pp_plr_opr_.... Reporting activity through a provider-operator integration issuespp_plr_prv_.... - PlayerPal is not on the game critical path. If every PlayerPal service is down, the casino and provider must still authenticate the player, launch the game, accept bets, settle rounds and pay winnings. See ingestion-and-availability.md and ADR 0015.
Components
Player and admin clients
→ API gateway / BFF
→ IAM (Keycloak or ZITADEL) protocols, credentials, sessions
→ Identity Service players, aliases, links, consent
→ Authorization Service organisation, audience, resource
→ Ingestion API
→ durable inbox / object storage
→ Loyalty engine
→ Analytics pipeline
Logical components (spec §22):
| Component | Owns |
|---|---|
| IAM platform | OIDC/OAuth, passkeys, social federation, MFA, enterprise SSO, machine tokens |
| Player BFF | Browser session boundary |
| Identity Service | Players, auth mappings, aliases, links, Unlink, Delete Account |
| Integration Registry | Operators, providers, credentials, namespaces |
| Authorization Service | Organisation and resource decisions |
| Ingestion API | Authenticated batch acceptance (integration-scoped) |
| Event stream | Decoupled processing (Postgres inbox first; Redpanda/Kafka later) |
| Loyalty engine | Ledger, rules, campaigns |
| Analytics | Provider studio cohort and per-alias numbers; no partner-facing identity keys |
Do not store loyalty balances, aliases, operator connections or gameplay facts as IAM attributes. See ADR 0011.
Initial stack
Technology-neutral in the spec; pragmatic first deployment (spec §24, §35):
| Stage | Initial choice |
|---|---|
| IAM | ZITADEL for local POC (ADR 0021); production pick still needs §23.2 |
| Core API (control plane) | NestJS modular monolith (services/api) |
| Data-plane workers | Go (services/worker) when ingestion load justifies it |
| Identity / ledger | PostgreSQL (TypeORM DDL, ADR 0023) |
| Player web | React portals + NestJS BFF; popup + postMessage for embedded operator flows |
| Cache | Valkey/Redis where needed; never source of truth |
| Bulk landing | S3-compatible object storage |
| Batch jobs | Dagster |
| Observability | OpenTelemetry, Prometheus/Grafana |
Start as a modular monolith plus an independent ingestion worker. Split services only at measured hotspots. Airflow is not the default orchestrator.
Delivery
Spec §42 (preferred over the earlier four-phase sketch in §28 where they differ):
- Contract and failure proof. Freeze identifiers and error codes; prove play continues while PlayerPal is blocked; no-email IAM POC.
- Thin vertical slice. ZITADEL (local POC), Nest-owned passkey/password/social, one social method, Bucks Casino linking, Pragmatic Play alias in the link exchange, Postgres, points ledger, player wallet.
- Production hardening. Provider outbox, gzip batches, idempotency, object-storage profile, key rotation, SLOs.
- Scale where observed. Redpanda, analytical store, regional ingestion, policy engine only if application authZ is no longer enough.