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
|
## Project Purpose
|
||||||
|
|
||||||
GameCult Access exists to hold the shared auth and access-control architecture
|
Heimdall exists to hold the shared auth authority design for GameCult-hosted
|
||||||
for GameCult-hosted experiments without letting that shared layer metastasize
|
experiments without letting that shared layer metastasize into a giant
|
||||||
into a giant mixed-data pit.
|
mixed-data pit.
|
||||||
|
|
||||||
The planned reusable backbone is:
|
The planned shared authority owns:
|
||||||
|
|
||||||
- provider OAuth and linking
|
- provider OAuth and linking
|
||||||
- signed local sessions
|
- signed session / claim issuance
|
||||||
- entitlement refresh
|
- entitlement refresh
|
||||||
- capability evaluation
|
- capability evaluation
|
||||||
- app-facing access claims
|
- app-facing access claims
|
||||||
|
- audit and grant surfaces
|
||||||
|
|
||||||
The planned non-shared-by-default layer is:
|
The planned non-shared-by-default layer is:
|
||||||
|
|
||||||
|
|
@ -21,6 +22,7 @@ The planned non-shared-by-default layer is:
|
||||||
- creator data
|
- creator data
|
||||||
- queue/job payloads
|
- queue/job payloads
|
||||||
- product-specific state
|
- product-specific state
|
||||||
|
- app-local resource ownership checks
|
||||||
|
|
||||||
## Canonical State
|
## Canonical State
|
||||||
|
|
||||||
|
|
@ -38,35 +40,35 @@ The planned non-shared-by-default layer is:
|
||||||
|
|
||||||
## Important Paths
|
## Important Paths
|
||||||
|
|
||||||
- Project root: `E:\Projects\gamecult-access`
|
- Project root: `E:\Projects\Heimdall`
|
||||||
- Shared architecture: `E:\Projects\gamecult-access\docs\architecture.md`
|
- Shared architecture: `E:\Projects\Heimdall\docs\architecture.md`
|
||||||
- Implementation plan: `E:\Projects\gamecult-access\docs\implementation-plan.md`
|
- Implementation plan: `E:\Projects\Heimdall\docs\implementation-plan.md`
|
||||||
- Repixelizer profile: `E:\Projects\gamecult-access\docs\app-profiles\repixelizer.md`
|
- Repixelizer profile: `E:\Projects\Heimdall\docs\app-profiles\repixelizer.md`
|
||||||
- StreamPixels profile: `E:\Projects\gamecult-access\docs\app-profiles\streampixels.md`
|
- StreamPixels profile: `E:\Projects\Heimdall\docs\app-profiles\streampixels.md`
|
||||||
- Handoff summary: `E:\Projects\gamecult-access\notes\fresh-workspace-handoff.md`
|
- Handoff summary: `E:\Projects\Heimdall\notes\fresh-workspace-handoff.md`
|
||||||
- State CLI: `E:\Projects\gamecult-access\tools\gamecult_access_state.py`
|
- State CLI: `E:\Projects\Heimdall\tools\heimdall_state.py`
|
||||||
- Pre-compaction helper: `E:\Projects\gamecult-access\tools\gamecult_access_prepare_compaction.py`
|
- Pre-compaction helper: `E:\Projects\Heimdall\tools\heimdall_prepare_compaction.py`
|
||||||
|
|
||||||
## Useful Commands
|
## Useful Commands
|
||||||
|
|
||||||
This repo does not carry a dedicated virtualenv yet. Use any working Python
|
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`
|
3.11+ interpreter. On this workstation,
|
||||||
is known-good.
|
`E:\Projects\repixelizer\.venv\Scripts\python.exe` is known-good.
|
||||||
|
|
||||||
Preferred here:
|
Preferred here:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
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
|
||||||
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\heimdall_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_prepare_compaction.py
|
||||||
```
|
```
|
||||||
|
|
||||||
Fallback if a normal `python` command exists:
|
Fallback if a normal `python` command exists:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
python .\tools\gamecult_access_state.py status
|
python .\tools\heimdall_state.py status
|
||||||
python .\tools\gamecult_access_state.py add-evidence --type design --status accepted --note "..."
|
python .\tools\heimdall_state.py add-evidence --type design --status accepted --note "..."
|
||||||
python .\tools\gamecult_access_prepare_compaction.py
|
python .\tools\heimdall_prepare_compaction.py
|
||||||
```
|
```
|
||||||
|
|
||||||
## Session Bootstrap And Re-entry Protocol
|
## Session Bootstrap And Re-entry Protocol
|
||||||
|
|
@ -79,21 +81,21 @@ On fresh session load:
|
||||||
- `docs/architecture.md`
|
- `docs/architecture.md`
|
||||||
- `docs/implementation-plan.md`
|
- `docs/implementation-plan.md`
|
||||||
2. run:
|
2. run:
|
||||||
- `python .\tools\gamecult_access_state.py status`
|
- `python .\tools\heimdall_state.py status`
|
||||||
- `git status --short --branch`
|
- `git status --short --branch`
|
||||||
- `git log --oneline -5`
|
- `git log --oneline -5`
|
||||||
3. restate the current next action from persisted state before editing
|
3. restate the current next action from persisted state before editing
|
||||||
|
|
||||||
After compaction or suspicious continuity loss:
|
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`
|
2. reread `state/map.yaml` and `notes/fresh-workspace-handoff.md`
|
||||||
3. treat the persisted next action as authoritative unless fresh evidence
|
3. treat the persisted next action as authoritative unless fresh evidence
|
||||||
contradicts it
|
contradicts it
|
||||||
|
|
||||||
When the user says to prepare for imminent compaction:
|
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
|
2. update only the state that actually changed
|
||||||
3. rerun the helper
|
3. rerun the helper
|
||||||
4. commit the completed persistence pass unless the work is deliberately
|
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
|
## Operating Discipline
|
||||||
|
|
||||||
- Before substantial edits, restate the current mechanism and intended change.
|
- Before substantial edits, restate the current mechanism and intended change.
|
||||||
- Keep the shared backbone separate from app-domain data unless there is a very
|
- Keep identity authority central and app-domain data local unless there is a
|
||||||
deliberate, source-grounded reason to share something.
|
very deliberate, source-grounded reason to share something.
|
||||||
- Prefer embedded shared-package mode before inventing a dedicated auth service.
|
- Prefer the Yggdrasil-hosted Heimdall service over duplicated per-app provider
|
||||||
- Do not let app profiles turn into duplicate copies of the shared architecture.
|
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.
|
- If the diff grows while the boundary gets blurrier, stop and simplify.
|
||||||
- Before handoff or compaction, sync `state/map.yaml`, refresh
|
- Before handoff or compaction, sync `state/map.yaml`, refresh
|
||||||
`notes/fresh-workspace-handoff.md`, add distilled evidence when future belief
|
`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
|
Heimdall is the planned shared auth authority for GameCult-hosted experiments.
|
||||||
GameCult-hosted experiments.
|
|
||||||
|
|
||||||
The point is painfully simple:
|
The point is painfully simple:
|
||||||
|
|
||||||
- stop rebuilding Discord/Patreon login glue in every app
|
- 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"
|
- keep app-domain data separate so "shared auth" does not become "shared swamp"
|
||||||
|
|
||||||
## Current Shape
|
## 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:
|
Canonical docs:
|
||||||
|
|
||||||
|
|
@ -25,7 +28,9 @@ Canonical docs:
|
||||||
## Working Loop
|
## Working Loop
|
||||||
|
|
||||||
1. Rehydrate from canonical state.
|
1. Rehydrate from canonical state.
|
||||||
2. Keep the shared backbone separate from app-domain data.
|
2. Keep identity authority central and app-domain data local.
|
||||||
3. Prefer embedded shared-package mode before inventing a central auth service.
|
3. Prefer a Yggdrasil-hosted shared service over duplicated per-app provider
|
||||||
4. Move only one organ at a time.
|
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.
|
5. Commit finished passes before the notes start breeding.
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
|
|
||||||
## What this file is
|
## 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.
|
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
|
- job ownership resolves from the local session/account, not from Discord or
|
||||||
Patreon ids
|
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
|
## Config mapping
|
||||||
|
|
||||||
- keep runtime/queue/limit env vars under `REPIXELIZER_*`
|
- 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
|
- keep per-app role/tier/capability bindings small and app-scoped
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## What this file is
|
## 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
|
`docs/architecture.md` without turning streamer-audience data into shared auth
|
||||||
sludge.
|
sludge.
|
||||||
|
|
||||||
|
|
@ -27,6 +27,13 @@ That is already compatible with the shared backbone idea.
|
||||||
- creator/operator capability evaluation
|
- creator/operator capability evaluation
|
||||||
- grant/invite primitives where they fit
|
- 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
|
## What StreamPixels should keep separate
|
||||||
|
|
||||||
- viewer audience profiles
|
- viewer audience profiles
|
||||||
|
|
@ -37,7 +44,7 @@ That is already compatible with the shared backbone idea.
|
||||||
|
|
||||||
In plain language:
|
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
|
- StreamPixels audience data stays StreamPixels data
|
||||||
|
|
||||||
## Capability direction
|
## Capability direction
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
# GameCult Hosted Access Architecture
|
# Heimdall Architecture
|
||||||
|
|
||||||
## What this file is
|
## What this file is
|
||||||
|
|
||||||
This file is the shared architecture note for GameCult-hosted experiments that
|
This file is the shared architecture note for Heimdall, the planned auth
|
||||||
need the same basic promise:
|
authority service for GameCult-hosted experiments.
|
||||||
|
|
||||||
|
Its basic promise is:
|
||||||
|
|
||||||
- let people sign in with Discord and/or Patreon
|
- let people sign in with Discord and/or Patreon
|
||||||
- decide access from GameCult/community entitlements
|
- decide access from GameCult/community entitlements and local grants
|
||||||
- keep app runtimes tied to local sessions and local accounts, not provider ids
|
- issue signed local claims that hosted apps can verify without trusting raw
|
||||||
|
provider tokens
|
||||||
|
|
||||||
It is not a map of landed auth code yet.
|
It is not a map of landed auth code yet.
|
||||||
|
|
||||||
|
|
@ -19,30 +22,32 @@ If every experiment rebuilds:
|
||||||
- identity linking
|
- identity linking
|
||||||
- entitlement refresh
|
- entitlement refresh
|
||||||
- session cookies
|
- session cookies
|
||||||
- queue/job ownership
|
- grant handling
|
||||||
- "Discord role OR Patreon tier" logic
|
- "Discord role OR Patreon tier" logic
|
||||||
|
|
||||||
then we get five near-identical auth pits and deserve what happens next.
|
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.
|
bind its own policy and runtime seams onto it.
|
||||||
|
|
||||||
## Design goals
|
## Design goals
|
||||||
|
|
||||||
- one reusable local account/session model across hosted experiments
|
- 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
|
- 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
|
- 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
|
- fit the current GameCult topology: Heimdall on Yggdrasil behind nginx, with
|
||||||
service
|
hosted apps either on the same host or verifying over normal HTTPS/JWKS
|
||||||
|
|
||||||
## Non-goals
|
## Non-goals
|
||||||
|
|
||||||
- becoming a general public identity provider
|
- becoming a general public identity provider
|
||||||
- building multi-tenant SaaS auth machinery
|
- building multi-tenant SaaS auth machinery
|
||||||
- inventing global billing analytics because we got bored
|
- 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
|
## Core concepts
|
||||||
|
|
||||||
|
|
@ -61,6 +66,8 @@ bind its own policy and runtime seams onto it.
|
||||||
policy rules
|
policy rules
|
||||||
- `Session`
|
- `Session`
|
||||||
signed local app/browser session; the browser gets this, not provider tokens
|
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`
|
- `AuditEvent`
|
||||||
durable record for login, link, unlink, refresh, denial, and admin overrides
|
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
|
```mermaid
|
||||||
flowchart TD
|
flowchart TD
|
||||||
A["Browser / app web UI"] --> B["Hosted experiment web surface"]
|
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 --> D["Discord OAuth + guild role checks"]
|
||||||
C --> E["Patreon OAuth + campaign/tier checks"]
|
C --> E["Patreon OAuth + campaign/tier checks"]
|
||||||
C --> F["Local account/session/grant store"]
|
C --> F["Local auth/control-plane store"]
|
||||||
C --> G["Signed access claims"]
|
C --> G["Signed sessions / claims"]
|
||||||
G --> H["Hosted experiment runtime"]
|
B -->|verify claims locally| H["Hosted experiment runtime"]
|
||||||
```
|
```
|
||||||
|
|
||||||
The important split:
|
The important split:
|
||||||
|
|
||||||
- the access layer owns provider OAuth, identity linking, entitlement refresh,
|
- Heimdall owns provider OAuth, identity linking, entitlement refresh, signed
|
||||||
and signed local sessions
|
sessions, claims, grants, and audit surfaces
|
||||||
- the host experiment owns its workload, queue, editor, render path, or other
|
- the host experiment owns its workload, queue, editor, render path, viewer
|
||||||
product-specific machinery
|
profile model, creator model, or other product-specific machinery
|
||||||
|
|
||||||
Do not smear provider logic directly through every host runtime.
|
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:
|
The reusable thing is:
|
||||||
|
|
||||||
- provider OAuth plumbing
|
- provider OAuth plumbing
|
||||||
- signed local session mechanics
|
- signed session and claim mechanics
|
||||||
- linked-identity primitives
|
- linked-identity primitives
|
||||||
- entitlement refresh
|
- entitlement refresh
|
||||||
- capability evaluation
|
- capability evaluation
|
||||||
|
- local grant and audit surfaces
|
||||||
|
|
||||||
The non-reusable-by-default thing is:
|
The non-reusable-by-default thing is:
|
||||||
|
|
||||||
|
|
@ -109,12 +117,12 @@ The non-reusable-by-default thing is:
|
||||||
|
|
||||||
Recommended default:
|
Recommended default:
|
||||||
|
|
||||||
- shared auth/access **code**
|
- shared auth/access **authority**
|
||||||
- separate app databases or at least separate app-owned schemas/tables for
|
- separate app databases or at least separate app-owned schemas/tables for
|
||||||
domain data
|
domain data
|
||||||
- if a shared access store exists at all, keep it limited to auth/control-plane
|
- if a shared Heimdall store exists at all, keep it limited to auth/control
|
||||||
truth such as identities, grants, entitlement snapshots, sessions, and audit
|
plane truth such as identities, grants, entitlement snapshots, sessions,
|
||||||
events
|
claims metadata, and audit events
|
||||||
|
|
||||||
Do not let "same auth backbone" quietly mutate into "same user-data swamp."
|
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
|
- one provider identity belongs to one local account only
|
||||||
- provider tokens stay server-side only
|
- provider tokens stay server-side only
|
||||||
- session cookies represent local sessions, not provider trust directly
|
- session cookies represent local sessions, not provider trust directly
|
||||||
- app-specific capability checks read local claims, not Discord or Patreon on
|
- app-specific capability checks read signed local claims, not Discord or
|
||||||
every route
|
Patreon on every route
|
||||||
|
|
||||||
## Provider adapters
|
## Provider adapters
|
||||||
|
|
||||||
|
|
@ -203,7 +211,7 @@ Shared provider/gate adapters should be boring and reusable:
|
||||||
|
|
||||||
## Policy model
|
## 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.
|
evaluate capabilities against an app profile.
|
||||||
|
|
||||||
Suggested app-profile shape:
|
Suggested app-profile shape:
|
||||||
|
|
@ -227,18 +235,41 @@ queue_submit = app_access
|
||||||
admin_access = grant.operator || grant.admin_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
|
## Auth and linking flow
|
||||||
|
|
||||||
The generic flow should be the same across hosted experiments:
|
The generic flow should be the same across hosted experiments:
|
||||||
|
|
||||||
1. user starts OAuth from the host app UI
|
1. user starts OAuth from the host app UI
|
||||||
2. shared access layer signs OAuth `state`
|
2. host app redirects to Heimdall or calls Heimdall to begin the flow
|
||||||
3. provider callback resolves the provider identity
|
3. Heimdall signs OAuth `state`
|
||||||
4. shared layer finds or creates the local account
|
4. provider callback resolves the provider identity through Heimdall
|
||||||
5. if the user was already signed in locally, the identity may link onto the
|
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
|
existing account instead of creating a new one
|
||||||
6. shared layer refreshes entitlements and capability claims
|
7. Heimdall refreshes entitlements and capability claims
|
||||||
7. shared layer issues or updates the local session cookie
|
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.
|
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 an `app_slug`
|
||||||
- define the app capability profile
|
- define the app capability profile
|
||||||
|
- verify Heimdall-issued claims locally
|
||||||
- mark which routes are public, authenticated, or capability-gated
|
- mark which routes are public, authenticated, or capability-gated
|
||||||
- attach `account_id`, `session_id`, and `access_revision` to owned resources
|
- attach `account_id`, `session_id`, and `access_revision` to owned resources
|
||||||
such as jobs, drafts, uploads, or queue entries
|
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
|
- active-job cancellation policy is app-specific, but the recommended first cut
|
||||||
is still "let the active job finish, block new work"
|
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
|
## Deployment modes
|
||||||
|
|
||||||
### Mode 1: Embedded shared package
|
### Mode 1: Same-host shared access service
|
||||||
|
|
||||||
Recommended first cut.
|
Recommended first cut.
|
||||||
|
|
||||||
- one shared `gamecult_access` package/module
|
- one Heimdall service on Yggdrasil
|
||||||
- each hosted experiment embeds it
|
- each hosted experiment keeps its own runtime and route integration
|
||||||
- each app keeps its own runtime and route integration
|
|
||||||
- sessions may still be app-local
|
|
||||||
- each app keeps its own domain-data store
|
- each app keeps its own domain-data store
|
||||||
|
- apps verify signed claims locally after issuance
|
||||||
|
|
||||||
Why this is the right first cut:
|
Why this is the right first cut:
|
||||||
|
|
||||||
- reusable without forcing a separate always-on dependency
|
- fits the current host topology
|
||||||
- small blast radius
|
- avoids cross-runtime shared-library nonsense
|
||||||
- easy to evolve while only one or two apps use it
|
- 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
|
Use this when multiple app stacks across hosts still want one shared auth
|
||||||
session issuer, but full centralization still feels premature.
|
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
|
- shared cross-app sessions
|
||||||
- central grant administration
|
- central grant administration
|
||||||
- one callback origin for all providers
|
- 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.
|
Do not build this first because it sounds important.
|
||||||
|
|
||||||
Even in this mode, the dedicated service should own auth/control-plane data, not
|
Even in this mode, Heimdall should own auth/control-plane data, not absorb
|
||||||
absorb every app's audience or product data.
|
every app's audience or product data.
|
||||||
|
|
||||||
## Configuration split
|
## Configuration split
|
||||||
|
|
||||||
Suggested generic env surface:
|
Suggested generic env surface:
|
||||||
|
|
||||||
- `GC_ACCESS_ENABLED=1`
|
- `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_SESSION_SECRET=...`
|
||||||
- `GC_ACCESS_BASE_URL=https://access.gamecult.org` or host-app local equivalent
|
|
||||||
- `GC_ACCESS_DISCORD_CLIENT_ID=...`
|
- `GC_ACCESS_DISCORD_CLIENT_ID=...`
|
||||||
- `GC_ACCESS_DISCORD_CLIENT_SECRET=...`
|
- `GC_ACCESS_DISCORD_CLIENT_SECRET=...`
|
||||||
- `GC_ACCESS_DISCORD_BOT_TOKEN=...`
|
- `GC_ACCESS_DISCORD_BOT_TOKEN=...`
|
||||||
|
|
@ -321,10 +367,12 @@ env namespaces.
|
||||||
|
|
||||||
The correct reusable seam is:
|
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
|
- thin per-app access profiles and runtime bindings
|
||||||
|
|
||||||
The incorrect reusable seam is:
|
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 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
|
## 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
|
It is not the canonical shared architecture note. That lives in
|
||||||
`docs/architecture.md`.
|
`docs/architecture.md`.
|
||||||
|
|
@ -16,86 +16,112 @@ There is no landed auth machine here yet.
|
||||||
|
|
||||||
What exists right now is:
|
What exists right now is:
|
||||||
|
|
||||||
- the extracted shared architecture note
|
- the shared architecture note
|
||||||
- app-binding notes for Repixelizer and StreamPixels
|
- app-binding notes for Repixelizer and StreamPixels
|
||||||
- explicit state/doctrine scaffolding so the plan does not immediately turn into
|
- explicit state/doctrine scaffolding so the plan does not immediately turn
|
||||||
soup
|
into soup
|
||||||
|
|
||||||
## Primary objective
|
## 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
|
- own provider OAuth and identity linking
|
||||||
- issue signed local sessions
|
- issue signed sessions and app-facing claims
|
||||||
- refresh Discord and Patreon entitlements
|
- refresh Discord and Patreon entitlements
|
||||||
- evaluate per-app capability rules
|
- 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
|
## Current priorities
|
||||||
|
|
||||||
### 1. Lock the shared boundary
|
### 1. Lock the service boundary
|
||||||
|
|
||||||
Goals:
|
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-domain data in app-owned stores
|
||||||
- keep app profiles thin and explicit
|
- keep app profiles thin and explicit
|
||||||
|
|
||||||
### 2. Prefer embedded-package mode first
|
### 2. Define the contract before the service grows teeth
|
||||||
|
|
||||||
Goals:
|
Goals:
|
||||||
|
|
||||||
- define the reusable package/module seam
|
- define signed claim/session shapes
|
||||||
- avoid a dedicated shared service until a second real app proves the need
|
- define OAuth start/callback/link surfaces
|
||||||
- keep initial blast radius small
|
- 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:
|
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`
|
- attach queue ownership to `account_id` and `session_id`
|
||||||
- preserve the existing hosted GUI and queue runtime
|
- 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:
|
Goals:
|
||||||
|
|
||||||
- let StreamPixels reuse auth/control-plane machinery
|
- move provider/linking authority toward Heimdall
|
||||||
- keep viewer profiles, creator memberships, connector creds, and audience data
|
- keep viewer profiles, creator memberships, connector creds, and audience data
|
||||||
in StreamPixels
|
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
|
## Proposed phases
|
||||||
|
|
||||||
### Phase 1: Shared package scaffold
|
### Phase 1: Contract and schema scaffold
|
||||||
|
|
||||||
- create package/module boundaries for:
|
- define:
|
||||||
- provider adapters
|
- signed claim/session payloads
|
||||||
- signed session issuance/verification
|
- JWKS or equivalent verification surface
|
||||||
- linked identity persistence interfaces
|
- OAuth `state` and callback contracts
|
||||||
- entitlement refresh/evaluation
|
- grant / entitlement / audit record shapes
|
||||||
- capability rule evaluation
|
- 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 `POST /api/jobs`
|
||||||
- gate per-job read/cancel/event routes by local session ownership
|
- 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
|
- replace provider/linking authority paths with Heimdall calls where they truly
|
||||||
- explicitly reject audience-data centralization
|
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
|
- define nginx/systemd/env/runbook surfaces for Heimdall on Yggdrasil
|
||||||
should stay embedded or move into a shared service/store
|
- keep callers and state stores localhost-first where practical
|
||||||
|
|
||||||
## Guardrails
|
## Guardrails
|
||||||
|
|
||||||
- do not let auth reuse become data mixing
|
- 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 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
|
- keep one clear hypothesis per pass
|
||||||
- commit completed work before the planning notes start breeding
|
- commit completed work before the planning notes start breeding
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
# Fresh Workspace Handoff
|
# 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
|
It is intentionally short. Historical proof belongs in git history and the
|
||||||
distilled `state/evidence.jsonl` ledger; the shared architecture belongs in
|
distilled `state/evidence.jsonl` ledger; the shared architecture belongs in
|
||||||
|
|
@ -13,7 +13,7 @@ From the repo root:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
# This repo has no dedicated venv yet. Use any working Python 3.11+.
|
# 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 '.\state\map.yaml'
|
||||||
Get-Content '.\notes\fresh-workspace-handoff.md'
|
Get-Content '.\notes\fresh-workspace-handoff.md'
|
||||||
Get-Content '.\docs\architecture.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
|
## 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`
|
- the canonical shared architecture lives in `docs/architecture.md`
|
||||||
- app profiles live under `docs/app-profiles/`
|
- app profiles live under `docs/app-profiles/`
|
||||||
- the key boundary is explicit:
|
- 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
|
- app-domain data stays app-owned by default
|
||||||
- Repixelizer is the first binding target
|
- host apps verify signed claims locally for routine auth instead of calling
|
||||||
- StreamPixels is the boundary case that proves backbone reuse must not imply
|
Heimdall on every request
|
||||||
audience-data centralization
|
- Repixelizer is the first auth-blank binding target
|
||||||
- the recommended first implementation mode is an embedded shared package, not a
|
- StreamPixels is the migration target with useful existing auth seams that
|
||||||
dedicated auth service
|
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
|
## Critical Doctrine
|
||||||
|
|
||||||
- Persistent state is the agent's mind.
|
- Persistent state is the agent's mind.
|
||||||
- Cut stale architecture notes as ruthlessly as stale code.
|
- Cut stale architecture notes as ruthlessly as stale code.
|
||||||
- Do not mistake "shared auth" for "shared database."
|
- 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.
|
- If compaction hits before boundary findings are persisted, that work is gone.
|
||||||
Re-gather it instead of pretending continuity happened.
|
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.
|
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
|
If the user asks to continue, the current next move is to define the first
|
||||||
`gamecult_access` package seam:
|
Heimdall service contract and skeleton:
|
||||||
|
|
||||||
- storage interfaces
|
- OAuth start/callback surfaces
|
||||||
- provider adapter interfaces
|
- signed session / claim shape
|
||||||
- signed-session claims
|
- claim verification contract for app backends
|
||||||
- capability rule evaluation boundaries
|
- entitlement/grant persistence boundaries
|
||||||
|
- Repixelizer and StreamPixels integration seams
|
||||||
|
|
||||||
without central-service theatrics and without smearing app-domain data into the
|
without dragging app-domain data into the shared layer and without forcing
|
||||||
shared layer.
|
per-request auth round-trips.
|
||||||
|
|
||||||
## Immediate Re-entry Instruction
|
## Immediate Re-entry Instruction
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
"branches": [
|
"branches": [
|
||||||
{
|
{
|
||||||
"id": "main",
|
"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",
|
"status": "active",
|
||||||
"artifacts": [
|
"artifacts": [
|
||||||
"state/map.yaml",
|
"state/map.yaml",
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
"docs/architecture.md",
|
"docs/architecture.md",
|
||||||
"docs/implementation-plan.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 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":"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-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:
|
project:
|
||||||
name: GameCult Access
|
name: Heimdall
|
||||||
status: active
|
status: active
|
||||||
phase: architecture_extraction
|
phase: service_topology_alignment
|
||||||
premise: >
|
premise: >
|
||||||
Build one reusable GameCult-hosted access backbone for OAuth, linked
|
Build Heimdall as one reusable GameCult auth authority service for OAuth,
|
||||||
identities, local sessions, and capability checks without mixing in
|
linked identities, local sessions, grants, and capability claims without
|
||||||
app-domain audience or product data.
|
mixing in app-domain audience or product data.
|
||||||
|
|
||||||
objective:
|
objective:
|
||||||
primary: >
|
primary: >
|
||||||
Create a reusable shared access layer that Repixelizer, StreamPixels, and
|
Create a shared auth authority service that Repixelizer, StreamPixels, and
|
||||||
future GameCult-hosted experiments can bind onto.
|
future GameCult-hosted experiments can consume.
|
||||||
current: >
|
current: >
|
||||||
Lock the shared architecture, extraction path, and app-boundary rules before
|
Lock the Heimdall service boundary, Yggdrasil deployment shape, and app
|
||||||
implementation starts breeding myths.
|
integration rules before implementation starts breeding myths.
|
||||||
|
|
||||||
constraints:
|
constraints:
|
||||||
- Shared auth/access code is reusable; app-domain data is separate by default.
|
- Heimdall owns auth/control-plane authority; app-domain data stays app-owned by default.
|
||||||
- Prefer an embedded shared package before a dedicated auth service.
|
- 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.
|
- 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.
|
- 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.
|
- The map is canonical and slow-changing.
|
||||||
- Scratch is temporary and disposable.
|
- Scratch is temporary and disposable.
|
||||||
- Evidence is durable and distilled; routine activity logs belong in git history and commit messages.
|
- 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.
|
- 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.
|
- Host apps verify signed claims locally and keep resource ownership checks local.
|
||||||
- StreamPixels can reuse control-plane auth mechanics without centralizing streamer-audience data.
|
- StreamPixels can reuse shared auth authority without centralizing streamer-audience data.
|
||||||
- Persistent state cruft is the same failure class as architecture cruft.
|
- Persistent state cruft is the same failure class as architecture cruft.
|
||||||
|
|
||||||
data_flow:
|
data_flow:
|
||||||
|
|
@ -37,45 +38,48 @@ data_flow:
|
||||||
- shared architecture note
|
- shared architecture note
|
||||||
- app profile bindings
|
- app profile bindings
|
||||||
- current app/runtime seams in Repixelizer and StreamPixels
|
- current app/runtime seams in Repixelizer and StreamPixels
|
||||||
|
- Yggdrasil host topology and nginx/service shape
|
||||||
process:
|
process:
|
||||||
- rehydrate from canonical state
|
- rehydrate from canonical state
|
||||||
- choose one bounded architecture or implementation slice
|
- 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
|
- record only belief-changing evidence
|
||||||
- commit or backtrack
|
- commit or backtrack
|
||||||
cut:
|
cut:
|
||||||
- reject shared-data creep
|
- reject shared-data creep
|
||||||
- reject duplicated auth blobs per app
|
- 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:
|
active_subgoals:
|
||||||
- Keep the shared architecture explicit and separate from app profiles.
|
- Keep the shared architecture explicit and separate from app profiles.
|
||||||
- Preserve the default boundary that app-domain data remains app-owned.
|
- Preserve the default boundary that app-domain data remains app-owned.
|
||||||
- Make Repixelizer the first binding and StreamPixels the boundary stress test.
|
- Align the plan to Heimdall as a Yggdrasil-hosted authority service.
|
||||||
- Define the extraction seam for an embedded `gamecult_access` package.
|
- Define the claim-verification contract that Repixelizer and StreamPixels will consume locally.
|
||||||
|
|
||||||
accepted_design:
|
accepted_design:
|
||||||
- `docs/architecture.md` is the canonical shared architecture note.
|
- `docs/architecture.md` is the canonical shared architecture note.
|
||||||
- `docs/app-profiles/*.md` are thin app bindings, not duplicate brains.
|
- `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.
|
- 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.
|
- Host apps verify signed claims locally for routine route authorization.
|
||||||
- Repixelizer is the first hosted-app binding.
|
- A shared Heimdall store, if it exists, should hold auth/control-plane truth only.
|
||||||
- StreamPixels is the boundary case that proves backbone reuse must not imply audience-data centralization.
|
- 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:
|
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.
|
- Repixelizer and StreamPixels app-profile notes created.
|
||||||
- Repo-local state, handoff, and compaction scaffolding created.
|
- Repo-local state, handoff, and compaction scaffolding created.
|
||||||
|
|
||||||
rejected_paths:
|
rejected_paths:
|
||||||
- name: per_app_auth_copy_paste
|
- name: per_app_auth_copy_paste
|
||||||
reason: duplicates the same provider/session sludge across every experiment
|
reason: duplicates the same provider/session sludge across every experiment
|
||||||
- name: central_auth_service_first
|
- name: per_request_auth_round_trip
|
||||||
reason: adds deployment weight before two real bindings justify it
|
reason: turns shared auth into a noisy bottleneck instead of local claim verification
|
||||||
- name: shared_user_data_swamp
|
- name: shared_user_data_swamp
|
||||||
reason: reusing auth mechanics does not justify centralizing app-domain audience or product data
|
reason: reusing auth mechanics does not justify centralizing app-domain audience or product data
|
||||||
|
|
||||||
current_status:
|
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.
|
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 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.
|
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:
|
def add_agents_checks(findings: list[Finding]) -> None:
|
||||||
text = read_text(AGENTS_PATH)
|
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"))
|
findings.append(Finding("ok", "AGENTS.md tells agents to use the compaction helper"))
|
||||||
else:
|
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():
|
if "prepare for imminent compaction" in text.lower():
|
||||||
findings.append(Finding("ok", "AGENTS.md names the imminent-compaction trigger"))
|
findings.append(Finding("ok", "AGENTS.md names the imminent-compaction trigger"))
|
||||||
else:
|
else:
|
||||||
|
|
@ -232,7 +232,7 @@ def render_report(findings: list[Finding], status: str, log: str, evidence: list
|
||||||
}
|
}
|
||||||
|
|
||||||
lines = [
|
lines = [
|
||||||
"GameCult Access pre-compaction persistence check",
|
"Heimdall pre-compaction persistence check",
|
||||||
f"Workspace: {ROOT}",
|
f"Workspace: {ROOT}",
|
||||||
f"Findings: {counts['ok']} ok, {counts['warn']} warn, {counts['error']} error",
|
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:
|
def build_parser() -> argparse.ArgumentParser:
|
||||||
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(
|
parser.add_argument(
|
||||||
"--strict",
|
"--strict",
|
||||||
|
|
@ -131,7 +131,7 @@ def cmd_close_branch(args: argparse.Namespace) -> int:
|
||||||
|
|
||||||
def build_parser() -> argparse.ArgumentParser:
|
def build_parser() -> argparse.ArgumentParser:
|
||||||
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)
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
||||||
|
|
||||||
Loading…
Reference in a new issue