No description
- TypeScript 93.7%
- C++ 4.4%
- Python 1.7%
- Shell 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Soul pass 6, F1-F4, F7 and F8. Keys (F1, F2): a key is met only by a unique index that is valid and ready, so the invalid index a failed CREATE UNIQUE INDEX CONCURRENTLY leaves behind (every sign-in failing with 42P10, or one consume taking two rows) is refused; and whose key columns compare under deterministic collations, so a case-insensitive collation cannot merge two providers' user ids into one row and move an identity between accounts. Predicates (F3, F7): the declared predicates are printed by Postgres itself, by indexing a temporary copy of each keyed table's declared columns in one transaction that is rolled back, and compared with the index's printed predicate as sets of top-level AND-ed conditions, split by nesting and never inside quoted text. Case and structure are kept, so ((a OR b) AND c) and (a OR (b AND c)) differ, as do 'Pending' and 'pending', and an extra condition narrows the predicate and is refused. Conditions in another order match. F4: lookup-only indexes are no longer required (REQUIRED_RELATIONS is now REQUIRED_TABLES; unique indexes are checked by shape in REQUIRED_KEYS), so a renamed attempt index or a dropped lookup index starts. Privileges are checked per column with has_column_privilege on every column the schema declares, which a table grant satisfies; a role granted each needed privilege column by column starts and runs every store operation, and one holding INSERT on only one audit column is refused. On (c): a unique index treats NULL keys as distinct unless it is NULLS NOT DISTINCT, so rows with a NULL attempt_id never collide and `attempt_id IS NOT NULL` is redundant; that condition may be absent from the attempt index's predicate, except on a NULLS NOT DISTINCT index (refused). A key declared without a predicate must have none, since ON CONFLICT cannot infer a partial arbiter. F8: a column the schema leaves nullable must be nullable; NOT NULL added to attempt_id is refused. CHECK constraints, triggers and row-level security stay out of scope, as the schema comment says. Every change is a live fixture: invalid identity key, invalid attempt index over duplicate attempts, case-insensitive key, narrowed and OR-widened predicates, NULLS NOT DISTINCT, NOT NULL added, partial column grants are refused; reordered and unredundant predicates, a renamed attempt index, dropped lookup indexes and column-by-column grants start. The fake-pool code-mapping tests are replaced by unit tests of the predicate comparison. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
| .voidbot/state | ||
| assets | ||
| deployment/idunn | ||
| docs | ||
| notes | ||
| plugins/gamecult.heimdall.access | ||
| schemas | ||
| src | ||
| state | ||
| tests | ||
| tools | ||
| vendor | ||
| .env.example | ||
| .gitignore | ||
| .gitmodules | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| LICENSE | ||
| package.json | ||
| pnpm-lock.yaml | ||
| README.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
Heimdall
Heimdall is the shared auth authority taking shape for GameCult-hosted experiments.
The point is painfully simple:
- stop rebuilding Discord/Patreon login glue in every app
- 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 now has a stateful first slice instead of a decorative skeleton.
Landed right now:
- a TypeScript/Fastify service scaffold under
src/ - Ed25519 JWT signing plus
/.well-known/jwks.json - file-backed signing-key loading/bootstrap so restart does not vaporize the service identity when you configure a real key path
- signed OAuth start-state scaffolding for Discord, Patreon, GitHub, Twitch, YouTube, and Spotify
- durable auth/control-plane storage with in-memory and Postgres backends
- account, linked-identity, session, persistent OAuth attempt, grant, entitlement snapshot, and audit persistence
- a real Discord OAuth callback path for the first Repixelizer access flow
- direct backend callback handoff for same-host or Yggdrasil-reachable app backends
- AES-256-GCM sealing for managed provider tokens at rest
- a one-time browser completion exchange kept as fallback for opener
postMessagehandoff - app profile surfaces for Repixelizer, StreamPixels, and Spotiverse
- signed claim issuance for app-local verification experiments
- a local verifier helper in
src/verifier.tsso app backends can validate Heimdall JWTs without phoning home on every guarded request - a read-only Odin/Eve provider advertisement export that names redacted CultCache witness document shapes and paths without touching live secrets
- a daemon-owned CultCache boundary witness at
GC_ACCESS_CULTCACHE_PATHcontaining the Heimdall provider advertisement, command boundary, transport profile, and Idunn daemon-health summary - optional Idunn daemon-health publication over
cultnet.transport.rudp.v0whenGC_ACCESS_IDUNN_RUDP_HEALTHis configured - the
gamecult.heimdall.accessEve plugin with executabledescribe,validate, andprojectoperations plus a browser reference adapter - a loopback-only CultNet operation boundary on port
4101for authenticatedheimdall.auth.beginandheimdall.auth.complete; app payloads and completion claims are AES-256-GCM wrapped and HMAC-bound to expiry, nonce, operation, runtime, and idempotency identity - redacted provider, command-boundary, plugin, and transport records published through Odin under unique typed catalog keys; secret-bearing auth state never enters the catalog
- the deployed Ghostlight Eve host consumes that discovered boundary and keeps routine app-session verification local
Remaining work:
- the two-account and eight-account Ghostlight human canaries
- refresh/revocation flows and admin surfaces
- broader admin and inspection projections beyond the redacted access boundary
Canonical docs:
docs/architecture.mddocs/implementation-plan.mddocs/service-contract.mddocs/verse-service-contract.mddocs/app-profiles/repixelizer.mddocs/app-profiles/streampixels.mddocs/app-profiles/spotiverse.mdstate/map.yamlnotes/fresh-workspace-handoff.md
Dev Commands
pnpm install
pnpm dev
pnpm test
pnpm build
pnpm export:provider-advertisement
Working Loop
- Rehydrate from canonical state.
- Keep identity authority central and app-domain data local.
- Prefer a Yggdrasil-hosted shared service over duplicated per-app provider stacks.
- Let app backends verify signed claims locally instead of begging Heimdall on every guarded route.
- Commit finished passes before the notes start breeding.