# Open questions

Genuinely undecided items. If you are about to assume an answer, stop and
write an ADR instead of guessing.

The long-form list in spec §29 is filtered here: items closed by spec §33
or ADRs 0011-0032 are in the resolved section at the bottom.

## Production hardening (not a product pick)

1. Production IAM lock-in still waits on the remaining spec §23.2 proofs.
   Local/dev is ZITADEL ([ADR 0021](decisions/0021-nestjs-zitadel-local-poc.md)).
   Keycloak remains the fallback if those proofs fail. Do not pick a third
   vendor without a new ADR.

   Already demonstrated in this POC:

   - Passkey-only Account create without player email (ADR 0024).
   - Social start requests `openid` only; Nest keeps `iss` + `sub` and drops
     IdP `rawInformation` (ADR 0012).
   - Linking is an explicit intent, not email match (ADR 0017, 0019).
   - Player and staff portals are URL-isolated (ADR 0018). Staff still uses
     `MACHINE_API_KEY`, not workforce SSO.
   - Players can revoke portal sessions.

   Still required before production IAM lock-in:

   - Live capture that Google + ZITADEL do not leave email/name/phone in
     PlayerPal cookies, DB, API logs, or traces (code filtering is in place;
     the live hop is not).
   - Enterprise admin SAML/OIDC.
   - Machine clients using `private_key_jwt` (POC ingest still uses
     `MACHINE_API_KEY`).
   - Export/migration of authentication mappings.
   - IAM key rotation.

## Resolved. Do not re-open without an ADR

- ~~Keycloak vs ZITADEL for local POC~~ → [ADR 0021](decisions/0021-nestjs-zitadel-local-poc.md) (ZITADEL + NestJS control plane)
- ~~Partner aliases are `pp_plr_...`~~ → [ADR 0013](decisions/0013-typed-pairwise-aliases.md)
- ~~No extra operator×provider scope~~ → [ADR 0014](decisions/0014-provider-operator-alias-scope.md)
- ~~JWT at operator login for the widget~~ → [ADR 0015](decisions/0015-async-loyalty-sidecar.md), [ADR 0016](decisions/0016-bff-cookies-and-machine-oauth.md)
- ~~Token lifetime unknown~~ → machine tokens 5-15 minutes; player BFF cookies; no widget refresh tokens ([ADR 0016](decisions/0016-bff-cookies-and-machine-oauth.md))
- ~~Richer profile may use email~~ → [ADR 0012](decisions/0012-never-receive-player-pii.md)
- ~~Link UX / consent surface~~ → [ADR 0017](decisions/0017-operator-assertion-popup-linking.md)
- ~~Sign in as existing account~~ → [ADR 0019](decisions/0019-scoped-bootstrap-proofs-controlled-merge.md)
- ~~Points before registration / retrospective claim~~ → spec §33: no shadow profiles, no historical points by default ([ADR 0009](decisions/0009-mandatory-one-time-link-before-data-flows.md))
- ~~Never store vs never receive email~~ → never receive or store ([ADR 0012](decisions/0012-never-receive-player-pii.md))
- ~~PostgreSQL-first then Redpanda~~ → [ADR 0005](decisions/0005-phased-event-log-infrastructure.md)
- ~~Batch orchestrator~~ → Dagster initially (spec §35.1)
- ~~Random pairwise vs HMAC / Base32(UUID)~~ → [ADR 0002](decisions/0002-random-pairwise-external-ids.md)
- ~~Portal role picker~~ → [ADR 0018](decisions/0018-url-isolated-portals.md)
- ~~Custom / branded portal login UI path~~ → Login V2 fork available ([ADR 0022](decisions/0022-official-login-app-fork.md)); **Nest owns** player password/passkey + social for the local portal ([ADR 0024](decisions/0024-nest-owned-portal-auth.md))
- ~~Whether PlayerPal ID + password is offered~~ → yes on player signup, provisional A0, pre-minted `pp_plr_...` as IAM loginname ([ADR 0020](decisions/0020-assurance-levels-provisional-password.md), [ADR 0024](decisions/0024-nest-owned-portal-auth.md))
- ~~Nest data access / DDL ownership~~ → TypeORM ([ADR 0023](decisions/0023-typeorm-data-layer.md))
- ~~Permitted cross-operator analytics (item 6)~~ → partners get cohort and
  per-alias **numeric** enrichment; they never get another Audience’s
  identifier. Legal joint-controller contracts remain a product-permission
  assumption, not an identifier rule ([ADR 0026](decisions/0026-analytics-subject-and-provider-cohort.md))
- ~~Retention after Unlink or Delete Account (item 7)~~ → Unlink revokes one
  Audience (spec §36.6); Delete Account erases the identity graph and retains
  gameplay facts without identity keys ([ADR 0026](decisions/0026-analytics-subject-and-provider-cohort.md))
- ~~Google claim behaviour in PlayerPal-controlled systems (item 2)~~ →
  `openid` only; Nest persists `iss` + `sub`; IdP `rawInformation` is dropped
  before return ([ADR 0012](decisions/0012-never-receive-player-pii.md)). Live
  hop capture remains under production hardening above.
- ~~Recovery beyond recovery codes (item 3)~~ → codes, extra passkeys, linked
  social, remaining-device session revoke. Operator-assisted recovery is out
  of v1 ([ADR 0028](decisions/0028-v1-recovery-without-operator-assist.md)).
- ~~Eager vs on-demand provider aliases (item 4)~~ → mint listed integrations
  at link complete; later integrations via resolve; never on ingest
  ([ADR 0027](decisions/0027-alias-provisioning-at-link.md)).
- ~~Which providers get a provider-global alias exception (item 5)~~ → none.
  Exceptions need a new ADR plus a named contract
  ([ADR 0014](decisions/0014-provider-operator-alias-scope.md)).
- ~~Authorization implementation (item 8)~~ → application-native Nest checks
  until a second service shares the same permissions
  ([ADR 0029](decisions/0029-application-native-authorization.md)).
- ~~Redpanda date (item 9)~~ → no date. Phase 2 starts when a spec §35.4
  condition is measured ([ADR 0005](decisions/0005-phased-event-log-infrastructure.md)).
- ~~How Bucks learns PlayerPal Unlink~~ → async notice plus rare alias
  reconcile; Unlink does not wait ([ADR 0032](decisions/0032-unlink-partner-notice.md)).
