Add read-only Heimdall Eve witness advertisement

This commit is contained in:
Meta Vens 2026-06-03 03:21:31 +01:00
commit 5a06957a0d
8 changed files with 391 additions and 3 deletions

View file

@ -23,7 +23,7 @@ Landed right now:
- 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,
and YouTube
YouTube, and Spotify
- durable auth/control-plane storage with in-memory and Postgres backends
- account, linked-identity, session, grant, entitlement snapshot, and audit
persistence
@ -33,15 +33,18 @@ Landed right now:
- AES-256-GCM sealing for managed provider tokens at rest
- a one-time browser completion exchange kept as fallback for opener
`postMessage` handoff
- app profile surfaces for Repixelizer and StreamPixels
- app profile surfaces for Repixelizer, StreamPixels, and Spotiverse
- signed claim issuance for app-local verification experiments
- a local verifier helper in `src/verifier.ts` so 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
Still not landed:
- end-to-end app integrations in consumer repos
- refresh/revocation flows and admin surfaces
- runtime CultCache `.cc` witness writing or CultMesh publication
Canonical docs:
@ -51,6 +54,7 @@ Canonical docs:
- `docs/verse-service-contract.md`
- `docs/app-profiles/repixelizer.md`
- `docs/app-profiles/streampixels.md`
- `docs/app-profiles/spotiverse.md`
- `state/map.yaml`
- `notes/fresh-workspace-handoff.md`
@ -61,6 +65,7 @@ pnpm install
pnpm dev
pnpm test
pnpm build
pnpm export:provider-advertisement
```
## Working Loop

View file

@ -85,6 +85,7 @@ Current app slugs:
- `repixelizer`
- `streampixels`
- `spotiverse`
Profile fields:
@ -597,6 +598,42 @@ The following are not landed yet:
- revocation flows
- admin/grant management surfaces
- GitHub callback runtime
- runtime CultCache `.cc` witness writing or CultMesh publication
## Odin/Eve provider advertisement
Purpose:
- advertise Heimdall as a read-only Eve provider surface for Odin discovery
- name the redacted CultCache witness document schemas and future `.cc` paths
- avoid touching provider secrets, token custody, store migrations, or runtime
mutation paths
Command:
```powershell
pnpm export:provider-advertisement
```
Current output shape:
- `schemaVersion`: `gamecult.eve.provider_advertisement.v1`
- `providerId`: `heimdall`
- `status`: `read_only_witness_planned`
- provider catalog entries for Discord, Patreon, GitHub, Twitch, YouTube, and
Spotify
- app profile projections for Repixelizer, StreamPixels, and Spotiverse
- redacted witness descriptors for accounts, linked identities, grants,
sessions, entitlement snapshots, auth completions, audit events, app profiles,
and managed credential projections
Important behavior:
- this is a projection/export command, not a writer
- advertised witness paths are future CultCache targets, not proof that a live
`.cc` exporter is already running
- exported descriptors forbid raw provider tokens, encrypted token blobs,
completion codes, callback secrets, and app-domain state
## Next implementation move

View file

@ -99,3 +99,44 @@ refreshing an entitlement snapshot. Heimdall accepts or denies those actions.
The invariant: Heimdall owns shared auth truth, not app-domain truth. CultCache
and CultMesh make Heimdall inspectable; they do not leak secrets or move product
state into the auth authority.
## Read-Only Witness First Cut
The current repo cut defines the read-only witness/ad surface without wiring a
runtime writer or migrating live state.
Authority map:
- Owner: Heimdall remains the only owner of auth/control-plane mutation.
- Inputs: static provider catalog, static app profiles, and the redacted witness
descriptor table in `src/verse-witness.ts`.
- Outputs: `gamecult.eve.provider_advertisement.v1` JSON printed by
`pnpm export:provider-advertisement`.
- Derived state: advertised CultCache paths and schema IDs are witness/export
targets. They do not prove that a live `.cc` writer exists yet.
- Forbidden writers: Odin probes, Eve renderers, dashboards, and this export
command cannot mutate accounts, linked identities, grants, sessions, tokens,
completions, entitlements, audit events, or app profiles.
- Shared paths: future CultCache export, CultMesh publication, and Eve lowering
should reuse the same document IDs and redaction doctrine.
- Deletion line: if a future writer needs app-domain fields or provider tokens
to make the surface useful, the design is wrong; keep those fields out or move
the concern back to the owning app/service.
The advertised witness descriptors currently name these redacted `.cc` targets:
- `heimdall.account.v0` at `cultcache/heimdall/accounts/{accountId}.cc`
- `heimdall.linked_identity.v0` at
`cultcache/heimdall/linked-identities/{provider}/{providerUserId}.cc`
- `heimdall.capability_grant.v0` at
`cultcache/heimdall/grants/{grantId}.cc`
- `heimdall.session.v0` at `cultcache/heimdall/sessions/{sessionId}.cc`
- `heimdall.entitlement_snapshot.v0` at
`cultcache/heimdall/entitlements/{accountId}/{provider}/{scope}.cc`
- `heimdall.auth_completion.v0` at
`cultcache/heimdall/auth-completions/{completionCodeHash}.cc`
- `heimdall.audit_event.v0` at `cultcache/heimdall/audit/{eventId}.cc`
- `heimdall.app_profile.v0` at
`cultcache/heimdall/app-profiles/{appSlug}.cc`
- `heimdall.managed_credential_projection.v0` at
`cultcache/heimdall/managed-credentials/{appSlug}/{accountId}/{provider}.cc`

View file

@ -11,7 +11,8 @@
"dev": "tsx watch src/index.ts",
"start": "node dist/index.js",
"test": "vitest run --config vitest.config.ts",
"typecheck": "tsc -p tsconfig.json --noEmit"
"typecheck": "tsc -p tsconfig.json --noEmit",
"export:provider-advertisement": "tsx src/export-provider-advertisement.ts --pretty"
},
"dependencies": {
"@fastify/cors": "^11.2.0",

View file

@ -0,0 +1,23 @@
import { buildHeimdallProviderAdvertisement } from "./verse-witness.js";
function readUpdatedAt(args: string[]): string {
const index = args.indexOf("--updated-at");
const value = index >= 0 ? args[index + 1] : undefined;
if (!value) {
return new Date().toISOString();
}
const parsed = new Date(value);
if (Number.isNaN(parsed.getTime())) {
throw new Error(`Invalid --updated-at value: ${value}`);
}
return parsed.toISOString();
}
const pretty = process.argv.includes("--pretty");
const updatedAt = readUpdatedAt(process.argv.slice(2));
const advertisement = buildHeimdallProviderAdvertisement({ updatedAt });
process.stdout.write(`${JSON.stringify(advertisement, null, pretty ? 2 : 0)}\n`);

222
src/verse-witness.ts Normal file
View file

@ -0,0 +1,222 @@
import { appProfiles, type CapabilityDefinition } from "./app-profiles.js";
import { appSlugs, providers, type AppSlug, type Provider } from "./contracts.js";
import { providerCatalog, type ProviderRole } from "./providers.js";
export const heimdallWitnessSchemaIds = [
"heimdall.account.v0",
"heimdall.linked_identity.v0",
"heimdall.capability_grant.v0",
"heimdall.session.v0",
"heimdall.entitlement_snapshot.v0",
"heimdall.auth_completion.v0",
"heimdall.audit_event.v0",
"heimdall.app_profile.v0",
"heimdall.managed_credential_projection.v0",
] as const;
export type HeimdallWitnessSchemaId = (typeof heimdallWitnessSchemaIds)[number];
export interface HeimdallWitnessDocumentDescriptor {
schemaId: HeimdallWitnessSchemaId;
documentType: HeimdallWitnessSchemaId;
cultCachePath: string;
redaction: string;
ownedTruth: string;
forbiddenFields: string[];
}
export const heimdallWitnessDocuments: readonly HeimdallWitnessDocumentDescriptor[] = [
{
schemaId: "heimdall.account.v0",
documentType: "heimdall.account.v0",
cultCachePath: "cultcache/heimdall/accounts/{accountId}.cc",
redaction: "Email is optional and should be omitted unless an operator-safe identity hint is needed.",
ownedTruth: "Local GameCult account record independent of upstream providers.",
forbiddenFields: ["providerAccessToken", "providerRefreshToken"],
},
{
schemaId: "heimdall.linked_identity.v0",
documentType: "heimdall.linked_identity.v0",
cultCachePath: "cultcache/heimdall/linked-identities/{provider}/{providerUserId}.cc",
redaction: "Token ciphertext and raw provider profile stay out; export only custody status and selected identity labels.",
ownedTruth: "External provider identity linked to exactly one Heimdall account.",
forbiddenFields: ["accessTokenEncrypted", "refreshTokenEncrypted", "profileJson"],
},
{
schemaId: "heimdall.capability_grant.v0",
documentType: "heimdall.capability_grant.v0",
cultCachePath: "cultcache/heimdall/grants/{grantId}.cc",
redaction: "Operator notes must be reviewed before export and kept free of secrets or private app data.",
ownedTruth: "Manual or system-issued shared capability grant.",
forbiddenFields: ["providerAccessToken", "providerRefreshToken", "appDomainPayload"],
},
{
schemaId: "heimdall.session.v0",
documentType: "heimdall.session.v0",
cultCachePath: "cultcache/heimdall/sessions/{sessionId}.cc",
redaction: "Signed tokens are never exported; claims are reduced to audience, revision, expiry, facts, and capabilities.",
ownedTruth: "Local app/browser session issued by Heimdall.",
forbiddenFields: ["accessToken", "refreshToken", "claimsJson"],
},
{
schemaId: "heimdall.entitlement_snapshot.v0",
documentType: "heimdall.entitlement_snapshot.v0",
cultCachePath: "cultcache/heimdall/entitlements/{accountId}/{provider}/{scope}.cc",
redaction: "Raw provider responses stay out; export reason codes and compact summaries only.",
ownedTruth: "Cached provider entitlement evaluation result.",
forbiddenFields: ["rawProviderResponse", "providerAccessToken", "providerRefreshToken"],
},
{
schemaId: "heimdall.auth_completion.v0",
documentType: "heimdall.auth_completion.v0",
cultCachePath: "cultcache/heimdall/auth-completions/{completionCodeHash}.cc",
redaction: "Completion codes are hashed or omitted; payload is redacted to status, provider, app, mode, and expiry.",
ownedTruth: "One-time OAuth browser/backend handoff completion state.",
forbiddenFields: ["code", "accessToken", "refreshToken", "callbackSecret"],
},
{
schemaId: "heimdall.audit_event.v0",
documentType: "heimdall.audit_event.v0",
cultCachePath: "cultcache/heimdall/audit/{eventId}.cc",
redaction: "Audit payloads must pass token, code, secret, callback, and private-profile redaction before export.",
ownedTruth: "Durable auth/control-plane event history.",
forbiddenFields: ["accessToken", "refreshToken", "authorizationCode", "clientSecret"],
},
{
schemaId: "heimdall.app_profile.v0",
documentType: "heimdall.app_profile.v0",
cultCachePath: "cultcache/heimdall/app-profiles/{appSlug}.cc",
redaction: "Profile exports contain policy shape and capability names, not app-local state.",
ownedTruth: "Per-app auth policy profile owned by Heimdall.",
forbiddenFields: ["appSharedSecret", "appDomainState"],
},
{
schemaId: "heimdall.managed_credential_projection.v0",
documentType: "heimdall.managed_credential_projection.v0",
cultCachePath: "cultcache/heimdall/managed-credentials/{appSlug}/{accountId}/{provider}.cc",
redaction: "Expose custody status, provider subject, scopes, and expiry only; never provider tokens.",
ownedTruth: "Read-only projection that a managed provider credential exists in Heimdall custody.",
forbiddenFields: ["accessToken", "refreshToken", "accessTokenEncrypted", "refreshTokenEncrypted"],
},
] as const;
export interface HeimdallAdvertisedProvider {
key: Provider;
displayName: string;
roles: ProviderRole[];
defaultScopes: string[];
authorizationEndpoint: string;
configuredByEnv: {
clientId: string;
clientSecret: string;
};
}
export interface HeimdallAdvertisedAppProfile {
slug: AppSlug;
displayName: string;
profileVersion: string;
identityProviders: Provider[];
entitlementSources: Provider[];
managedConnectionProviders: Provider[];
capabilities: CapabilityDefinition[];
}
export interface EveProviderAdvertisementDocument {
schemaVersion: "gamecult.eve.provider_advertisement.v1";
providerId: "heimdall";
verseId: "gamecult";
title: "Heimdall";
description: string;
version: string;
status: "read_only_witness_planned";
updatedAt: string;
provider: {
id: "heimdall";
title: "Heimdall";
description: string;
capabilities: string[];
usesCultMesh: boolean;
transport: string;
};
controlSurface: {
primary: string;
controls: {
mode: "read-only";
writes: false;
reason: string;
};
};
endpoints: string[];
providers: HeimdallAdvertisedProvider[];
appProfiles: HeimdallAdvertisedAppProfile[];
documents: HeimdallWitnessDocumentDescriptor[];
}
export function buildHeimdallProviderAdvertisement(options: { updatedAt: string }): EveProviderAdvertisementDocument {
return {
schemaVersion: "gamecult.eve.provider_advertisement.v1",
providerId: "heimdall",
verseId: "gamecult",
title: "Heimdall",
description: "Shared GameCult auth/control-plane authority with redacted CultCache witness projections.",
version: "read-only-witness-v0",
status: "read_only_witness_planned",
updatedAt: options.updatedAt,
provider: {
id: "heimdall",
title: "Heimdall",
description: "Publishes provider/app auth surfaces and planned redacted witness document paths for Odin and Eve.",
capabilities: [
"shared-auth",
"provider-oauth",
"linked-identities",
"capability-grants",
"session-claims",
"managed-credential-custody",
"redacted-cultcache-witness",
],
usesCultMesh: true,
transport: "CultMesh provider advertisement; first cut is a read-only fixture/export.",
},
controlSurface: {
primary: "cultmesh://gamecult/heimdall/provider-advertisement",
controls: {
mode: "read-only",
writes: false,
reason: "This export advertises witness shapes only. Runtime auth mutation remains behind Heimdall APIs.",
},
},
endpoints: [
"cultmesh://gamecult/heimdall/provider-advertisement",
"cultmesh://gamecult/heimdall/witness",
],
providers: providers.map((provider) => {
const descriptor = providerCatalog[provider];
return {
key: descriptor.key,
displayName: descriptor.displayName,
roles: descriptor.roles,
defaultScopes: descriptor.defaultScopes,
authorizationEndpoint: descriptor.authorizationEndpoint,
configuredByEnv: {
clientId: descriptor.clientIdEnv,
clientSecret: descriptor.clientSecretEnv,
},
};
}),
appProfiles: appSlugs.map((appSlug) => {
const profile = appProfiles[appSlug];
return {
slug: profile.slug,
displayName: profile.displayName,
profileVersion: profile.profileVersion,
identityProviders: profile.identityProviders,
entitlementSources: profile.entitlementSources,
managedConnectionProviders: profile.managedConnectionProviders,
capabilities: profile.capabilities,
};
}),
documents: [...heimdallWitnessDocuments],
};
}

View file

@ -32,6 +32,7 @@ invariants:
- Host apps verify signed claims locally and keep resource ownership checks local.
- StreamPixels can reuse shared auth authority without centralizing streamer-audience data.
- Spotiverse consumes Spotify as a Heimdall-managed credential; Spotiverse does not own Spotify refresh-token custody.
- Heimdall's first Odin/Eve provider advertisement is read-only: it names redacted CultCache witness document shapes and paths, but does not write `.cc` state or mutate auth truth.
- Persistent state cruft is the same failure class as architecture cruft.
data_flow:
@ -93,6 +94,7 @@ landed_baseline:
- Heimdall now has generic Twitch and YouTube OAuth callback runtimes plus an app-authenticated managed-credential resolve endpoint for app-owned connector runtimes.
- StreamPixels now starts viewer and creator Twitch/YouTube OAuth through Heimdall, redeems Heimdall completion codes server-side, writes its own viewer sessions and creator connector bindings, and leaves provider refresh-token custody in Heimdall.
- Spotiverse now has a Heimdall app profile and Spotify provider runtime for managed credential custody.
- A read-only `gamecult.eve.provider_advertisement.v1` export now advertises Heimdall provider/app surfaces plus redacted CultCache witness document paths for Odin/Eve discovery.
rejected_paths:
- name: per_app_auth_copy_paste

View file

@ -0,0 +1,57 @@
import { describe, expect, it } from "vitest";
import {
buildHeimdallProviderAdvertisement,
heimdallWitnessDocuments,
heimdallWitnessSchemaIds,
} from "../src/verse-witness.js";
describe("Heimdall Verse witness advertisement", () => {
it("advertises the Odin/Eve provider surface and redacted witness documents", () => {
const advertisement = buildHeimdallProviderAdvertisement({
updatedAt: "2026-06-03T12:00:00.000Z",
});
expect(advertisement).toMatchObject({
schemaVersion: "gamecult.eve.provider_advertisement.v1",
providerId: "heimdall",
verseId: "gamecult",
status: "read_only_witness_planned",
updatedAt: "2026-06-03T12:00:00.000Z",
controlSurface: {
controls: {
mode: "read-only",
writes: false,
},
},
});
expect(advertisement.providers.map((provider) => provider.key)).toEqual([
"discord",
"patreon",
"github",
"twitch",
"youtube",
"spotify",
]);
expect(advertisement.appProfiles.map((profile) => profile.slug)).toEqual([
"repixelizer",
"streampixels",
"spotiverse",
]);
expect(advertisement.documents.map((document) => document.schemaId)).toEqual([...heimdallWitnessSchemaIds]);
});
it("keeps token custody fields forbidden in witness descriptors", () => {
expect(heimdallWitnessDocuments).toEqual(
expect.arrayContaining([
expect.objectContaining({
schemaId: "heimdall.linked_identity.v0",
forbiddenFields: expect.arrayContaining(["accessTokenEncrypted", "refreshTokenEncrypted"]),
}),
expect.objectContaining({
schemaId: "heimdall.managed_credential_projection.v0",
forbiddenFields: expect.arrayContaining(["accessToken", "refreshToken"]),
}),
])
);
});
});