Heimdall/state/evidence.jsonl
2026-08-22 01:53:21 +02:00

34 lines
12 KiB
JSON

{"ts":"2026-04-26T00:00:00Z","type":"design","status":"accepted","note":"The shared hosted-access plan was extracted from Repixelizer into its own repo so the backbone can evolve separately from app-specific notes."}
{"ts":"2026-04-26T00:00:00Z","type":"design","status":"superseded","note":"The initial first implementation hypothesis was an embedded shared gamecult_access package. This was later superseded by the Heimdall service-first shape after the cross-runtime split and Yggdrasil topology review."}
{"ts":"2026-04-26T00:00:00Z","type":"boundary","status":"accepted","note":"StreamPixels can reuse the same auth/control-plane backbone, and Heimdall may own OAuth/token custody for shared provider connections, but streamer-audience data, creator memberships, creator-side connection bindings, and runtime state remain StreamPixels-owned."}
{"ts":"2026-04-26T06:00:00Z","type":"design","status":"accepted","note":"The cross-runtime split and current Yggdrasil topology changed the first implementation shape from an embedded shared package to Heimdall: a shared auth authority service whose signed claims are verified locally by each app."}
{"ts": "2026-04-26T11:00:02+00:00", "type": "design", "status": "accepted", "note": "Heimdall's provider model splits shared OAuth and credential custody from app-owned binding semantics and runtime use. Different provider mixes change adapter inventory and claim vocabulary, not the core service boundary."}
{"ts": "2026-04-26T11:24:39+00:00", "type": "design", "status": "accepted", "note": "The first Heimdall implementation pass landed as a standalone TypeScript/Fastify service skeleton with Ed25519 JWT signing, JWKS discovery, signed OAuth state, and app-profile-driven claim issuance for Repixelizer and StreamPixels."}
{"ts": "2026-04-26T11:49:41+00:00", "type": "implementation", "status": "accepted", "note": "The first stateful Heimdall slice landed durable auth-control-plane persistence plus a real Discord callback path for Repixelizer, with accounts, linked identities, sessions, grants, entitlement snapshots, and audit events stored behind in-memory/Postgres backends."}
{"ts": "2026-04-26T13:58:45+00:00", "type": "design", "status": "accepted", "note": "Heimdall claim facts should prefer provider-agnostic, app-facing entitlement names such as entitlement.app_access. Provider-specific evidence like Discord role matches stays in linked identities and entitlement snapshots unless a consumer truly needs that upstream detail in the claim surface."}
{"ts": "2026-04-26T14:22:37+00:00", "type": "implementation", "status": "accepted", "note": "Browser-style Heimdall auth handoff now uses a short-lived one-time completion code: the callback page posts the result to the opener, tries to close itself, and the app backend redeems the code with Heimdall instead of receiving the final access token in a callback URL fragment."}
{"ts": "2026-04-26T15:22:58+00:00", "type": "implementation", "status": "accepted", "note": "Heimdall now supports file-backed signing-key loading/bootstrap, AES-256-GCM token sealing for managed provider credentials at rest, and a local JWT verifier seam in src/verifier.ts; the next move shifts from custody hardening to the first real Repixelizer consumer integration."}
{"ts": "2026-04-26T16:00:35+00:00", "type": "design", "status": "accepted", "note": "Heimdall now supports direct backend callback delivery as the preferred same-host or Yggdrasil-reachable app handoff path; the browser completion-code redeem flow remains as fallback, not the primary consumer contract."}
{"ts": "2026-04-29T10:51:08+00:00", "type": "design", "status": "accepted", "note": "Repixelizer has consumed Heimdall's direct backend callback flow locally, so Heimdall is no longer blocked on Repixelizer auth refactoring. The next Heimdall objective is Discord-only live launch configuration and end-to-end OAuth verification on Yggdrasil; other providers remain catalogued but callback runtimes are not implemented yet."}
{"ts": "2026-04-29T10:53:44+00:00", "type": "design", "status": "accepted", "note": "Repixelizer Discord launch policy is intentionally role-gated: the GameCult guild can remain public, but Heimdall should grant Repixelizer app access only to members with the Cultist role configured in GC_ACCESS_APP_REPIXELIZER_DISCORD_ALLOWED_ROLE_IDS."}
{"ts": "2026-04-29T11:39:36+00:00", "type": "deployment", "status": "accepted", "note": "Heimdall is deployed on Yggdrasil behind nginx at https://heimdall.gamecult.org with Postgres storage, file-backed Ed25519 signing key bootstrap, configured token custody, and public JWKS/discovery/health checks passing. Repixelizer is deployed at https://repixelizer.gamecult.org in Heimdall mode with Discord as the only provider, required access enabled, and queue protection enabled; public auth-start returns a Discord authorization URL using the Heimdall callback and Repixelizer backend handoff."}
{"ts": "2026-04-29T12:06:26+00:00", "type": "config", "status": "accepted", "note": "Repixelizer Discord access now accepts either KLTST (861962027550244914) or the Patreon-synced Inner Sanctum role (1497585425491955894) via GC_ACCESS_APP_REPIXELIZER_DISCORD_ALLOWED_ROLE_IDS."}
{"ts": "2026-04-29T12:19:57+00:00", "type": "design", "status": "accepted", "note": "Repixelizer Discord role policy moved out of Heimdall app-specific env. Repixelizer owns the GameCult guild and role list through REPIXELIZER_ACCESS_DISCORD_* and sends a caller-owned discord_role_access policy in the trusted backend-callback OAuth start; Heimdall keeps provider OAuth credentials/callback mechanics and evaluates the supplied policy."}
{"ts": "2026-04-29T13:37:17+00:00", "type": "provider", "status": "accepted", "note": "Patreon OAuth for Repixelizer is enabled through Heimdall with API v2 identity lookup. Repixelizer owns the access rule and sends patreon_membership_access with requiredTierTitle=Inner Sanctum; Heimdall evaluates the signed-in user's currently entitled Patreon tiers by title instead of currency amount."}
{"ts": "2026-04-29T13:56:56+00:00", "type": "design", "status": "accepted", "note": "StreamPixels viewer and creator Twitch/YouTube OAuth now delegates to Heimdall. Heimdall owns provider OAuth, identity resolution, encrypted token custody, and app-authenticated access-token resolution; StreamPixels owns viewer profiles, creator memberships, connector bindings, diagnostics, EventSub/live-chat runtime behavior, and audience data."}
{"ts": "2026-04-29T14:16:59+00:00", "type": "config", "status": "accepted", "note": "Local Heimdall deployment env artifact now contains StreamPixels Twitch and YouTube OAuth client credentials under secrets/heimdall-service.env as GC_ACCESS_PROVIDER_TWITCH_* and GC_ACCESS_PROVIDER_YOUTUBE_*; values remain ignored and unprinted. Google callback URI in the downloaded secret file was ignored in favor of the configured Heimdall callback."}
{"ts": "2026-04-29T15:23:01+00:00", "type": "implementation", "status": "accepted", "note": "Twitch token exchange can return scope as an array; Heimdall now normalizes provider token scopes from string or array before storing managed credentials."}
{"ts": "2026-04-29T15:25:08+00:00", "type": "verification", "status": "accepted", "note": "Live StreamPixels Twitch sign-in through Heimdall succeeded on Yggdrasil: Heimdall audit shows oauth_callback_succeeded, auth_completion_created, and auth_completion_redeemed for twitch after the scope normalization deploy."}
{"ts": "2026-04-29T16:17:34+00:00", "type": "design", "status": "accepted", "note": "Twitch EventSub subscription management must use the same Twitch application Client-ID as Heimdall creator OAuth, because Twitch authorizes EventSub creation against the app token Client-ID even when StreamPixels stores no creator refresh token."}
{"ts": "2026-04-29T16:24:26+00:00", "type": "implementation", "status": "accepted", "note": "Heimdall managed-credential resolution now refreshes Twitch and YouTube access tokens when they are expired or within the refresh window, preserving refresh-token custody in Heimdall while returning only access tokens to app runtimes."}
{"ts": "2026-05-20T12:43:02+00:00", "type": "implementation", "status": "accepted", "note": "Heimdall now issues signed app refresh tokens, exposes /v1/apps/{appSlug}/sessions/refresh, refreshes Discord/Patreon provider tokens before Repixelizer entitlement re-evaluation, and reissues short-lived app access claims without restarting provider OAuth."}
{"ts": "2026-05-20T17:56:35+00:00", "type": "deployment", "status": "ok", "note": "Deployed Heimdall refresh-token session support to Yggdrasil and verified heimdall.service active, health/JWKS/discovery checks passing, and /v1/apps/repixelizer/sessions/refresh live with validation."}
{"ts": "2026-06-02T15:25:08+00:00", "type": "implementation", "status": "accepted", "note": "Spotiverse now consumes Spotify as a Heimdall-managed credential: Heimdall owns Spotify OAuth/token custody, and Spotiverse resolves current access via /v1/apps/spotiverse/managed-credentials/resolve without storing provider refresh tokens."}
{"ts": "2026-06-02T18:25:05+00:00", "type": "implementation", "status": "accepted", "note": "Public Heimdall can now hand Spotiverse OAuth results to a configured portal callback URL via GC_ACCESS_APP_SPOTIVERSE_BACKEND_CALLBACK_URLS; backend_callback handoffs are rejected unless the app callback is trusted, preserving token-bearing handoff boundaries."}
{"ts": "2026-06-02T18:36:46+00:00", "type": "deployment", "status": "ok", "note": "Public Yggdrasil Heimdall was deployed with the Spotify/Spotiverse provider code, Spotify credentials from secrets/spotify.txt, a generated Spotiverse app shared secret, and GC_ACCESS_APP_SPOTIVERSE_BACKEND_CALLBACK_URLS set to the Yggdrasil loopback reverse-forward callback. Local Spotiverse now points at public Heimdall, and the Yggdrasil SSH tunnel exposes 127.0.0.1:18796 back to local Spotiverse :8796."}
{"ts": "2026-06-08T20:02:27+00:00", "type": "implementation", "status": "accepted", "note": "Added Bifrost as a Heimdall app profile for Discord/Patreon member access. Bifrost owns KTLST role and Patreon tier policies through backend-callback OAuth starts; Heimdall owns provider OAuth/token custody and generic configured backend callback allowlisting."}
{"ts": "2026-06-09T19:21:17+00:00", "type": "implementation", "status": "accepted", "note": "Bifrost patron support sync reuses Heimdall's existing linked Patreon identity and membership reader. Heimdall exposes app-authenticated /v1/apps/bifrost/patron-support/sync, refreshes Patreon custody when needed, maps active paid tier membership into a signed RecurringSupportSnapshot, and posts it to Bifrost's /heimdall/patron-support/events intake; Bifrost owns points, tiers, voting weight, and ledger meaning."}
{"ts": "2026-06-16T16:59:54+00:00", "type": "implementation", "status": "accepted", "note": "Heimdall now writes a daemon-owned CultCache boundary store with provider advertisement, command boundary, transport profile, and daemon-health summary state, and can publish heimdall.cultnet-rudp-provider-health to Idunn over cultnet.transport.rudp.v0 when configured."}
{"ts":"2026-08-20T18:32:47+00:00","type":"deployment","status":"ok","note":"Yggdrasil runs Heimdall commit 2a905354b9dc85101463030dea9045c1561a4638 with a persistent machine-bound Ed25519 provider-health identity and canonical idunn.signed_daemon_health.v1 publication. Idunn at Odin commit 5586910fa704b8a0d330b90a902a89b99566db77 has the exact root trust binding, retries interrupted UDP receives, respects whole-second ingress timestamp precision, admitted seven consecutive Heimdall pulses as authority=authenticated at the intended cadence, evaluated the daemon healthy, and maintained the redacted public provider-health .cc projection. Both services were active with zero restarts and the deployment brake engaged."}
{"ts": "2026-08-21T22:37:58+00:00", "type": "implementation", "status": "accepted", "note": "Ghostlight auth now enters Heimdall through gamecult.heimdall.access plus a loopback CultNet operation plane. Heimdall persists attempts and completions; app-secret/source-runtime/expiry/nonce/idempotency binding and AES-256-GCM keep completion claims off public CultMesh and browser state. The plugin is projection-only and cannot grant access."}
{"ts": "2026-08-21T23:53:15+00:00", "type": "implementation", "status": "ok", "note": "Ghostlight private auth now validates refresh against exact persisted session custody and revision; logout atomically expires that session and advances its revision, preventing stale or racing refresh from resurrecting revoked access."}