Skip to main content
InternalVerified against 53fd444Markdown

Personal data

This page describes player data only. Workforce identity (staff and partner admins) is a separate domain and may use business email (ADR 0012).

Policy​

Status: Implemented — evidence: ADR 0012, spec §2.2

  1. No direct identifiers. PlayerPal-controlled persistence, tokens, logs, traces, events and analytics must never contain a player's email, name or telephone number (ADR 0012).
  2. Pseudonymous data is personal data. PlayerPal treats pseudonymous player identifiers and gameplay data as personal data. The spec says they "may remain personal data under applicable privacy law even though they are not direct identifiers" and must be "protected, access-controlled, retained appropriately and included in privacy-impact analysis" (spec §2.2). This is a product policy, not a legal conclusion.

What we never receive​

Status: Implemented (code and tests) / Not yet verified live — evidence: oidc.service.ts, oidc.claims.spec.ts

DataHow it is kept out
Email, name, phone from sign-inPlayer OIDC requests scope openid only. Only issuer and subject are kept; email, name and phone claims are ignored. IdP raw profile data is stripped before return (oidc.service.ts; test "keeps only issuer and subject when the token carries email name phone" in oidc.claims.spec.ts)
Columns for those fieldsNone of the entities in db/entities has an email, name, phone, address or birth-date column. The schema notes "Never store email, name, or phone" (initial-schema.sql)
Direct identifiers in partner factsplayer_ref must match a restricted reference pattern and be an alias PlayerPal issued to that partner (ingest.schemas.ts; test "never accepts direct PII-shaped refs" in ingest.schemas.spec.ts)
Email-based linking or recoveryDisabled. Email magic links and SMS OTP are not player sign-in methods (ADR 0012, ADR 0028)
Operator credentials or session cookiesExcluded by the spec (spec §2.2)

Two caveats from the repo:

  • The IAM may require a synthetic placeholder email on its side when creating a user. PlayerPal never persists it (ADR 0012 amendment).
  • A live capture proving that social sign-in leaves no email, name or phone in PlayerPal cookies, database, logs or traces has not been done yet. Code filtering is in place; the live hop is not verified (open-questions.md).

Identifiers​

Status: Implemented — evidence: identity.md, ADR 0013

IdentifierWho may see itNotes
Internal UUIDv7PlayerPal onlyDatabase key, never exposed
pp_plr_...Player and PlayerPalNever sent to any partner
pp_plr_opr_...That operatorRandom per operator (ADR 0002)
pp_plr_prv_...That provider-operator integrationOne per integration, so a provider cannot join a player across casinos (ADR 0014)
pp_anl_... analytics subjectPlayerPal onlyNever on a player or partner API (ADR 0026)
IAM subjectPlayerPal onlyOpaque sign-in subject, not returned on /v1/me

Aliases are random 128-bit values, not derived from the UUID or any personal data. They are minted only for audiences on a completed link, never on activity ingest (ADR 0027). A foreign alias gets the same response as an unknown one (ADR 0014). Affiliate and reward-partner prefixes are reserved and not minted (ADR 0013).

Analytics subject is minted at Account activation, random, and used to cluster retained activity without the UUID (ADR 0026; tests in analytics-subject.spec.ts, including "writes the analytics subject onto processed round_settled and hides it from APIs").

IAM subject storage. The authentication_identities row holds (authentication-identity.entity.ts, identity.service.ts, common/ids.ts):

  • subject_lookup_hmac: HMAC-SHA256 of issuer and subject, keyed with a pepper (SUBJECT_PEPPER), used for lookup.
  • subject_ciphertext: despite the name, this is also a keyed HMAC of the subject (one-way), not reversible encryption.
  • iam_subject: the opaque subject in plain text, used for password sign-in reverse lookup. The docs call it "not PII" (data-model.md, CONTEXT.md).

Discrepancy: spec §20.2 asks to "encrypt sensitive mappings at application level" and keep keys in a managed KMS/HSM. The code reads the pepper from configuration and stores the plain iam_subject; no application-level encryption or KMS use is visible in the code.

Status: Implemented — evidence: ADR 0026, ADR 0030, identity-unlink.spec.ts, identity-delete.spec.ts

Both verbs live only on the PlayerPal Account screen, need recent reauthentication and take an Idempotency-Key (identity.md).

Unlink (one audience)Delete Account
AliasesRevoked, never reassigned; relink mints a new aliasAll revoked
Later activityDoes not earn; delayed activity that occurred before Unlink still qualifiesA former alias is treated as unknown
PointsStay on the ledgerIdentity graph erased
Sign-in dataUnchangedAuthentication identities, passkeys, sessions deleted; IAM user deleted first. If IAM delete fails, the request fails and the Account stays active
AnalyticsNo new partner-player-day rows; history staysPartner-player-day rows and fact rows deleted; player_id / player_alias nulled on retained activity; UUID↔analytics-subject mapping destroyed
RetainedHistorical analytics daysround_ref, amounts, occurred_at, integration and analytics subject on activity; redemption rows with player_id nulled (identity.store.ts)
PartnerOptional async notice; Unlink does not wait for it (ADR 0032)PlayerPal does not command partner erasure

v1 does not crypto-shred backups. A restored pre-delete backup can still join, and the docs say "do not market otherwise" (ADR 0026).

Who can see what​

Status: Implemented (API, scoping and floors) / In progress (demo only) (partner portal sign-in) — evidence: services/cube/security.js, kpi.service.ts, comparison.math.ts

  • Partner scoping. Partners call the NestJS KPI gateway, never the semantic layer directly. Each query is limited to the cubes allowed for the party type, refuses player_id and ungrouped queries, and gets a party_id filter on every cube (security.js, services/cube/README.md).
  • KPI account floor. Partner KPI cells under the floor are suppressed. KPI_ACCOUNT_FLOOR (falls back to STUDIO_ACCOUNT_FLOOR) defaults to 50; staff tier uses a floor of 0 (kpi.service.ts).
  • Studio analytics floor. STUDIO_ACCOUNT_FLOOR defaults to 50. Per-alias lookup is n=1 by design and returns numbers only (activity.service.ts, ADR 0026).
  • Benchmark floors (DEFAULT_ANONYMITY in comparison.math.ts): at least 5 peer parties (minPeers), at least 50 distinct Accounts (minAccounts), and suppression when one party holds more than 50% (dominanceP) or the top two more than 75% (dominanceP2). Overrides: BENCHMARK_MIN_PEERS, BENCHMARK_MIN_ACCOUNTS, BENCHMARK_DOMINANCE_P, BENCHMARK_DOMINANCE_P2; lower values apply only with BENCHMARK_ALLOW_LOW_THRESHOLDS=1 (comparison.job.ts).
  • Legacy studio header. The shared machine key plus x-party-id is still accepted unless STUDIO_LEGACY_PARTY_HEADER=false, and must be turned off in production (loyalty.md).

Retention and purpose limits​

Status: Partly decided — evidence: ADR 0026, spec

There is no retention schedule document. What the repo does state:

  • Delete Account and Unlink behaviour, as in the table above.
  • Command Idempotency-Key values are kept for at least 24 hours (spec §37).
  • The raw accepted envelope is stored for replay, and "retention and pseudonymisation policies" must apply to it as personal data. No period is given (spec §38.3).
  • IP/network velocity limits use "short retention". No period is given. IP addresses and device fingerprints must never become durable player-linking keys (spec, abuse controls section).
  • Purpose: separate identity mappings from general analytics access (spec §20.2); partners get numeric enrichment only, never another audience's identifier (ADR 0026).

Open questions​

The repo does not decide these. Do not assume answers; record an ADR instead (open-questions.md).

  • Retention periods for activity, facts, analytics serving rows, the raw envelope layer, ledger entries and audit data.
  • Retention of anonymised gameplay facts after Delete Account (kept, but for how long is not stated).
  • Backup retention, given backups are not crypto-shredded.
  • Whether a social provider that emits email regardless of scope may be enabled (product, legal and security must decide; ADR 0012).
  • Live capture of the social sign-in hop (open-questions.md).
  • Audience scope for affiliate and reward-partner aliases (ADR 0013).
  • Joint-controller contracts remain "a product-permission assumption" (ADR 0026).

Related: Product overview, Privacy (public), identity.md, authorization.md.