# StreamPixels Access Profile ## What this file is This file records how StreamPixels should fit onto Heimdall in `docs/architecture.md` without turning streamer-audience data into shared auth sludge. It is a boundary note, not a command to rewrite StreamPixels around Repixelizer. ## Public origin `https://streampixels.gamecult.org` — this is the `allowedReturnOrigins` entry Heimdall's built-in `streampixels` profile carries (`src/app-profiles.ts`). Corroborated by `gamecult-ops/nginx/streampixels.gamecult.org.conf:14` (`server_name streampixels.gamecult.org`) and StreamPixels' own `.env` example. ## Current repo boundary The current StreamPixels architecture already makes useful splits: - the web app owns the remembered viewer session - Heimdall owns provider OAuth exchange, identity resolution and refresh-token custody for Twitch, YouTube and Joystick; StreamPixels never calls a provider token endpoint and holds no provider refresh token. It asks Heimdall for a short-lived access token (`managed-credentials/resolve`) and treats `410 managed_credential_revoked` as "the creator must reconnect" - the overlay remains auth-free - creator memberships and operator roles are separate from general viewer state That is already compatible with the shared backbone idea. ## What StreamPixels can reuse - signed local session mechanics - linked-identity and signed OAuth-state patterns - provider adapter structure - creator/operator capability evaluation - grant/invite primitives where they fit In the service-first Heimdall version, the most likely reusable slice is: - provider OAuth authority - identity-linking authority - managed Twitch/YouTube/Joystick credential custody for creator connections (Joystick is a managed connection only, not a viewer sign-in provider: `startOAuthFlow` admits `sign_in` and `link` only for `identityProviders` and `connect` only for `managedConnectionProviders` or `identityProviders`, and refuses anything else before any redirect. The callback decides again from the signed state's mode: a `connect` for a managed-connection-only provider is admitted only with `connection.kind` `creator` and a `targetId`, refused otherwise at start and again at the callback before the code exchange; an admitted one stores the credential and mints a claim with no facts, no capabilities and no refresh token. Heimdall's claim is therefore never a viewer session. Heimdall cannot stop an app from minting its own viewer from a completion, so StreamPixels' redeem must treat a non-creator Joystick completion as an error, never as a sign-in. This is an S1 rule, recorded for SP) - entitlement refresh - shared claim semantics ## What StreamPixels should keep separate - viewer audience profiles - creator memberships tied to streamer spaces - creator connector bindings and feature semantics - overlay/runtime state - audience-facing app data in general In plain language: - Heimdall can authenticate and authorize the control plane - Heimdall can also own OAuth flow and token custody for creator-side provider connections when that reduces auth duplication - StreamPixels still owns which creator a connection belongs to, what subscriptions or sync flows it drives, and how failures are surfaced - StreamPixels audience data stays StreamPixels data ## Capability direction Likely capability families: - `viewer_access` - `creator_access` - `creator_admin` - `operator_access` These should bind onto the existing StreamPixels creator/operator separation instead of flattening it into one GameCult-member gate. ## Invariant If a future shared access store exists, it must not become the canonical home for streamer-audience identity and product data. That stays with StreamPixels.