Rename repo to Heimdall
This commit is contained in:
parent
397ccfd901
commit
17096e85b1
12 changed files with 296 additions and 179 deletions
64
AGENTS.md
64
AGENTS.md
|
|
@ -1,18 +1,19 @@
|
|||
# GameCult Access Instructions
|
||||
# Heimdall Instructions
|
||||
|
||||
## Project Purpose
|
||||
|
||||
GameCult Access exists to hold the shared auth and access-control architecture
|
||||
for GameCult-hosted experiments without letting that shared layer metastasize
|
||||
into a giant mixed-data pit.
|
||||
Heimdall exists to hold the shared auth authority design for GameCult-hosted
|
||||
experiments without letting that shared layer metastasize into a giant
|
||||
mixed-data pit.
|
||||
|
||||
The planned reusable backbone is:
|
||||
The planned shared authority owns:
|
||||
|
||||
- provider OAuth and linking
|
||||
- signed local sessions
|
||||
- signed session / claim issuance
|
||||
- entitlement refresh
|
||||
- capability evaluation
|
||||
- app-facing access claims
|
||||
- audit and grant surfaces
|
||||
|
||||
The planned non-shared-by-default layer is:
|
||||
|
||||
|
|
@ -21,6 +22,7 @@ The planned non-shared-by-default layer is:
|
|||
- creator data
|
||||
- queue/job payloads
|
||||
- product-specific state
|
||||
- app-local resource ownership checks
|
||||
|
||||
## Canonical State
|
||||
|
||||
|
|
@ -38,35 +40,35 @@ The planned non-shared-by-default layer is:
|
|||
|
||||
## Important Paths
|
||||
|
||||
- Project root: `E:\Projects\gamecult-access`
|
||||
- Shared architecture: `E:\Projects\gamecult-access\docs\architecture.md`
|
||||
- Implementation plan: `E:\Projects\gamecult-access\docs\implementation-plan.md`
|
||||
- Repixelizer profile: `E:\Projects\gamecult-access\docs\app-profiles\repixelizer.md`
|
||||
- StreamPixels profile: `E:\Projects\gamecult-access\docs\app-profiles\streampixels.md`
|
||||
- Handoff summary: `E:\Projects\gamecult-access\notes\fresh-workspace-handoff.md`
|
||||
- State CLI: `E:\Projects\gamecult-access\tools\gamecult_access_state.py`
|
||||
- Pre-compaction helper: `E:\Projects\gamecult-access\tools\gamecult_access_prepare_compaction.py`
|
||||
- Project root: `E:\Projects\Heimdall`
|
||||
- Shared architecture: `E:\Projects\Heimdall\docs\architecture.md`
|
||||
- Implementation plan: `E:\Projects\Heimdall\docs\implementation-plan.md`
|
||||
- Repixelizer profile: `E:\Projects\Heimdall\docs\app-profiles\repixelizer.md`
|
||||
- StreamPixels profile: `E:\Projects\Heimdall\docs\app-profiles\streampixels.md`
|
||||
- Handoff summary: `E:\Projects\Heimdall\notes\fresh-workspace-handoff.md`
|
||||
- State CLI: `E:\Projects\Heimdall\tools\heimdall_state.py`
|
||||
- Pre-compaction helper: `E:\Projects\Heimdall\tools\heimdall_prepare_compaction.py`
|
||||
|
||||
## Useful Commands
|
||||
|
||||
This repo does not carry a dedicated virtualenv yet. Use any working Python
|
||||
3.11+ interpreter. On this workstation, `E:\Projects\repixelizer\.venv\Scripts\python.exe`
|
||||
is known-good.
|
||||
3.11+ interpreter. On this workstation,
|
||||
`E:\Projects\repixelizer\.venv\Scripts\python.exe` is known-good.
|
||||
|
||||
Preferred here:
|
||||
|
||||
```powershell
|
||||
E:\Projects\repixelizer\.venv\Scripts\python.exe .\tools\gamecult_access_state.py status
|
||||
E:\Projects\repixelizer\.venv\Scripts\python.exe .\tools\gamecult_access_state.py add-evidence --type design --status accepted --note "..."
|
||||
E:\Projects\repixelizer\.venv\Scripts\python.exe .\tools\gamecult_access_prepare_compaction.py
|
||||
E:\Projects\repixelizer\.venv\Scripts\python.exe .\tools\heimdall_state.py status
|
||||
E:\Projects\repixelizer\.venv\Scripts\python.exe .\tools\heimdall_state.py add-evidence --type design --status accepted --note "..."
|
||||
E:\Projects\repixelizer\.venv\Scripts\python.exe .\tools\heimdall_prepare_compaction.py
|
||||
```
|
||||
|
||||
Fallback if a normal `python` command exists:
|
||||
|
||||
```powershell
|
||||
python .\tools\gamecult_access_state.py status
|
||||
python .\tools\gamecult_access_state.py add-evidence --type design --status accepted --note "..."
|
||||
python .\tools\gamecult_access_prepare_compaction.py
|
||||
python .\tools\heimdall_state.py status
|
||||
python .\tools\heimdall_state.py add-evidence --type design --status accepted --note "..."
|
||||
python .\tools\heimdall_prepare_compaction.py
|
||||
```
|
||||
|
||||
## Session Bootstrap And Re-entry Protocol
|
||||
|
|
@ -79,21 +81,21 @@ On fresh session load:
|
|||
- `docs/architecture.md`
|
||||
- `docs/implementation-plan.md`
|
||||
2. run:
|
||||
- `python .\tools\gamecult_access_state.py status`
|
||||
- `python .\tools\heimdall_state.py status`
|
||||
- `git status --short --branch`
|
||||
- `git log --oneline -5`
|
||||
3. restate the current next action from persisted state before editing
|
||||
|
||||
After compaction or suspicious continuity loss:
|
||||
|
||||
1. rerun `gamecult_access_state.py status`
|
||||
1. rerun `heimdall_state.py status`
|
||||
2. reread `state/map.yaml` and `notes/fresh-workspace-handoff.md`
|
||||
3. treat the persisted next action as authoritative unless fresh evidence
|
||||
contradicts it
|
||||
|
||||
When the user says to prepare for imminent compaction:
|
||||
|
||||
1. run `tools/gamecult_access_prepare_compaction.py`
|
||||
1. run `tools/heimdall_prepare_compaction.py`
|
||||
2. update only the state that actually changed
|
||||
3. rerun the helper
|
||||
4. commit the completed persistence pass unless the work is deliberately
|
||||
|
|
@ -102,10 +104,14 @@ When the user says to prepare for imminent compaction:
|
|||
## Operating Discipline
|
||||
|
||||
- Before substantial edits, restate the current mechanism and intended change.
|
||||
- Keep the shared backbone separate from app-domain data unless there is a very
|
||||
deliberate, source-grounded reason to share something.
|
||||
- Prefer embedded shared-package mode before inventing a dedicated auth service.
|
||||
- Do not let app profiles turn into duplicate copies of the shared architecture.
|
||||
- Keep identity authority central and app-domain data local unless there is a
|
||||
very deliberate, source-grounded reason to share something.
|
||||
- Prefer the Yggdrasil-hosted Heimdall service over duplicated per-app provider
|
||||
stacks.
|
||||
- Do not require a Heimdall round-trip for every guarded app route; host apps
|
||||
should verify signed claims locally and keep resource ownership checks local.
|
||||
- Do not let app profiles turn into duplicate copies of the shared
|
||||
architecture.
|
||||
- If the diff grows while the boundary gets blurrier, stop and simplify.
|
||||
- Before handoff or compaction, sync `state/map.yaml`, refresh
|
||||
`notes/fresh-workspace-handoff.md`, add distilled evidence when future belief
|
||||
|
|
|
|||
21
README.md
21
README.md
|
|
@ -1,17 +1,20 @@
|
|||
# GameCult Access
|
||||
# Heimdall
|
||||
|
||||
GameCult Access is the planned shared auth and access-control backbone for
|
||||
GameCult-hosted experiments.
|
||||
Heimdall is the planned shared auth authority for GameCult-hosted experiments.
|
||||
|
||||
The point is painfully simple:
|
||||
|
||||
- stop rebuilding Discord/Patreon login glue in every app
|
||||
- keep signed local sessions, linked identities, and capability checks reusable
|
||||
- keep provider OAuth, linked identities, grants, entitlements, and session
|
||||
issuance in one place
|
||||
- let app runtimes verify signed claims locally so "shared auth" does not
|
||||
become "everything phones home for every request"
|
||||
- keep app-domain data separate so "shared auth" does not become "shared swamp"
|
||||
|
||||
## Current Shape
|
||||
|
||||
This repo is planning and extraction work, not a landed auth service yet.
|
||||
This repo is still planning and service-contract work, not a landed auth
|
||||
service yet.
|
||||
|
||||
Canonical docs:
|
||||
|
||||
|
|
@ -25,7 +28,9 @@ Canonical docs:
|
|||
## Working Loop
|
||||
|
||||
1. Rehydrate from canonical state.
|
||||
2. Keep the shared backbone separate from app-domain data.
|
||||
3. Prefer embedded shared-package mode before inventing a central auth service.
|
||||
4. Move only one organ at a time.
|
||||
2. Keep identity authority central and app-domain data local.
|
||||
3. Prefer a Yggdrasil-hosted shared service over duplicated per-app provider
|
||||
stacks.
|
||||
4. Let app backends verify signed claims locally instead of begging Heimdall on
|
||||
every guarded route.
|
||||
5. Commit finished passes before the notes start breeding.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
## What this file is
|
||||
|
||||
This file binds Repixelizer onto the shared design in `docs/architecture.md`.
|
||||
This file binds Repixelizer onto the shared Heimdall design in
|
||||
`docs/architecture.md`.
|
||||
|
||||
It is future design, not a claim that the hosted demo already has auth.
|
||||
|
||||
|
|
@ -89,8 +90,20 @@ Important invariant:
|
|||
- job ownership resolves from the local session/account, not from Discord or
|
||||
Patreon ids
|
||||
|
||||
Heimdall should be involved in:
|
||||
|
||||
- login / link entry
|
||||
- session and claim issuance
|
||||
- entitlement refresh
|
||||
|
||||
Repixelizer should still do locally:
|
||||
|
||||
- claim verification on guarded routes
|
||||
- ownership checks on job reads, events, heartbeat, and cancel
|
||||
- queue/runtime behavior
|
||||
|
||||
## Config mapping
|
||||
|
||||
- keep runtime/queue/limit env vars under `REPIXELIZER_*`
|
||||
- move shared access machinery under `GC_ACCESS_*`
|
||||
- move Heimdall integration under `GC_ACCESS_*`
|
||||
- keep per-app role/tier/capability bindings small and app-scoped
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## What this file is
|
||||
|
||||
This file records how StreamPixels should fit onto the shared access backbone in
|
||||
This file records how StreamPixels should fit onto Heimdall in
|
||||
`docs/architecture.md` without turning streamer-audience data into shared auth
|
||||
sludge.
|
||||
|
||||
|
|
@ -27,6 +27,13 @@ That is already compatible with the shared backbone idea.
|
|||
- 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
|
||||
- entitlement refresh
|
||||
- shared claim semantics
|
||||
|
||||
## What StreamPixels should keep separate
|
||||
|
||||
- viewer audience profiles
|
||||
|
|
@ -37,7 +44,7 @@ That is already compatible with the shared backbone idea.
|
|||
|
||||
In plain language:
|
||||
|
||||
- the same backbone can authenticate and authorize the control plane
|
||||
- Heimdall can authenticate and authorize the control plane
|
||||
- StreamPixels audience data stays StreamPixels data
|
||||
|
||||
## Capability direction
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
# GameCult Hosted Access Architecture
|
||||
# Heimdall Architecture
|
||||
|
||||
## What this file is
|
||||
|
||||
This file is the shared architecture note for GameCult-hosted experiments that
|
||||
need the same basic promise:
|
||||
This file is the shared architecture note for Heimdall, the planned auth
|
||||
authority service for GameCult-hosted experiments.
|
||||
|
||||
Its basic promise is:
|
||||
|
||||
- let people sign in with Discord and/or Patreon
|
||||
- decide access from GameCult/community entitlements
|
||||
- keep app runtimes tied to local sessions and local accounts, not provider ids
|
||||
- decide access from GameCult/community entitlements and local grants
|
||||
- issue signed local claims that hosted apps can verify without trusting raw
|
||||
provider tokens
|
||||
|
||||
It is not a map of landed auth code yet.
|
||||
|
||||
|
|
@ -19,30 +22,32 @@ If every experiment rebuilds:
|
|||
- identity linking
|
||||
- entitlement refresh
|
||||
- session cookies
|
||||
- queue/job ownership
|
||||
- grant handling
|
||||
- "Discord role OR Patreon tier" logic
|
||||
|
||||
then we get five near-identical auth pits and deserve what happens next.
|
||||
|
||||
The reusable move is to build one GameCult hosted-access layer and let each app
|
||||
The reusable move is to build one Heimdall authority service and let each app
|
||||
bind its own policy and runtime seams onto it.
|
||||
|
||||
## Design goals
|
||||
|
||||
- one reusable local account/session model across hosted experiments
|
||||
- provider login and linking owned by one shared access layer
|
||||
- provider login and linking owned by one shared authority
|
||||
- per-app capability policies instead of hard-coded one-off gate checks
|
||||
- host runtimes consume signed local session claims, not raw provider tokens
|
||||
- host runtimes consume signed local claims, not raw provider tokens
|
||||
- routine route auth stays local to the host app after claim verification
|
||||
- keep app-domain data separate unless there is a deliberate reason to share it
|
||||
- start as an embedded shared package before pretending we need a central auth
|
||||
service
|
||||
- fit the current GameCult topology: Heimdall on Yggdrasil behind nginx, with
|
||||
hosted apps either on the same host or verifying over normal HTTPS/JWKS
|
||||
|
||||
## Non-goals
|
||||
|
||||
- becoming a general public identity provider
|
||||
- building multi-tenant SaaS auth machinery
|
||||
- inventing global billing analytics because we got bored
|
||||
- forcing cross-app single sign-on on day one before a second app exists
|
||||
- forcing app-domain data into a shared auth store
|
||||
- requiring every guarded route in every app to call Heimdall synchronously
|
||||
|
||||
## Core concepts
|
||||
|
||||
|
|
@ -61,6 +66,8 @@ bind its own policy and runtime seams onto it.
|
|||
policy rules
|
||||
- `Session`
|
||||
signed local app/browser session; the browser gets this, not provider tokens
|
||||
- `AccessClaim`
|
||||
signed app-facing claim set derived from the account, grants, and app policy
|
||||
- `AuditEvent`
|
||||
durable record for login, link, unlink, refresh, denial, and admin overrides
|
||||
|
||||
|
|
@ -69,20 +76,20 @@ bind its own policy and runtime seams onto it.
|
|||
```mermaid
|
||||
flowchart TD
|
||||
A["Browser / app web UI"] --> B["Hosted experiment web surface"]
|
||||
B --> C["GameCult access layer"]
|
||||
B -->|start auth / link| C["Heimdall"]
|
||||
C --> D["Discord OAuth + guild role checks"]
|
||||
C --> E["Patreon OAuth + campaign/tier checks"]
|
||||
C --> F["Local account/session/grant store"]
|
||||
C --> G["Signed access claims"]
|
||||
G --> H["Hosted experiment runtime"]
|
||||
C --> F["Local auth/control-plane store"]
|
||||
C --> G["Signed sessions / claims"]
|
||||
B -->|verify claims locally| H["Hosted experiment runtime"]
|
||||
```
|
||||
|
||||
The important split:
|
||||
|
||||
- the access layer owns provider OAuth, identity linking, entitlement refresh,
|
||||
and signed local sessions
|
||||
- the host experiment owns its workload, queue, editor, render path, or other
|
||||
product-specific machinery
|
||||
- Heimdall owns provider OAuth, identity linking, entitlement refresh, signed
|
||||
sessions, claims, grants, and audit surfaces
|
||||
- the host experiment owns its workload, queue, editor, render path, viewer
|
||||
profile model, creator model, or other product-specific machinery
|
||||
|
||||
Do not smear provider logic directly through every host runtime.
|
||||
|
||||
|
|
@ -94,10 +101,11 @@ pour their user data into one communal bucket.
|
|||
The reusable thing is:
|
||||
|
||||
- provider OAuth plumbing
|
||||
- signed local session mechanics
|
||||
- signed session and claim mechanics
|
||||
- linked-identity primitives
|
||||
- entitlement refresh
|
||||
- capability evaluation
|
||||
- local grant and audit surfaces
|
||||
|
||||
The non-reusable-by-default thing is:
|
||||
|
||||
|
|
@ -109,12 +117,12 @@ The non-reusable-by-default thing is:
|
|||
|
||||
Recommended default:
|
||||
|
||||
- shared auth/access **code**
|
||||
- shared auth/access **authority**
|
||||
- separate app databases or at least separate app-owned schemas/tables for
|
||||
domain data
|
||||
- if a shared access store exists at all, keep it limited to auth/control-plane
|
||||
truth such as identities, grants, entitlement snapshots, sessions, and audit
|
||||
events
|
||||
- if a shared Heimdall store exists at all, keep it limited to auth/control
|
||||
plane truth such as identities, grants, entitlement snapshots, sessions,
|
||||
claims metadata, and audit events
|
||||
|
||||
Do not let "same auth backbone" quietly mutate into "same user-data swamp."
|
||||
|
||||
|
|
@ -182,8 +190,8 @@ Important invariants:
|
|||
- one provider identity belongs to one local account only
|
||||
- provider tokens stay server-side only
|
||||
- session cookies represent local sessions, not provider trust directly
|
||||
- app-specific capability checks read local claims, not Discord or Patreon on
|
||||
every route
|
||||
- app-specific capability checks read signed local claims, not Discord or
|
||||
Patreon on every route
|
||||
|
||||
## Provider adapters
|
||||
|
||||
|
|
@ -203,7 +211,7 @@ Shared provider/gate adapters should be boring and reusable:
|
|||
|
||||
## Policy model
|
||||
|
||||
The reusable core should not hard-code one app's access rules. It should
|
||||
The shared authority should not hard-code one app's access rules. It should
|
||||
evaluate capabilities against an app profile.
|
||||
|
||||
Suggested app-profile shape:
|
||||
|
|
@ -227,18 +235,41 @@ queue_submit = app_access
|
|||
admin_access = grant.operator || grant.admin_access
|
||||
```
|
||||
|
||||
## Request path discipline
|
||||
|
||||
This is where shared authority stays useful instead of becoming a nuisance.
|
||||
|
||||
Heimdall should be in the loop for:
|
||||
|
||||
- OAuth start and callback flows
|
||||
- explicit identity linking and unlinking
|
||||
- session issuance and refresh
|
||||
- entitlement refresh
|
||||
- grant administration
|
||||
- occasional explicit introspection or revocation checks
|
||||
|
||||
Host apps should stay in the loop for:
|
||||
|
||||
- routine route authorization after claim verification
|
||||
- resource ownership checks
|
||||
- domain-specific permission decisions that depend on app-local data
|
||||
|
||||
Normal guarded routes should **not** need to round-trip to Heimdall every time.
|
||||
|
||||
## Auth and linking flow
|
||||
|
||||
The generic flow should be the same across hosted experiments:
|
||||
|
||||
1. user starts OAuth from the host app UI
|
||||
2. shared access layer signs OAuth `state`
|
||||
3. provider callback resolves the provider identity
|
||||
4. shared layer finds or creates the local account
|
||||
5. if the user was already signed in locally, the identity may link onto the
|
||||
2. host app redirects to Heimdall or calls Heimdall to begin the flow
|
||||
3. Heimdall signs OAuth `state`
|
||||
4. provider callback resolves the provider identity through Heimdall
|
||||
5. Heimdall finds or creates the local account
|
||||
6. if the user was already signed in locally, the identity may link onto the
|
||||
existing account instead of creating a new one
|
||||
6. shared layer refreshes entitlements and capability claims
|
||||
7. shared layer issues or updates the local session cookie
|
||||
7. Heimdall refreshes entitlements and capability claims
|
||||
8. Heimdall issues or updates the local session / claim artifacts consumed by
|
||||
the host app
|
||||
|
||||
The browser should never be the custodian of provider tokens.
|
||||
|
||||
|
|
@ -248,6 +279,7 @@ Every hosted experiment should integrate through the same seams:
|
|||
|
||||
- define an `app_slug`
|
||||
- define the app capability profile
|
||||
- verify Heimdall-issued claims locally
|
||||
- mark which routes are public, authenticated, or capability-gated
|
||||
- attach `account_id`, `session_id`, and `access_revision` to owned resources
|
||||
such as jobs, drafts, uploads, or queue entries
|
||||
|
|
@ -260,50 +292,64 @@ If an app has a queue or job system:
|
|||
- active-job cancellation policy is app-specific, but the recommended first cut
|
||||
is still "let the active job finish, block new work"
|
||||
|
||||
## Deployment topology
|
||||
|
||||
Current intended first deployment shape:
|
||||
|
||||
- Heimdall runs on `yggdrasil.gamecult.org`
|
||||
- Heimdall binds a localhost port behind nginx
|
||||
- public auth hostname is expected to become `heimdall.gamecult.org`
|
||||
- same-host app stacks such as StreamPixels and Repixelizer call Heimdall over
|
||||
localhost or normal private routing when they need auth-authority work done
|
||||
- app backends verify signed claims locally for routine guarded requests
|
||||
|
||||
This matches the host pattern already used by other GameCult app workloads.
|
||||
|
||||
## Deployment modes
|
||||
|
||||
### Mode 1: Embedded shared package
|
||||
### Mode 1: Same-host shared access service
|
||||
|
||||
Recommended first cut.
|
||||
|
||||
- one shared `gamecult_access` package/module
|
||||
- each hosted experiment embeds it
|
||||
- each app keeps its own runtime and route integration
|
||||
- sessions may still be app-local
|
||||
- one Heimdall service on Yggdrasil
|
||||
- each hosted experiment keeps its own runtime and route integration
|
||||
- each app keeps its own domain-data store
|
||||
- apps verify signed claims locally after issuance
|
||||
|
||||
Why this is the right first cut:
|
||||
|
||||
- reusable without forcing a separate always-on dependency
|
||||
- small blast radius
|
||||
- easy to evolve while only one or two apps use it
|
||||
- fits the current host topology
|
||||
- avoids cross-runtime shared-library nonsense
|
||||
- keeps OAuth and provider sludge in one place
|
||||
- keeps routine guarded requests local inside the app
|
||||
|
||||
### Mode 2: Same-host shared access service
|
||||
### Mode 2: Multi-host shared access service
|
||||
|
||||
Use this when multiple apps on the same server want one shared access store and
|
||||
session issuer, but full centralization still feels premature.
|
||||
Use this when multiple app stacks across hosts still want one shared auth
|
||||
authority, but the same claim-verification discipline continues.
|
||||
|
||||
### Mode 3: Dedicated GameCult access service
|
||||
### Mode 3: Wider cross-app session platform
|
||||
|
||||
Only do this when multiple independently deployed apps genuinely need:
|
||||
Only do this when independently deployed apps genuinely need:
|
||||
|
||||
- shared cross-app sessions
|
||||
- central grant administration
|
||||
- one callback origin for all providers
|
||||
- enough auth churn that duplicated embedded deployments become the bigger pain
|
||||
- enough auth churn that Heimdall is clearly a long-lived platform surface
|
||||
|
||||
Do not build this first because it sounds important.
|
||||
|
||||
Even in this mode, the dedicated service should own auth/control-plane data, not
|
||||
absorb every app's audience or product data.
|
||||
Even in this mode, Heimdall should own auth/control-plane data, not absorb
|
||||
every app's audience or product data.
|
||||
|
||||
## Configuration split
|
||||
|
||||
Suggested generic env surface:
|
||||
|
||||
- `GC_ACCESS_ENABLED=1`
|
||||
- `GC_ACCESS_BASE_URL=https://heimdall.gamecult.org`
|
||||
- `GC_ACCESS_INTERNAL_URL=http://127.0.0.1:4100`
|
||||
- `GC_ACCESS_SESSION_SECRET=...`
|
||||
- `GC_ACCESS_BASE_URL=https://access.gamecult.org` or host-app local equivalent
|
||||
- `GC_ACCESS_DISCORD_CLIENT_ID=...`
|
||||
- `GC_ACCESS_DISCORD_CLIENT_SECRET=...`
|
||||
- `GC_ACCESS_DISCORD_BOT_TOKEN=...`
|
||||
|
|
@ -321,10 +367,12 @@ env namespaces.
|
|||
|
||||
The correct reusable seam is:
|
||||
|
||||
- shared provider/session/entitlement/capability machinery
|
||||
- one shared identity authority service
|
||||
- local verification of signed claims by each host app
|
||||
- thin per-app access profiles and runtime bindings
|
||||
|
||||
The incorrect reusable seam is:
|
||||
|
||||
- cloning one app’s auth blob into every future experiment
|
||||
- cloning one app's auth blob into every future experiment
|
||||
- or turning shared auth into shared domain-data soup
|
||||
- or forcing every guarded app route to make a Heimdall network hop
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
# GameCult Access Implementation Plan
|
||||
# Heimdall Implementation Plan
|
||||
|
||||
## What this file is
|
||||
|
||||
This file is the forward plan and active hypothesis ledger for GameCult Access.
|
||||
This file is the forward plan and active hypothesis ledger for Heimdall.
|
||||
|
||||
It is not the canonical shared architecture note. That lives in
|
||||
`docs/architecture.md`.
|
||||
|
|
@ -16,86 +16,112 @@ There is no landed auth machine here yet.
|
|||
|
||||
What exists right now is:
|
||||
|
||||
- the extracted shared architecture note
|
||||
- the shared architecture note
|
||||
- app-binding notes for Repixelizer and StreamPixels
|
||||
- explicit state/doctrine scaffolding so the plan does not immediately turn into
|
||||
soup
|
||||
- explicit state/doctrine scaffolding so the plan does not immediately turn
|
||||
into soup
|
||||
|
||||
## Primary objective
|
||||
|
||||
Build a reusable `gamecult_access` layer that can:
|
||||
Build Heimdall as a shared auth authority service that can:
|
||||
|
||||
- own provider OAuth and identity linking
|
||||
- issue signed local sessions
|
||||
- issue signed sessions and app-facing claims
|
||||
- refresh Discord and Patreon entitlements
|
||||
- evaluate per-app capability rules
|
||||
- stay separate from app-domain data
|
||||
- expose a clean contract to app backends without owning their domain data
|
||||
|
||||
## Current priorities
|
||||
|
||||
### 1. Lock the shared boundary
|
||||
### 1. Lock the service boundary
|
||||
|
||||
Goals:
|
||||
|
||||
- keep the shared layer scoped to auth/control-plane machinery
|
||||
- keep Heimdall scoped to auth/control-plane machinery
|
||||
- keep app-domain data in app-owned stores
|
||||
- keep app profiles thin and explicit
|
||||
|
||||
### 2. Prefer embedded-package mode first
|
||||
### 2. Define the contract before the service grows teeth
|
||||
|
||||
Goals:
|
||||
|
||||
- define the reusable package/module seam
|
||||
- avoid a dedicated shared service until a second real app proves the need
|
||||
- keep initial blast radius small
|
||||
- define signed claim/session shapes
|
||||
- define OAuth start/callback/link surfaces
|
||||
- define grant and entitlement persistence boundaries
|
||||
- define how app backends verify claims locally
|
||||
|
||||
### 3. Make Repixelizer the first binding
|
||||
### 3. Make Repixelizer the first auth-blank consumer
|
||||
|
||||
Goals:
|
||||
|
||||
- gate hosted access and queue submission through local sessions
|
||||
- gate hosted access and queue submission through Heimdall-issued claims
|
||||
- attach queue ownership to `account_id` and `session_id`
|
||||
- preserve the existing hosted GUI and queue runtime
|
||||
|
||||
### 4. Keep StreamPixels separate where it matters
|
||||
### 4. Migrate StreamPixels without flattening its good local seams
|
||||
|
||||
Goals:
|
||||
|
||||
- let StreamPixels reuse auth/control-plane machinery
|
||||
- move provider/linking authority toward Heimdall
|
||||
- keep viewer profiles, creator memberships, connector creds, and audience data
|
||||
in StreamPixels
|
||||
- keep creator/operator route authorization local to StreamPixels after claim
|
||||
verification
|
||||
|
||||
### 5. Capture the Yggdrasil deployment shape early
|
||||
|
||||
Goals:
|
||||
|
||||
- treat Heimdall as a localhost service behind nginx on Yggdrasil
|
||||
- support same-host callers first
|
||||
- avoid per-request network chatter for routine app authorization
|
||||
|
||||
## Proposed phases
|
||||
|
||||
### Phase 1: Shared package scaffold
|
||||
### Phase 1: Contract and schema scaffold
|
||||
|
||||
- create package/module boundaries for:
|
||||
- provider adapters
|
||||
- signed session issuance/verification
|
||||
- linked identity persistence interfaces
|
||||
- entitlement refresh/evaluation
|
||||
- capability rule evaluation
|
||||
- define:
|
||||
- signed claim/session payloads
|
||||
- JWKS or equivalent verification surface
|
||||
- OAuth `state` and callback contracts
|
||||
- grant / entitlement / audit record shapes
|
||||
- app profile / capability rule inputs and outputs
|
||||
|
||||
### Phase 2: Repixelizer binding
|
||||
### Phase 2: Heimdall service skeleton
|
||||
|
||||
- integrate the shared layer into the hosted demo
|
||||
- create the first real service layout for:
|
||||
- provider OAuth start/callback routes
|
||||
- account / linked-identity persistence
|
||||
- session issuance and refresh
|
||||
- capability evaluation
|
||||
- audit logging
|
||||
|
||||
### Phase 3: Repixelizer binding
|
||||
|
||||
- integrate Heimdall into the hosted demo
|
||||
- gate `POST /api/jobs`
|
||||
- gate per-job read/cancel/event routes by local session ownership
|
||||
|
||||
### Phase 3: StreamPixels control-plane fit check
|
||||
### Phase 4: StreamPixels authority migration
|
||||
|
||||
- map the shared access layer onto StreamPixels creator/operator auth seams
|
||||
- explicitly reject audience-data centralization
|
||||
- replace provider/linking authority paths with Heimdall calls where they truly
|
||||
belong
|
||||
- keep viewer/session expansion, creator memberships, and operator route
|
||||
enforcement local where they are app-domain concerns
|
||||
|
||||
### Phase 4: Shared-store decision
|
||||
### Phase 5: Ops and deploy
|
||||
|
||||
- only after two real bindings exist, decide whether auth/control-plane state
|
||||
should stay embedded or move into a shared service/store
|
||||
- define nginx/systemd/env/runbook surfaces for Heimdall on Yggdrasil
|
||||
- keep callers and state stores localhost-first where practical
|
||||
|
||||
## Guardrails
|
||||
|
||||
- do not let auth reuse become data mixing
|
||||
- do not build a central service first just because it sounds grand
|
||||
- do not turn Heimdall into a mandatory round-trip for every guarded app route
|
||||
- do not fantasize about one shared cross-runtime embedded library; the apps can
|
||||
share contracts without sharing a runtime
|
||||
- do not duplicate the shared architecture note into app profiles
|
||||
- do not rip out app-local ownership and authorization logic that still belongs
|
||||
in the app
|
||||
- keep one clear hypothesis per pass
|
||||
- commit completed work before the planning notes start breeding
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Fresh Workspace Handoff
|
||||
|
||||
This is the re-entry packet for `E:\Projects\gamecult-access`.
|
||||
This is the re-entry packet for `E:\Projects\Heimdall`.
|
||||
|
||||
It is intentionally short. Historical proof belongs in git history and the
|
||||
distilled `state/evidence.jsonl` ledger; the shared architecture belongs in
|
||||
|
|
@ -13,7 +13,7 @@ From the repo root:
|
|||
|
||||
```powershell
|
||||
# This repo has no dedicated venv yet. Use any working Python 3.11+.
|
||||
E:\Projects\repixelizer\.venv\Scripts\python.exe .\tools\gamecult_access_state.py status
|
||||
E:\Projects\repixelizer\.venv\Scripts\python.exe .\tools\heimdall_state.py status
|
||||
Get-Content '.\state\map.yaml'
|
||||
Get-Content '.\notes\fresh-workspace-handoff.md'
|
||||
Get-Content '.\docs\architecture.md'
|
||||
|
|
@ -27,23 +27,29 @@ Do not trust this file for the exact live HEAD. Always check git.
|
|||
|
||||
## Current Orientation
|
||||
|
||||
- this repo is planning and extraction work, not a landed auth service yet
|
||||
- this repo is still planning and service-contract work, not a landed auth
|
||||
service yet
|
||||
- the canonical shared architecture lives in `docs/architecture.md`
|
||||
- app profiles live under `docs/app-profiles/`
|
||||
- the key boundary is explicit:
|
||||
- shared auth/control-plane mechanics are reusable
|
||||
- Heimdall owns OAuth, linking, grants, entitlement refresh, and signed claim
|
||||
issuance
|
||||
- app-domain data stays app-owned by default
|
||||
- Repixelizer is the first binding target
|
||||
- StreamPixels is the boundary case that proves backbone reuse must not imply
|
||||
audience-data centralization
|
||||
- the recommended first implementation mode is an embedded shared package, not a
|
||||
dedicated auth service
|
||||
- host apps verify signed claims locally for routine auth instead of calling
|
||||
Heimdall on every request
|
||||
- Repixelizer is the first auth-blank binding target
|
||||
- StreamPixels is the migration target with useful existing auth seams that
|
||||
should not be flattened into mush
|
||||
- the intended first deployment shape is a Heimdall service on Yggdrasil behind
|
||||
nginx, not an embedded cross-runtime shared library fantasy
|
||||
|
||||
## 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.
|
||||
|
||||
|
|
@ -51,16 +57,17 @@ Do not trust this file for the exact live HEAD. Always check git.
|
|||
|
||||
Do not continue implementation automatically from a rehydrate-only request.
|
||||
|
||||
If the user asks to continue, the current next move is to define the embedded
|
||||
`gamecult_access` package seam:
|
||||
If the user asks to continue, the current next move is to define the first
|
||||
Heimdall service contract and skeleton:
|
||||
|
||||
- storage interfaces
|
||||
- provider adapter interfaces
|
||||
- signed-session claims
|
||||
- capability rule evaluation boundaries
|
||||
- OAuth start/callback surfaces
|
||||
- signed session / claim shape
|
||||
- claim verification contract for app backends
|
||||
- entitlement/grant persistence boundaries
|
||||
- Repixelizer and StreamPixels integration seams
|
||||
|
||||
without central-service theatrics and without smearing app-domain data into the
|
||||
shared layer.
|
||||
without dragging app-domain data into the shared layer and without forcing
|
||||
per-request auth round-trips.
|
||||
|
||||
## Immediate Re-entry Instruction
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"branches": [
|
||||
{
|
||||
"id": "main",
|
||||
"hypothesis": "A shared GameCult access backbone can be reused across hosted experiments if OAuth/session/capability machinery is centralized while app-domain data stays app-owned.",
|
||||
"hypothesis": "A shared Heimdall authority service on Yggdrasil can be reused across hosted experiments if OAuth, sessions, grants, and capability machinery are centralized while app-domain data and routine route authorization stay app-local.",
|
||||
"status": "active",
|
||||
"artifacts": [
|
||||
"state/map.yaml",
|
||||
|
|
@ -11,7 +11,7 @@
|
|||
"docs/architecture.md",
|
||||
"docs/implementation-plan.md"
|
||||
],
|
||||
"notes": "Current mainline. Repixelizer is the first binding target; StreamPixels is the boundary stress test."
|
||||
"notes": "Current mainline. Repixelizer is the first auth-blank binding target; StreamPixels is the migration and boundary stress test."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
{"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":"accepted","note":"The recommended first implementation mode is an embedded shared gamecult_access package, not a dedicated auth service."}
|
||||
{"ts":"2026-04-26T00:00:00Z","type":"boundary","status":"accepted","note":"StreamPixels can reuse the same auth/control-plane backbone, but streamer-audience data, creator memberships, connector credentials, and runtime state should 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."}
|
||||
|
|
|
|||
|
|
@ -1,23 +1,24 @@
|
|||
project:
|
||||
name: GameCult Access
|
||||
name: Heimdall
|
||||
status: active
|
||||
phase: architecture_extraction
|
||||
phase: service_topology_alignment
|
||||
premise: >
|
||||
Build one reusable GameCult-hosted access backbone for OAuth, linked
|
||||
identities, local sessions, and capability checks without mixing in
|
||||
app-domain audience or product data.
|
||||
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 reusable shared access layer that Repixelizer, StreamPixels, and
|
||||
future GameCult-hosted experiments can bind onto.
|
||||
Create a shared auth authority service that Repixelizer, StreamPixels, and
|
||||
future GameCult-hosted experiments can consume.
|
||||
current: >
|
||||
Lock the shared architecture, extraction path, and app-boundary rules before
|
||||
implementation starts breeding myths.
|
||||
Lock the Heimdall service boundary, Yggdrasil deployment shape, and app
|
||||
integration rules before implementation starts breeding myths.
|
||||
|
||||
constraints:
|
||||
- Shared auth/access code is reusable; app-domain data is separate by default.
|
||||
- Prefer an embedded shared package before a dedicated auth service.
|
||||
- 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.
|
||||
|
||||
|
|
@ -25,10 +26,10 @@ 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.
|
||||
- The shared access layer owns OAuth, local sessions, entitlement refresh, capability evaluation, and audit surfaces.
|
||||
- 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.
|
||||
- Embedded shared-package mode is the default first implementation strategy.
|
||||
- StreamPixels can reuse control-plane auth mechanics without centralizing streamer-audience data.
|
||||
- Host apps verify signed claims locally and keep resource ownership checks local.
|
||||
- StreamPixels can reuse shared auth authority without centralizing streamer-audience data.
|
||||
- Persistent state cruft is the same failure class as architecture cruft.
|
||||
|
||||
data_flow:
|
||||
|
|
@ -37,45 +38,48 @@ data_flow:
|
|||
- 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 shared code and app-domain data boundaries explicit
|
||||
- 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 central-service theatrics before two real bindings justify them
|
||||
- reject per-request auth round-trip chatter
|
||||
|
||||
active_subgoals:
|
||||
- Keep the shared architecture explicit and separate from app profiles.
|
||||
- Preserve the default boundary that app-domain data remains app-owned.
|
||||
- Make Repixelizer the first binding and StreamPixels the boundary stress test.
|
||||
- Define the extraction seam for an embedded `gamecult_access` package.
|
||||
- Align the plan to Heimdall as a Yggdrasil-hosted authority service.
|
||||
- Define the claim-verification contract that Repixelizer and StreamPixels will consume locally.
|
||||
|
||||
accepted_design:
|
||||
- `docs/architecture.md` is the canonical shared architecture note.
|
||||
- `docs/app-profiles/*.md` are thin app bindings, not duplicate brains.
|
||||
- The reusable backbone owns provider OAuth, identity linking, signed local sessions, entitlement refresh, capability evaluation, and audit events.
|
||||
- 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.
|
||||
- A future shared access store, if it exists, should hold auth/control-plane truth only.
|
||||
- Repixelizer is the first hosted-app binding.
|
||||
- StreamPixels is the boundary case that proves backbone reuse must not imply audience-data centralization.
|
||||
- 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.
|
||||
|
||||
landed_baseline:
|
||||
- Shared architecture extracted into its own repo.
|
||||
- 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.
|
||||
|
||||
rejected_paths:
|
||||
- name: per_app_auth_copy_paste
|
||||
reason: duplicates the same provider/session sludge across every experiment
|
||||
- name: central_auth_service_first
|
||||
reason: adds deployment weight before two real bindings justify it
|
||||
- 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: The shared hosted-access planning work has been extracted into its own repo with a canonical architecture note, a forward implementation plan, and thin app profiles for Repixelizer and StreamPixels. The key boundary is explicit: reusable auth/control-plane code is shared, app-domain data remains app-owned by default. The recommended first implementation mode is an embedded shared package rather than a dedicated auth service.
|
||||
next_action: Start the first implementation-oriented pass by defining the embedded `gamecult_access` package seam: storage interfaces, provider adapter interfaces, signed-session claims, and capability rule evaluation boundaries, while keeping Repixelizer as the first binding target.
|
||||
summary: The shared hosted-access planning work now lives in Heimdall, a service-first auth authority repo with a Yggdrasil-hosted topology in mind. The key boundary is explicit: Heimdall owns OAuth, linking, grants, entitlement refresh, and signed claims; app-domain data remains app-owned; routine route authorization stays local after claim verification.
|
||||
next_action: Start the first implementation-oriented pass by defining Heimdall's service contract and skeleton: OAuth start/callback surfaces, signed session and claim payloads, claim verification material for app backends, and the first Repixelizer and StreamPixels integration seams.
|
||||
|
|
|
|||
|
|
@ -199,10 +199,10 @@ def add_handoff_checks(findings: list[Finding]) -> None:
|
|||
|
||||
def add_agents_checks(findings: list[Finding]) -> None:
|
||||
text = read_text(AGENTS_PATH)
|
||||
if "gamecult_access_prepare_compaction.py" in text:
|
||||
if "heimdall_prepare_compaction.py" in text:
|
||||
findings.append(Finding("ok", "AGENTS.md tells agents to use the compaction helper"))
|
||||
else:
|
||||
findings.append(Finding("error", "AGENTS.md does not mention tools/gamecult_access_prepare_compaction.py"))
|
||||
findings.append(Finding("error", "AGENTS.md does not mention tools/heimdall_prepare_compaction.py"))
|
||||
if "prepare for imminent compaction" in text.lower():
|
||||
findings.append(Finding("ok", "AGENTS.md names the imminent-compaction trigger"))
|
||||
else:
|
||||
|
|
@ -232,7 +232,7 @@ def render_report(findings: list[Finding], status: str, log: str, evidence: list
|
|||
}
|
||||
|
||||
lines = [
|
||||
"GameCult Access pre-compaction persistence check",
|
||||
"Heimdall pre-compaction persistence check",
|
||||
f"Workspace: {ROOT}",
|
||||
f"Findings: {counts['ok']} ok, {counts['warn']} warn, {counts['error']} error",
|
||||
"",
|
||||
|
|
@ -280,7 +280,7 @@ def render_report(findings: list[Finding], status: str, log: str, evidence: list
|
|||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Audit GameCult Access persistent state before imminent compaction."
|
||||
description="Audit Heimdall persistent state before imminent compaction."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--strict",
|
||||
|
|
@ -131,7 +131,7 @@ def cmd_close_branch(args: argparse.Namespace) -> int:
|
|||
|
||||
def build_parser() -> argparse.ArgumentParser:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Inspect and update GameCult Access persistent state files."
|
||||
description="Inspect and update Heimdall persistent state files."
|
||||
)
|
||||
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||
|
||||
Loading…
Reference in a new issue