feat: add display kiosk host
CI / verify (pull_request) Successful in 38s
CI / infrastructure-smoke (pull_request) Successful in 20s
CI / migration-smoke (pull_request) Successful in 59s

This commit is contained in:
ekklesia
2026-08-23 20:50:56 -05:00
parent be5ed7853d
commit 2e0fe059c3
80 changed files with 4868 additions and 397 deletions
+22 -5
View File
@@ -32,6 +32,7 @@ import {
import { type ServerType, serve } from "@hono/node-server";
import type { Hono } from "hono";
import { createApiApp } from "./app.js";
import { loadDisplayHostAssets } from "./display-host-assets.js";
import {
type ClientAddressConfigInput,
type ClientAddressResolver,
@@ -489,6 +490,21 @@ export async function startApi(
})),
);
// This snapshot is inert metadata only: construction reads no artifact and executes no module.
// It lets the API prove that a display host build is available *before* importing any eligible
// module code. The snapshot is not exposed anywhere unless the complete runtime subsequently
// starts and application composition succeeds.
const moduleUi = createModuleUiPublicationRegistry(eligibility.eligibleModules);
let displayHostAssets: Awaited<ReturnType<typeof loadDisplayHostAssets>> | undefined;
try {
displayHostAssets =
moduleUi.listDisplays().length === 0 ? undefined : await loadDisplayHostAssets();
} catch (error) {
await safeCloseStorage(storageProvider);
await safeCloseDatabase(databaseHandle);
throw error;
}
// Module load/register/start failures use Core's own existing rollback (already-started
// modules are stopped, in reverse order, before this rejects) — there is no separate runtime
// handle to stop here, only the pool this failure leaves open.
@@ -524,11 +540,12 @@ export async function startApi(
moduleRealtime: moduleRuntime.realtime,
moduleServices: services,
// Built from the exact **eligible** set the runtime just started, never the raw discovered
// inventory, and only once startup has succeeded — so the published set is the running set. A
// disabled or dependency-blocked module therefore publishes no artifact, appears in no browser
// inventory, and appears in no resolved Dashboard navigation, by construction rather than by a
// filter somewhere downstream. Modules declaring no `entrypoints.ui` are simply absent.
moduleUi: createModuleUiPublicationRegistry(eligibility.eligibleModules),
// inventory, and exposed only after startup succeeded — so the published set is the running
// set. A disabled or dependency-blocked module therefore publishes no artifact, appears in no
// browser inventory, and appears in no resolved Dashboard navigation, by construction rather
// than by a filter somewhere downstream. Modules declaring no UI role are simply absent.
moduleUi,
...(displayHostAssets === undefined ? {} : { displayHostAssets }),
moduleLifecycle: {
// The boot snapshot: what this process discovered, what it resolved, and what the runtime
// reports it actually started — read from the runtime rather than assumed equal to the