2026-06-16 18:01:07 +01:00
|
|
|
import { mkdtemp, readFile } from "node:fs/promises";
|
|
|
|
|
import os from "node:os";
|
|
|
|
|
import path from "node:path";
|
|
|
|
|
import { decode } from "@msgpack/msgpack";
|
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
|
import { loadConfig } from "../src/config.js";
|
|
|
|
|
import { createHeimdallRuntimePulse, publishHeimdallVerseState } from "../src/verse-state.js";
|
|
|
|
|
|
|
|
|
|
describe("Heimdall verse runtime publication", () => {
|
|
|
|
|
it("writes provider advertisement, command boundary, transport profile, and daemon health to the witness store", async () => {
|
|
|
|
|
const tmp = await mkdtemp(path.join(os.tmpdir(), "heimdall-verse-runtime-"));
|
|
|
|
|
const config = loadConfig({
|
|
|
|
|
GC_ACCESS_BASE_URL: "https://heimdall.gamecult.org",
|
|
|
|
|
GC_ACCESS_CULTCACHE_PATH: path.join(tmp, "cultcache", "heimdall.service.cc"),
|
|
|
|
|
GC_ACCESS_DATABASE_URL: "postgres://heimdall:test@localhost/heimdall",
|
|
|
|
|
GC_ACCESS_IDUNN_RUDP_HEALTH: "10.77.0.2:17870",
|
|
|
|
|
GC_ACCESS_PROVIDER_DISCORD_CLIENT_ID: "discord-client",
|
|
|
|
|
GC_ACCESS_PROVIDER_DISCORD_CLIENT_SECRET: "discord-secret",
|
|
|
|
|
GC_ACCESS_PROVIDER_TWITCH_CLIENT_ID: "twitch-client",
|
|
|
|
|
GC_ACCESS_PROVIDER_TWITCH_CLIENT_SECRET: "twitch-secret",
|
|
|
|
|
});
|
|
|
|
|
const pulse = createHeimdallRuntimePulse(config, "2026-06-16T17:10:00.000Z");
|
|
|
|
|
|
|
|
|
|
await publishHeimdallVerseState(config, pulse);
|
|
|
|
|
|
|
|
|
|
const contents = await readFile(config.cultCachePath);
|
|
|
|
|
const decoded = decode(contents) as [string, unknown[], Array<[string, string, string, Uint8Array]>];
|
|
|
|
|
|
|
|
|
|
expect(decoded[0]).toBe("cultcache.store.v1");
|
|
|
|
|
const entries = decoded[2];
|
|
|
|
|
const bySchema = new Map(entries.map((entry) => [entry[1], decode(entry[3])]));
|
|
|
|
|
|
|
|
|
|
expect(bySchema.get("gamecult.eve.provider_advertisement.v1")).toMatchObject({
|
|
|
|
|
providerId: "heimdall",
|
|
|
|
|
status: "daemon_live",
|
|
|
|
|
locatedService: "asgard.yggdrasil.heimdall",
|
|
|
|
|
runtime: {
|
|
|
|
|
storageBackend: "postgres",
|
|
|
|
|
configuredProviders: ["discord", "twitch"],
|
Retire the Spotiverse profile; keep the spotify provider intact for restore
Spotiverse was lost with the E: drive and has no repository in the swarm,
but /v1/oauth/spotify/start stayed publicly reachable and
GC_ACCESS_PROVIDER_SPOTIFY_CLIENT_ID/_SECRET stayed configured on
yggdrasil. That path would have run a real OAuth handshake against a real
secret and taken custody of a user's Spotify refresh token for a consumer
that could not use it.
The seam is supportsProvider(profile, provider): a provider with no app
behind it is unreachable. So the registry entry is what had to go, not the
provider.
Removed: spotiverseProfile and its registry entry, the spotiverse slug
from appSlugs, the hardcoded callback arm in app.ts, and three
spotiverse-specific tests. Removed from the host: the two spotify env
vars, backed up to /root/heimdall-service.env.bak-20260906-spotify. Nine
other provider vars and the file's 0600 heimdall:heimdall ownership are
untouched.
Kept deliberately: the entire spotify provider -- the providers.ts catalog
entry, spotifyRuntime and its token/profile exchange in oauth.ts, and
"spotify" in the providers union. It is intact and unreferenced, which is
what makes restoring a registry change rather than protocol work. The
capability is wanted back; a Persona managing the queue during a stream is
the motivating case.
docs/app-profiles/spotiverse.md is now the restore spec rather than an
archive: what was removed, what was kept, the base commit 5a15f71 to
recover the profile verbatim, and the daemon's callback URL
(http://127.0.0.1:8796/auth/heimdall/callback), which existed only in a
hardcoded allowlist arm and would otherwise have been lost with it.
Before deleting the three tests I checked their routes keep coverage:
managed-credential resolve is exercised twice via streampixels, and
backend_callback handoff at three other sites. The generic
untrusted_backend_callback negative test was retargeted to bifrost rather
than deleted. 35 pass, 9 files; tsc clean.
Verified the deployed build still boots without the vars --
readProviderConfig returns an empty config rather than throwing, so the
running release fails that route at request time instead of at startup.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-06 01:47:10 +02:00
|
|
|
appProfileCount: 4,
|
2026-06-16 18:01:07 +01:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect(bySchema.get("heimdall.command_boundary.v1")).toMatchObject({
|
|
|
|
|
boundaryId: "heimdall",
|
|
|
|
|
daemonId: "yggdrasil-heimdall",
|
|
|
|
|
healthPublication: {
|
|
|
|
|
contract: "heimdall.cultnet-rudp-provider-health",
|
|
|
|
|
transport: "cultnet.transport.rudp.v0",
|
|
|
|
|
},
|
2026-08-22 00:44:57 +02:00
|
|
|
commands: [
|
|
|
|
|
{ operation: "heimdall.auth.begin" },
|
|
|
|
|
{ operation: "heimdall.auth.complete" },
|
2026-08-22 01:27:28 +02:00
|
|
|
{ operation: "heimdall.auth.refresh" },
|
2026-08-22 01:53:21 +02:00
|
|
|
{ operation: "heimdall.auth.logout" },
|
2026-08-22 00:44:57 +02:00
|
|
|
],
|
|
|
|
|
privateRoute: {
|
|
|
|
|
endpoint: "rudp://127.0.0.1:4101",
|
|
|
|
|
exposure: "loopback-only",
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
expect(bySchema.get("gamecult.eve.plugin_advertisement.v1")).toMatchObject({
|
|
|
|
|
pluginId: "gamecult.heimdall.access",
|
|
|
|
|
ownerService: "asgard.heimdall",
|
|
|
|
|
commands: ["heimdall.auth.begin", "heimdall.auth.complete", "app.auth.logout"],
|
2026-06-16 18:01:07 +01:00
|
|
|
});
|
|
|
|
|
expect(bySchema.get("heimdall.transport_profile.v1")).toMatchObject({
|
|
|
|
|
daemonId: "yggdrasil-heimdall",
|
|
|
|
|
targetTransport: "cultnet.transport.rudp.v0",
|
|
|
|
|
healthTransport: "cultnet.transport.rudp.v0",
|
|
|
|
|
});
|
|
|
|
|
expect(bySchema.get("idunn.daemon_health")).toEqual([
|
|
|
|
|
"yggdrasil-heimdall",
|
|
|
|
|
"active",
|
2026-10-19 11:20:18 +02:00
|
|
|
"Heimdall auth runtime active; storage=postgres; providers=2/7; apps=4; healthTransport=CultNet/RUDP",
|
2026-06-16 18:01:07 +01:00
|
|
|
"2026-06-16T17:10:00.000Z",
|
|
|
|
|
"heimdall.cultnet-rudp-provider-health",
|
|
|
|
|
"daemon-published",
|
|
|
|
|
"cultnet.transport.rudp.v0",
|
|
|
|
|
]);
|
|
|
|
|
});
|
|
|
|
|
});
|