# 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](../spec/PLAYERPAL_AUTH_IDENTITY_SPEC.md) (wins on conflict). Topic pages under [docs/](README.md) are the day-to-day source of truth.

## Design principle

```text
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 issues `pp_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](ingestion-and-availability.md) and ADR 0015.

## Components

```text
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](decisions/0021-nestjs-zitadel-local-poc.md)); 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):

0. Contract and failure proof. Freeze identifiers and error codes; prove play continues while PlayerPal is blocked; no-email IAM POC.
1. 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.
2. Production hardening. Provider outbox, gzip batches, idempotency, object-storage profile, key rotation, SLOs.
3. Scale where observed. Redpanda, analytical store, regional ingestion, policy engine only if application authZ is no longer enough.

## Related ADRs

- [0001](decisions/0001-generic-party-model.md) generic party model
- [0004](decisions/0004-async-first-ingestion.md) async-first ingestion
- [0005](decisions/0005-phased-event-log-infrastructure.md) Postgres inbox then Redpanda
- [0011](decisions/0011-iam-authenticates-playerpal-owns-identity.md) IAM vs identity graph
- [0015](decisions/0015-async-loyalty-sidecar.md) off the game path
- [0026](decisions/0026-analytics-subject-and-provider-cohort.md) analytics subject and cohort
