project: name: Heimdall status: active phase: streampixels_migration premise: > Build Heimdall as one reusable GameCult auth authority service for OAuth, linked identities, local sessions, grants, and capability claims without mixing in app-domain audience or product data. objective: primary: > Create a shared auth authority service that Repixelizer, StreamPixels, and future GameCult-hosted experiments can consume. current: > Publish the Heimdall Eve access plugin and private CultNet authentication plane, then migrate Ghostlight onto app-local sessions without moving campaign or membership authority into Heimdall. constraints: - Heimdall owns auth/control-plane authority; app-domain data stays app-owned by default. - Prefer a same-host Heimdall service on Yggdrasil over duplicated per-app provider stacks. - Host apps verify signed claims locally for routine guarded routes. - App profiles stay thin; the shared architecture note owns the main model. - StreamPixels audience and creator data must not be absorbed into a shared auth store. invariants: - The map is canonical and slow-changing. - Scratch is temporary and disposable. - Evidence is durable and distilled; routine activity logs belong in git history and commit messages. - Heimdall owns OAuth, local sessions, grants, entitlement refresh, capability evaluation, and audit surfaces. - App-domain data stays with the host app unless there is a deliberate, source-grounded reason to share it. - Host apps verify signed claims locally and keep resource ownership checks local. - StreamPixels can reuse shared auth authority without centralizing streamer-audience data. - Spotiverse consumes Spotify as a Heimdall-managed credential; Spotiverse does not own Spotify refresh-token custody. - Heimdall's first Odin/Eve daemon boundary publication remains secret-safe: it writes only provider advertisement, command boundary, transport profile, and daemon-health summary state, not per-account auth truth. - Heimdall owns its persistent provider-health signing identity and signed observations; Idunn owns root-bound admission and the public provider-health projection. Unsigned health is never lifecycle authority. - Heimdall owns OAuth attempts and completion redemption; the Eve access plugin and browser can project or carry one opaque handle but cannot grant access. - Private auth commands are app-secret, source-runtime, expiry, nonce, and idempotency bound; sensitive completion claims remain AES-GCM wrapped until the app backend receives them. - Refresh and logout are exact persisted-session transitions. App/account custody and access revision must match; logout advances the revision and stale refresh cannot recreate the session. - Persistent state cruft is the same failure class as architecture cruft. data_flow: observe: - task request - shared architecture note - app profile bindings - current app/runtime seams in Repixelizer and StreamPixels - Yggdrasil host topology and nginx/service shape process: - rehydrate from canonical state - choose one bounded architecture or implementation slice - keep auth authority and app-domain data boundaries explicit - keep routine route auth local after claim verification - record only belief-changing evidence - commit or backtrack cut: - reject shared-data creep - reject duplicated auth blobs per app - reject per-request auth round-trip chatter active_subgoals: - Joystick H1 is merged and live (PR GameCult/Heimdall#1, d51fa6b; campaign joystick:campaign:self). Open follow-ups by id: joystick:follow_up:h1-joystick-consent-round-trip (next), cross-process-failed-refresh-marker, connect-start-needs-app-auth, heimdall-idunn-onboarding, joystick-viewer-signin-public-bot. StreamPixels S0/S1 run in other sessions. - OPEN, behind the H1 live round-trips: pressure the deployed Ghostlight gamecult.heimdall.access integration with two- and eight-account human canaries. - Keep the shared architecture explicit and separate from app profiles. - Preserve the default boundary that app-domain data remains app-owned. - Align the plan to Heimdall as a Yggdrasil-hosted authority service. - Publish Heimdall daemon health and boundary state for Idunn/Odin without moving auth truth out of Heimdall. - Verify the Discord and Patreon Repixelizer auth paths with real provider credentials, stable signing, persistent storage, and live browser callback verification. - Wire StreamPixels through Heimdall for Twitch/YouTube (Joystick joins under H1) viewer identity and managed creator-provider credentials without moving StreamPixels domain state into Heimdall. accepted_design: - `gamecult.heimdall.access` owns access-gate representation and browser navigation semantics; it has no access-grant or provider-state authority. - Heimdall's loopback CultNet operation service owns authenticated auth begin/complete commands, while retained HTTP integrations remain supported for existing consumers. - Ghostlight refresh and logout use the same private CultNet boundary; refresh re-evaluates the caller-owned KLTST policy and logout revokes exact Heimdall session custody. - `docs/architecture.md` is the canonical shared architecture note. - `docs/service-contract.md` records the concrete HTTP/JWKS contract of the landed service slice. - `docs/app-profiles/*.md` are thin app bindings, not duplicate brains. - Heimdall owns provider OAuth, identity linking, signed local sessions, grants, entitlement refresh, capability evaluation, and audit events. - Host apps own their domain data, runtime state, and product-specific resources. - Host apps verify signed claims locally for routine route authorization. - A shared Heimdall store, if it exists, should hold auth/control-plane truth only. - Repixelizer is the first auth-blank hosted-app binding. - StreamPixels is the migration case that proves shared authority must not imply audience-data centralization. - The first service skeleton is a standalone TypeScript/Fastify app with Ed25519 JWT signing, JWKS discovery, signed OAuth `state`, and app-profile-driven shared capability evaluation. - The first stateful slice adds durable auth/control-plane storage plus a real Discord callback path for Repixelizer. - Repixelizer has consumed the Heimdall backend-callback flow in its hosted web layer; Heimdall no longer needs to wait on Repixelizer refactoring before live Discord launch work. - Heimdall is deployed on Yggdrasil at `https://heimdall.gamecult.org` with nginx/TLS, Postgres storage, file-backed signing-key bootstrap, configured token custody, and passing public health/discovery/JWKS checks. - Heimdall now issues signed app refresh tokens alongside short-lived access claims and exposes `/v1/apps/{appSlug}/sessions/refresh` so app backends can reissue local claims without restarting provider OAuth. - Discord and Patreon provider access tokens can refresh inside Heimdall before Repixelizer entitlement re-evaluation. - Repixelizer is deployed on Yggdrasil at `https://repixelizer.gamecult.org` with `GC_ACCESS_MODE=heimdall`, `GC_ACCESS_ALLOWED_PROVIDERS=discord,patreon`, required access, queue protection enabled, app-owned Discord guild/role policy, and app-owned Patreon tier-title policy sent to Heimdall during backend-callback OAuth start. - StreamPixels viewer and creator provider OAuth should start through Heimdall; StreamPixels redeems one-time completion codes, owns local profile/connector binding, and resolves current provider access tokens from Heimdall by app-authenticated managed-credential calls. - Bifrost is a Heimdall app profile for Discord/Patreon member access. Bifrost owns the KTLST Discord role policy and Patreon tier policy; Heimdall owns provider OAuth, token custody, entitlement evaluation, and configured backend callback trust. - Bifrost patron support sync reuses Heimdall's existing linked Patreon identity and membership reader. Heimdall turns active paid Patreon tier evidence into signed support facts; Bifrost owns point/tier/vote derivation. - Heimdall's first runtime Verse cut publishes a service-owned CultCache boundary store and optional Idunn RUDP health pulse; full redacted auth-document witness export remains a later pass. landed_baseline: - Heimdall publishes its redacted provider, command boundary, Eve plugin, and transport profile through Odin under globally unique record keys; Ghostlight resolves that boundary instead of owning a direct endpoint. - Yggdrasil runs Heimdall d51fa6b (hand-deployed 2026-10-19 15:51Z; gamecult-ops 210a1af holds backup, rollback and provenance; prior 1086aee) with CultLib 5cefa0db0079a8e3ee22f29d7b9e6e5aa60912a9; live Ghostlight auth.begin through Odin discovery is accepted. - The Heimdall access plugin, persistent OAuth attempts, encrypted private CultNet commands, conformance fixtures, and browser reference adapter are implemented and locally verified. - Exact-session refresh validation and logout revocation are implemented; stale or racing refresh cannot resurrect a revoked session. - Shared architecture extracted into its own repo and renamed to Heimdall. - Repixelizer and StreamPixels app-profile notes created. - Repo-local state, handoff, and compaction scaffolding created. - Initial Heimdall service skeleton landed with discovery, JWKS, OAuth start/callback contract surfaces, and claim issuance scaffolding. - Durable auth persistence landed for accounts, linked identities, sessions, grants, entitlement snapshots, and audit events. - The first real provider slice landed for Discord callback exchange, identity resolution, entitlement evaluation, and session/claim issuance for Repixelizer. - Signing-key persistence/bootstrap, managed token encryption at rest, and a local verifier seam landed for the first real consumer integration pass. - Direct backend callback handoff landed as the preferred same-host app-consumer path, with browser completion-code exchange retained as fallback. - Signed session refresh landed for app-facing claims: refresh tokens remain Heimdall-issued, provider refresh tokens remain sealed in Heimdall, and callers can provide app-owned entitlement policies for re-evaluation. - Repixelizer's hosted web layer now creates Heimdall auth attempts, receives direct backend callbacks, verifies Heimdall Ed25519 access tokens locally, and adopts httpOnly local sessions. - Heimdall now has generic Twitch and YouTube OAuth callback runtimes plus an app-authenticated managed-credential resolve endpoint for app-owned connector runtimes. - StreamPixels now starts viewer and creator Twitch/YouTube OAuth through Heimdall, redeems Heimdall completion codes server-side, writes its own viewer sessions and creator connector bindings, and leaves provider refresh-token custody in Heimdall. - Spotiverse now has a Heimdall app profile and Spotify provider runtime for managed credential custody. - Bifrost now has a Heimdall app profile for Discord/Patreon member access, and backend callback trust is checked through configured app callback allowlists before legacy app-specific defaults. - Heimdall exposes `POST /v1/apps/bifrost/patron-support/sync` as an app-authenticated bridge from linked Patreon membership evidence to Bifrost's signed patron support intake endpoint. - A read-only `gamecult.eve.provider_advertisement.v1` export now advertises Heimdall provider/app surfaces plus redacted CultCache witness document paths for Odin/Eve discovery. - Heimdall now writes a daemon-owned `GC_ACCESS_CULTCACHE_PATH` boundary store containing provider advertisement, command boundary, transport profile, and daemon-health summary state, and it can publish `heimdall.cultnet-rudp-provider-health` to Idunn over `cultnet.transport.rudp.v0` when configured. - Heimdall now persists a machine-bound Ed25519 provider-health identity, publishes canonical `idunn.signed_daemon_health.v1` records on an independent sequential pulse loop, and is admitted by Idunn through an exact root trust binding. rejected_paths: - name: per_app_auth_copy_paste reason: duplicates the same provider/session sludge across every experiment - name: per_request_auth_round_trip reason: turns shared auth into a noisy bottleneck instead of local claim verification - name: shared_user_data_swamp reason: reusing auth mechanics does not justify centralizing app-domain audience or product data current_status: summary: Yggdrasil runs Heimdall release d51fa6b (hand-deployed 2026-10-19 15:51Z, gamecult-ops 210a1af), which includes Joystick H1 (PR #1). The plugin and encrypted loopback begin, complete, refresh and logout plane, the Odin discovery records and Ghostlight resolution were verified at 1086aee (2026-08-22) and are not re-verified at d51fa6b. next_action: Run joystick:follow_up:h1-joystick-consent-round-trip, then StreamPixels S0/S1 (other sessions). Still open behind that: separate-account KLTST completion, refresh, logout, privacy, and stale-attempt canaries through the deployed Ghostlight Eve host; keep provider credential rotation on the operator list because prior tool output exposed OAuth secrets.