Heimdall/notes/fresh-workspace-handoff.md
Meta Vens 5952cce8a0 State: live d51fa6b proven by a human Twitch sign-in; Joystick consent remains
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 18:03:17 +02:00

14 KiB

Fresh Workspace Handoff

This is the re-entry packet for this repo (path: whatever the checkout is; it has moved drives before).

It is intentionally short. Historical proof belongs in git history and the distilled state/evidence.jsonl ledger; the shared architecture belongs in docs/architecture.md; the forward build order belongs in docs/implementation-plan.md.

Rehydrate

From the repo root:

python .\tools\heimdall_state.py status
Get-Content '.\state\map.yaml'
Get-Content '.\notes\fresh-workspace-handoff.md'
Get-Content '.\docs\architecture.md'
Get-Content '.\docs\implementation-plan.md'
git status --short --branch
git log --oneline -5
Get-Content '.\state\evidence.jsonl' -Tail 8

Do not trust this file for the exact live HEAD. Always check git.

Active Work

Joystick H1 is merged and live (PR GameCult/Heimdall#1; campaign joystick:campaign:self). Next: joystick:follow_up:h1-joystick-consent-round-trip, then StreamPixels S0/S1 (other sessions). Open Heimdall follow-ups, by id: joystick:follow_up:cross-process-failed-refresh-marker, joystick:follow_up:connect-start-needs-app-auth, joystick:follow_up:heimdall-idunn-onboarding, joystick:follow_up:joystick-viewer-signin-public-bot. The Ghostlight KLTST canaries below stay open, behind them.

Current Orientation

Yggdrasil runs Heimdall d51fa6b, hand-deployed 2026-10-19 15:51Z; gamecult-ops 210a1af records backup, rollback and provenance. Facts below about Odin, Ghostlight and CultLib pins were verified at 1086aee (2026-08-22) and are not re-verified at d51fa6b.

  • At 1086aee01169bf60e8a492b2740db1c6f3e8cabf Heimdall ran with CultLib 5cefa0db0079a8e3ee22f29d7b9e6e5aa60912a9

  • Heimdall publishes four redacted access records to Odin under unique keys: eve:provider:heimdall, heimdall:command-boundary, eve:plugin:gamecult.heimdall.access, and heimdall:transport-profile

  • Odin b4f9a2e95f0b41cebdeddc49223781d1d3c7b42a persists and returns all four; it does not proxy private commands or receive claims

  • Ghostlight b515ca90c25573005a616244143803b37f2d06ec resolves the boundary through Odin and accepted a live canonical anonymous heimdall.auth.begin; its unit has no direct Heimdall endpoint

  • gamecult.heimdall.access now has a manifest, advertisement, schemas, fixtures, executable describe/validate/project ABI, and browser reference adapter

  • Heimdall persists OAuth attempts and serves authenticated heimdall.auth.begin / heimdall.auth.complete / heimdall.auth.refresh / heimdall.auth.logout through loopback CultNet/RUDP on port 4101

  • private commands are source-runtime, app-secret, expiry, nonce, and idempotency bound; their MessagePack payloads and completion claims are AES-256-GCM wrapped

  • the Ghostlight app profile now requires its caller-owned Discord role policy; Heimdall evaluates it and Ghostlight remains owner of local sessions, memberships, and campaigns

  • refresh verifies the exact persisted session revision before reissuing a claim; logout expires that session and advances its revision, so a stale or racing refresh cannot resurrect revoked custody

  • this repo now has a first stateful Heimdall service slice

  • the canonical shared architecture lives in docs/architecture.md

  • the concrete HTTP/JWKS contract lives in docs/service-contract.md

  • app profiles live under docs/app-profiles/

  • the key boundary is explicit:

    • Heimdall owns OAuth, linking, managed provider credential custody, grants, entitlement refresh, and signed claim issuance
    • app-domain data stays app-owned by default
    • host apps verify signed claims locally for routine auth instead of calling Heimdall on every request
  • the landed service now exposes:

    • /.well-known/jwks.json
    • /.well-known/heimdall-configuration
    • /v1/oauth/{provider}/start
    • /v1/oauth/{provider}/callback
    • /v1/apps/{appSlug}/auth-completions/redeem
    • /v1/apps/{appSlug}/sessions/refresh
    • /v1/apps/{appSlug}/managed-credentials/resolve
  • the Discord callback path now performs real code exchange, identity resolution, auth persistence, entitlement evaluation, audit logging, and claim issuance for the Repixelizer slice

  • browser-style auth handoff now uses a one-time completion code posted back to the opener, with backend redemption instead of fragment-token handoff

  • same-host or Yggdrasil-reachable app integrations can now ask Heimdall to deliver the auth result directly to an app backend callback endpoint, leaving the browser with only status/attempt signaling

  • signing keys can now load from a persisted file path and bootstrap that file on first boot when explicitly enabled

  • managed provider tokens are now sealed at rest with AES-256-GCM instead of being dumped into "encrypted" columns raw like an insult

  • src/verifier.ts now provides a small local JWT verifier seam for app backends

  • Repixelizer has consumed the backend-callback flow in its hosted web layer: it creates local auth attempts, asks Heimdall to start OAuth, receives the direct backend callback, verifies Heimdall Ed25519 access tokens locally, and adopts httpOnly local sessions

  • Heimdall is now deployed on Yggdrasil at https://heimdall.gamecult.org behind nginx/TLS with Postgres storage, file-backed signing-key bootstrap, configured token custody, and public health/discovery/JWKS checks passing

  • Repixelizer is now deployed on Yggdrasil at https://repixelizer.gamecult.org with Heimdall mode enabled, Discord and Patreon as public providers, required access enabled, and queue protection enabled

  • Repixelizer public auth-start returns Discord and Patreon authorization URLs using the Heimdall callback and Repixelizer backend handoff

  • Repixelizer auth now has a refresh path: Heimdall issues signed refresh tokens with access claims, /v1/apps/{appSlug}/sessions/refresh reissues short-lived access claims, and Discord/Patreon provider tokens refresh inside Heimdall before entitlement re-evaluation

  • Twitch and YouTube now have generic OAuth exchange and identity runtimes

  • GitHub is still catalogued, but its callback runtime is not implemented

  • StreamPixels now delegates viewer and creator Twitch/YouTube OAuth to Heimdall, redeems Heimdall completion codes server-side, writes its own viewer sessions and connector bindings, and resolves current provider access tokens from Heimdall without storing refresh tokens locally

  • Spotiverse now delegates Spotify OAuth to Heimdall. Heimdall has a spotiverse app profile and spotify provider runtime; Spotiverse stores a local Heimdall account binding and resolves current Spotify access through /v1/apps/spotiverse/managed-credentials/resolve without storing provider refresh tokens.

  • Bifrost now has a bifrost app profile for Discord/Patreon member access. Bifrost sends caller-owned KTLST Discord role and Patreon tier policies through trusted backend-callback OAuth starts; Heimdall owns provider OAuth/token custody and configured backend callback allowlisting.

  • Bifrost patron support sync now reuses Heimdall's existing linked Patreon identity and membership reader. POST /v1/apps/bifrost/patron-support/sync is app-authenticated, refreshes the stored Patreon credential if needed, reads active paid Patreon tier evidence, and emits a signed RecurringSupportSnapshot to Bifrost's /heimdall/patron-support/events intake.

  • Public Yggdrasil Heimdall has been deployed with the Spotiverse/Spotify code path, Spotify provider credentials, a Spotiverse shared secret, and an allowlisted loopback portal callback at http://127.0.0.1:18796/auth/heimdall/callback.

  • Heimdall and StreamPixels are deployed on Yggdrasil for the StreamPixels migration slice; Twitch callback handling has been patched so provider token scope can be either a space-delimited string or an array

  • live StreamPixels Twitch sign-in through Heimdall has succeeded on Yggdrasil and Heimdall audit showed callback success, completion creation, and completion redemption

  • the ignored local secrets/heimdall-service.env artifact now has GC_ACCESS_PROVIDER_TWITCH_* and GC_ACCESS_PROVIDER_YOUTUBE_* populated for the StreamPixels migration slice

  • the intended first deployment shape is a Heimdall service on Yggdrasil behind nginx, not an embedded cross-runtime shared library fantasy

  • Heimdall now writes a daemon-owned boundary witness store at GC_ACCESS_CULTCACHE_PATH containing provider advertisement, command boundary, transport profile, and daemon-health summary state

  • Heimdall can now publish heimdall.cultnet-rudp-provider-health to Idunn over cultnet.transport.rudp.v0 when GC_ACCESS_IDUNN_RUDP_HEALTH is configured; HTTP /healthz, JWKS, discovery, systemd, and nginx routing remain compatibility witnesses

  • Since 1086aee Heimdall persists its private provider-health identity at /srv/heimdall/keys/provider-health-identity.cc and publishes canonical signed health independently of the slower Verse snapshot cadence. Idunn owns the exact root trust binding, authenticated admission, lifecycle decision, and redacted public .cc projection.

  • Odin commit b4f9a2e95f0b41cebdeddc49223781d1d3c7b42a retains the RUDP health reliability cut and registers Heimdall's command-boundary, plugin, and transport schemas so the typed catalog can persist rather than silently drop them. Live proof returned all four exact Heimdall records.

Critical Doctrine

  • Persistent state is the agent's mind.
  • Cut stale architecture notes as ruthlessly as stale code.
  • Do not mistake "shared auth" for "shared database."
  • Do not mistake "shared auth service" for "every guarded route must call the mothership."
  • If compaction hits before boundary findings are persisted, that work is gone. Re-gather it instead of pretending continuity happened.

Next Real Move

The Active Work follow-ups first. Everything below is the standing queue.

Do not continue implementation automatically from a rehydrate-only request.

The Idunn/swarm health transport cut is live. Do not regress Heimdall to unsigned idunn.daemon_health, merge its private key into Idunn, or make HTTP health an authority. The next product work is the Bifrost and remaining browser verification listed below.

The active Ghostlight deployment cut is complete. The next Ghostlight work is a real separate-account KLTST completion/refresh/logout canary, followed by the eight-account Session Zero privacy and actor-binding smoke. Keep port 4101 loopback-only and treat any claim, token, completion, app secret, or account state in Odin/CultMesh/browser output as a release-blocking fault.

If the user asks to continue on Spotiverse, the current next move is the real browser /auth/login flow through public Heimdall. The Yggdrasil SSH tunnel must include reverse forward spotiverse-callback:18796:127.0.0.1:8796, and local Spotiverse must point at https://heimdall.gamecult.org with the matching HEIMDALL_APP_SECRET. Verify managed credential resolution after login. Spotiverse should not regain local Spotify OAuth token storage.

If the user asks to continue on StreamPixels, the current next move is real browser verification:

  • verify viewer claim/link through https://streampixels.gamecult.org/auth/connect
  • Twitch viewer sign-in has already passed once; continue with YouTube and connector attachment verification
  • verify creator connector attach from an admin creator session
  • confirm StreamPixels persists local profile/connector binding state while Heimdall retains provider token custody
  • if a provider callback fails, inspect Heimdall audit events and StreamPixels service logs before touching app/domain boundaries

Repixelizer still needs its real browser OAuth verification:

  • open https://repixelizer.gamecult.org/app/
  • start Discord sign-in with an account that has the KLTST or Patreon-synced Inner Sanctum role, or Patreon sign-in with a currently entitled tier titled Inner Sanctum
  • confirm Repixelizer sends its app-owned provider policy in the backend-callback OAuth start
  • confirm Heimdall receives the provider callback and evaluates entitlement
  • confirm Repixelizer receives the backend callback, verifies the Heimdall token, adopts a local httpOnly session, and gates hosted/queue routes
  • confirm the Discord authorization URL requests only identify guilds.members.read

Launch policy is role-gated on purpose: the GameCult Discord can remain public, but Repixelizer access is limited to members with either KLTST or the Patreon-synced Inner Sanctum role. Repixelizer owns that role policy through its own runtime env and sends it to Heimdall during backend-callback OAuth start; Heimdall owns provider OAuth mechanics and claim issuance, not the per-app role list.

Patreon access is title-gated on the currently entitled tier title Inner Sanctum; do not use currency amount as the durable policy because currencies are weird little traps.

Bifrost needs browser verification after deployment:

  • configure Heimdall with GC_ACCESS_APP_BIFROST_BACKEND_CALLBACK_URLS=https://bifrost.gamecult.org/auth/heimdall/callback
  • configure Heimdall with GC_ACCESS_APP_BIFROST_SHARED_SECRET_FILE, GC_ACCESS_BIFROST_PATRON_SUPPORT_ENDPOINT=https://bifrost.gamecult.org/heimdall/patron-support/events, and GC_ACCESS_BIFROST_PATRON_SUPPORT_SECRET_FILE, each naming a credential file
  • configure Bifrost with Heimdall__DiscordGuildId, Heimdall__DiscordAllowedRoleIds__0=<KTLST-role-id>, and Heimdall__PatreonTierTitle=Inner Sanctum
  • configure Bifrost with the matching Heimdall__PatronSupportIntakeSecret
  • start Discord sign-in from https://bifrost.gamecult.org/ with a user that has the KTLST cult-member role
  • start Patreon sign-in with a user that has the configured active tier
  • confirm Bifrost creates/updates a local account and active standard membership while Heimdall keeps provider token custody
  • trigger /v1/apps/bifrost/patron-support/sync for that linked Heimdall account and confirm Bifrost records a Patreon recurring-support snapshot without owning Patreon webhook or token custody

Immediate Re-entry Instruction

After compaction, first rehydrate and reorient from the listed files and git state. Wait for the user's next instruction unless they explicitly say to continue.