Skip to main content
InternalMarkdown

Portals

Each audience has its own URL, OAuth client, redirect URIs, session cookie and permission policy. The user never picks a role inside the product.

Source: conversation export §14 and spec §7 / §14. Decision: ADR 0018.

Audiences​

PortalIntended host (example)AudienceAuthentication
Playermy.playerpal.ioPlayersPasskey, approved social, provisional PlayerPal ID/password (A0), recovery
Staffadmin.playerpal.ioPlayerPal workforceEnterprise SSO, MFA, privileged-access controls
Operatorpartners.playerpal.io/operatorOperator workforceEnterprise OIDC/SAML
Providerpartners.playerpal.io/providerProvider workforceEnterprise OIDC/SAML
Affiliatepartners.playerpal.io/affiliateAffiliate workforceEnterprise SSO or approved passkey

Opening the wrong portal returns "you don't have access to this workspace", not a role switcher. One person may hold several roles; they enter each through its URL and authorization context.

UI navigation is not an authorization boundary. APIs enforce tenant, audience, role and resource policies (see authorization.md).

What each audience may see​

ActorPlayer identifier visiblePlayer email stored?
PlayerPrivate pp_plr_...No
Operatorpp_plr_opr_... onlyNo
Providerpp_plr_prv_... onlyNo
AffiliateAttributed or aggregate contextNo
PlayerPal staffInternal subject only where authorisedNo player email

Demo mapping​

Local demos use host routing (ADR 0018), not path roles:

HostAudience
player.localhost:8080Player (real Nest + ZITADEL auth)
staff.localhost:8080Staff (simulated sign-in)
operator.localhost:8080Operator
provider.localhost:8080Provider
affiliate.localhost:8080Affiliate
portals.localhost:8080 / localhost:5173Launcher

Path bypass (/player, …) remains for Vite direct mode. Presentation apps in demos/. HTML prototypes under spec/prototype/. Simulated sign-in outside the player portal is not production authentication.

Analytics (partner KPIs)​

The operator, provider and staff portals have an Analytics screen, and the operator and provider Overviews show live tiles. The screen has three tabs:

  • Your performance (Own tier)
  • You vs PlayerPal network (anonymous comparison)
  • Peer benchmarks

The rules are in kpis.md and ADR 0033. The partner portals still use simulated sign-in, so locally they read a seeded demo party through the API's /demo/kpis/* helper (DEMO_PARTNER_HELPER=1, demo parties only). They never use a partner credential in the browser. Running it locally needs Cube (task cube:db-role && task cube:up), CUBE_API_URL and CUBEJS_API_SECRET on the API. With only a handful of local parties, comparisons stay suppressed unless the API also sets BENCHMARK_ALLOW_LOW_THRESHOLDS=1 with lower BENCHMARK_* values.

Charts use Apache ECharts 5.6.0 (modular build, SVG renderer). The library is lazy-loaded, so the player portal never downloads it. One shared theme (components/kpi/echarts.ts) encodes the dataviz specs and the series colour #1b9bc8, which passes the validator on the portal surface. Every chart has a crosshair or item tooltip, arrow-key reading and a table view. Stat tiles and layout stay plain HTML.

Partner overview (Spec v0.1 §13)​

The operator, studio and affiliate portals open on a registry-driven overview (components/kpi/overview/):

  • two KPI rows
  • activity over time
  • partner mix
  • campaigns (affiliates)
  • geo mix and player segments
  • rewards redeemed, with redemption and mission performance
  • retention by cohort and missions

Every value comes from GET /v1/kpis/overview, and components never compute KPIs. A KPI whose feed hasn't delivered shows the API's requirement text (e.g. "Requires operator commercial feed"), never a zero. KPIs that don't apply to an audience aren't rendered. The affiliate portal shows PrimeWins (pp_aff_SIMPRIMEWINS) from the simulated network.

Multi-series charts use the validated 4-slot categorical palette in echarts.ts (dataviz reference dark slots, checked on the portal surface). Anything beyond four slices goes into a neutral "Other".