Beds read the catalogue: a shared loader, eight beds converted, the rest declared

catalogueModule() in the harness reads a module's manifest from the catalogue and
rewrites only what the lab must: the build section goes, each artifact becomes the
image the machine holds, images are pinned, and a bed may declare a host-port remap
or a lab-local address. confluence, gitlab, openai-consumer, audit-logger, ollama,
local-model-consumer, model-usage, mosquitto, anthropic-manager and
anthropic-consumer now install the catalogue's manifest. A unit test refuses any
inline copy naming a catalogue module unless the bed is declared with its reason;
the declared list is the debt (novox/hq 04-ISSUES/073).
This commit is contained in:
2026-09-21 14:27:49 +02:00
parent f2d29491b2
commit 2456b2f533
10 changed files with 259 additions and 361 deletions
+13 -61
View File
@@ -49,7 +49,7 @@ import { loadScenario } from "../../src/declaration/parse.ts";
import { raise } from "../../src/lifecycle/raise.ts";
import { destroy, exec } from "../../src/lifecycle/operate.ts";
import { hostBinaryPath, HOST_PATH } from "../../src/lifecycle/place.ts";
import { labIsUsable, destroyAll, foundationBundle, onTheMachine } from "./harness.ts";
import { labIsUsable, destroyAll, foundationBundle, onTheMachine, catalogueModule, catalogueIsPresent } from "./harness.ts";
import type { HeldImage } from "../../src/pinning.ts";
const capability = await labIsUsable();
@@ -62,7 +62,7 @@ const skip = !capability.usable
? "MESH_LAB_HOST_BINARY is not set to a built mesh-host"
: !bundle || !existsSync(bundle)
? "MESH_LAB_BUNDLE is not set to a foundation bundle (mesh-host examples/)"
: false;
: catalogueIsPresent();
const SCENARIO = "anthropic-bed";
const MACHINE = "anchor";
@@ -194,8 +194,6 @@ after(async () => {
test("model access refreshes on the manager node and delivers only the access token, never the refresh token", {
skip, timeout: 1_500_000,
}, async () => {
const managerImage = pinned("mesh-runtime-anthropic-manager");
const consumerImage = pinned("mesh-runtime-anthropic-consumer");
// --- the licence, and the manager as its holder ------------------------------------------------
// The manager module is a HOLDER, named the licence's manager. It is delivered the refresh token;
@@ -207,37 +205,15 @@ test("model access refreshes on the manager node and delivers only the access to
await mesh(`licence use personal ${MACHINE} anthropic-manager`);
// --- the manager module, deployed so the host delivers its bound facts --------------------------
// Inline manifest mirroring the committed module.json: model-access holder, refresh token bound as a
// sealed secret, no node-key mount. The scheduled container installs as present state (ADR 0053);
// the test drives adopt/refresh directly for a deterministic flow rather than waiting on cron.
const managerManifest = JSON.stringify({
module: "anthropic-manager",
version: "1",
requires: ["model-access"],
binds: { "model-access": "/var/lib/mesh/anthropic-manager/model.json" },
secrets: { "model-access": "/var/lib/mesh/anthropic-manager/refresh-token" },
"own-secrets": { broker: "/var/lib/mesh/anthropic-manager/broker" },
emits: ["module.anthropic-manager.usage.read"],
resources: [
{ id: "mesh-state", type: "directory", path: "/var/lib/mesh/anthropic-manager", mode: "0700" },
{ id: "out", type: "directory", path: "/var/lib/mesh/anthropic-manager/out", mode: "0700" },
{
id: "refresh", type: "container", name: "mesh-anthropic-manager-refresh",
image: managerImage, network: "host", schedule: "*/9 * * * *",
args: ["run", "/app/modules/anthropic-manager/dist/refresh/index.js"],
volumes: ["/var/lib/mesh/anthropic-manager:/run/state"],
env: {
MESH_ANTHROPIC_LICENCE: "personal",
MESH_ANTHROPIC_TOKEN_ENDPOINT: "http://127.0.0.1:9099/token",
MESH_ANTHROPIC_USAGE_ENDPOINT: "http://127.0.0.1:9099/usage",
MESH_MODEL_ACCESS_SECRET_FILE: "/run/state/refresh-token",
MESH_MODEL_ACCESS_BIND_FILE: "/run/state/model.json",
MESH_ANTHROPIC_ACCESS_OUT: "/run/state/out/access-token",
MESH_ANTHROPIC_GRANT_OUT: "/run/state/out/grant.json",
MESH_ANTHROPIC_USAGE_OUT: "/run/state/out/usage.json",
},
},
],
// The catalogue's own manifest (novox/hq 04-ISSUES/073): model-access holder, refresh token bound
// as a sealed secret, no node-key mount. The scheduled container installs as present state (ADR
// 0053); the test drives adopt/refresh directly for a deterministic flow rather than waiting on
// cron. The one lab rewrite: the OAuth endpoints point at the stub this bed raises below.
const managerManifest = catalogueModule("anthropic-manager", held, {
env: { refresh: {
MESH_ANTHROPIC_TOKEN_ENDPOINT: "http://127.0.0.1:9099/token",
MESH_ANTHROPIC_USAGE_ENDPOINT: "http://127.0.0.1:9099/usage",
} },
});
await must(`printf %s ${quote(managerManifest)} > /tmp/anthropic-manager.json && docker cp /tmp/anthropic-manager.json mesh-controller:/anthropic-manager.json`);
await mesh(`module add /anthropic-manager.json`);
@@ -329,32 +305,8 @@ test("model access refreshes on the manager node and delivers only the access to
assert.match(submitted, /sealed to 1 holder/, submitted);
// --- 5. deliver: deploy the consumer and push; it gets the sealed access token -------------------
const consumerManifest = JSON.stringify({
module: "anthropic-consumer",
version: "1",
requires: ["model-access"],
binds: { "model-access": "/var/lib/anthropic-consumer/model.json" },
secrets: { "model-access": "/var/lib/anthropic-consumer/access-token" },
"own-secrets": { broker: "/var/lib/mesh/anthropic-consumer/broker" },
emits: ["module.anthropic-consumer.usage.session"],
resources: [
{ id: "mesh-state", type: "directory", path: "/var/lib/mesh/anthropic-consumer", mode: "0700" },
{ id: "state", type: "directory", path: "/var/lib/anthropic-consumer", mode: "0700" },
{ id: "claude-home", type: "directory", path: "/var/lib/anthropic-consumer/claude", mode: "0700" },
{
id: "apply", type: "container", name: "mesh-anthropic-consumer-apply",
image: consumerImage, network: "host", schedule: "*/9 * * * *",
args: ["run", "/app/modules/anthropic-consumer/dist/apply/index.js"],
volumes: ["/var/lib/anthropic-consumer:/run/state"],
env: {
MESH_MODEL_ACCESS_SECRET_FILE: "/run/state/access-token",
MESH_MODEL_ACCESS_BIND_FILE: "/run/state/model.json",
MESH_CLAUDE_CREDENTIALS_FILE: "/run/state/claude/.credentials.json",
MESH_CLAUDE_IDENTITY_FILE: "/run/state/claude/.claude.json",
},
},
],
});
// The catalogue's own manifest (novox/hq 04-ISSUES/073).
const consumerManifest = catalogueModule("anthropic-consumer", held);
await must(`printf %s ${quote(consumerManifest)} > /tmp/anthropic-consumer.json && docker cp /tmp/anthropic-consumer.json mesh-controller:/anthropic-consumer.json`);
await mesh(`module add /anthropic-consumer.json`);
await mesh(`module issue anthropic-consumer --node ${MACHINE}`);