Feature 57 (config and provisioning): release mesh-sdk, the reviewed release branch on top of main
mesh/merge-gate head 1bc5719b8b85 merged into trunk a2d2af00dc5d beside mesh-host#116@bff1ac14 — pass, with a note: builds mesh-sdk (+40 dependent(s)) → ac…
mesh/repo-check head 1bc5719b8b85 merged into trunk a2d2af00dc5d beside mesh-host#116@bff1ac14 — pass: its merge-check.sh passed, each part in its toolchai…
mesh/delivery delivered
mesh/delivery-group group release/57-config-and-provisioning delivered: every member is delivered

This commit is contained in:
2026-10-16 08:24:11 +00:00
parent a2d2af00dc
commit 1bc5719b8b
24 changed files with 2580 additions and 40 deletions
+6 -6
View File
@@ -8,7 +8,7 @@ import { mkdtemp, writeFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { runProvisioner, provisionerTools, RETIREMENT_EVENT, type Provisioner } from "../dist/provisioner/index.js";
import { runProviderLoop, providerLoopTools, RETIREMENT_EVENT, type ProviderLoop } from "../dist/provider-loop/index.js";
import { resetTools, collectTools } from "../dist/tools/index.js";
const EVERY = 10;
@@ -32,7 +32,7 @@ interface World {
reenabled: string[];
events: Record<string, unknown>[];
said: string[];
p: Provisioner;
p: ProviderLoop;
done(): void;
}
@@ -62,7 +62,7 @@ async function world(
...(opts.retire === false ? {} : { async retire(p: { as: string; why: string }) { w.retired.push({ as: p.as, why: p.why }); } }),
...(opts.held ? { async held() { return opts.held!; } } : {}),
};
w.p = runProvisioner("postgres-database", adapter, {
w.p = runProviderLoop("postgres-database", adapter, {
receives, everyMs: EVERY, node: "anchor", stableForMs: opts.stableForMs ?? 0,
// Only retirement is these tests'; the standing (provisioner.failing, .recovered) is standing.test.ts's.
announce: (event, body) => { if (event === RETIREMENT_EVENT) w.events.push(body); },
@@ -168,7 +168,7 @@ test("five passes are not enough: the same set must also hold for stableForMs",
});
test("the default hold is ten minutes", async () => {
const { STABLE_FOR_MS } = await import("../dist/provisioner/index.js");
const { STABLE_FOR_MS } = await import("../dist/provider-loop/index.js");
assert.equal(STABLE_FOR_MS, 10 * 60_000);
});
@@ -327,7 +327,7 @@ test("the four tools are registered for the module and answer", async () => {
const dir = await mkdtemp(join(tmpdir(), "retire-tools-"));
const receives = join(dir, "r.json");
await writeFile(receives, JSON.stringify({ requirement: "r", given: [] }));
const p = runProvisioner("r", { async create() {}, async remove() {} }, { receives, everyMs: EVERY, module: "postgres" });
const p = runProviderLoop("r", { async create() {}, async remove() {} }, { receives, everyMs: EVERY, module: "postgres" });
try {
const mine = collectTools({}).find((c) => c.module === "postgres");
assert.deepEqual(mine?.tools.map((t) => t.name), [
@@ -336,7 +336,7 @@ test("the four tools are registered for the module and answer", async () => {
const answer = (await mine!.tools[0].run({})) as { resource: string; waiting: unknown };
assert.equal(answer.resource, "r");
assert.equal(answer.waiting, null);
assert.equal(provisionerTools(p).length, 4);
assert.equal(providerLoopTools(p).length, 4);
} finally {
p();
resetTools();
+26 -13
View File
@@ -6,7 +6,7 @@ import { join } from "node:path";
import { compareVersions } from "../dist/primitives/index.js";
import { registerModuleTools, collectTools, collectToolsEach, resetTools, serveTools, listTools, invokeTool } from "../dist/tools/index.js";
import { runProvisioner } from "../dist/provisioner/index.js";
import { runProviderLoop } from "../dist/provider-loop/index.js";
import { emit, on, type Event } from "../dist/events/index.js";
import { useBroker } from "../dist/messaging/index.js";
@@ -47,7 +47,7 @@ test("provisioner creates each consumer with the mesh's login and password; remo
doc([{ from: "webapp", node: "anchor", as: "webapp-anchor", secret: join(dir, "webapp.secret"), values: { name: "webapp" } }]),
);
const stop = runProvisioner(
const stop = runProviderLoop(
"analytics",
{
async create(p) {
@@ -101,7 +101,7 @@ test("provisioner hands the adapter what the mesh derived, and still has it at w
});
await writeFile(receives, doc([entry("mesh-anchor-ncloud")]));
const stop = runProvisioner(
const stop = runProviderLoop(
"s3-bucket",
{
async create(p) {
@@ -153,7 +153,7 @@ test("provisioner stops calling an unreadable secret a race, and says what to lo
console.error = (line: unknown) => { said.push(String(line)); };
let stop: (() => void) | undefined;
try {
stop = runProvisioner("db", { async create() {}, async remove() {} }, { receives, everyMs: 5 });
stop = runProviderLoop("db", { async create() {}, async remove() {} }, { receives, everyMs: 5 });
await waitFor(() => said.some((l) => l.includes("CANNOT READ")), 3000);
} finally {
stop?.();
@@ -185,7 +185,7 @@ test("provisioner removes nothing while its contributions file cannot be used (n
]);
await writeFile(receives, both);
const stop = runProvisioner(
const stop = runProviderLoop(
"postgres-database",
{
async create(p) {
@@ -239,7 +239,7 @@ test("provisioner applies again what the backend no longer holds, and trusts mem
const backend = new Set<string>();
let creates = 0;
let asked = 0;
const stop = runProvisioner(
const stop = runProviderLoop(
"cache",
{
async create(p) {
@@ -271,7 +271,7 @@ test("provisioner applies again what the backend no longer holds, and trusts mem
// An adapter without holds is trusted from memory, as before: a forgotten backend stays forgotten.
const forgetful = new Set<string>();
let plainCreates = 0;
const stopPlain = runProvisioner(
const stopPlain = runProviderLoop(
"cache",
{
async create(p) {
@@ -299,7 +299,7 @@ test("provisioner keeps a consumer applied when the backend cannot be asked", as
);
let creates = 0;
let asked = 0;
const stop = runProvisioner(
const stop = runProviderLoop(
"cache",
{
async create() {
@@ -330,7 +330,7 @@ test("provisioner backs off when create does not satisfy holds, and says so", as
const original = console.error;
console.error = (...a: unknown[]) => logged.push(a.join(" "));
try {
const stop = runProvisioner(
const stop = runProviderLoop(
"cache",
{
async create() {
@@ -371,7 +371,7 @@ test("provisioner treats a failed or hung holds as could-not-ask, and never logs
const original = console.error;
console.error = (...a: unknown[]) => logged.push(a.join(" "));
try {
const stop = runProvisioner(
const stop = runProviderLoop(
"cache",
{
async create() {
@@ -404,7 +404,7 @@ test("provisioner treats a failed or hung holds as could-not-ask, and never logs
const hungLogged: string[] = [];
console.error = (...a: unknown[]) => hungLogged.push(a.join(" "));
try {
const stopHung = runProvisioner(
const stopHung = runProviderLoop(
"cache",
{
async create() {
@@ -438,7 +438,7 @@ test("provisioner stops a verify pass at a timeout, and retries a failed re-appl
try {
// A timeout on "a" means "b" is not asked in that pass.
const askedFor: string[] = [];
const stopHung = runProvisioner(
const stopHung = runProviderLoop(
"cache",
{
async create() {},
@@ -460,7 +460,7 @@ test("provisioner stops a verify pass at a timeout, and retries a failed re-appl
let createCalls = 0;
const logged: string[] = [];
console.error = (...a: unknown[]) => logged.push(a.join(" "));
const stop = runProvisioner(
const stop = runProviderLoop(
"cache",
{
async create(p) {
@@ -649,3 +649,16 @@ test("each registration is given the environment asked for it, and sees no other
assert.deepEqual(seen.beta, { BETA_URL: "http://127.0.0.1:3000" });
resetTools();
});
test("the provider loop is named for what it is, and its old name stays an alias until its providers move (issue 678)", async () => {
const loop = await import("../dist/provider-loop/index.js");
const old = await import("../dist/index.js");
assert.equal(old.provisioner.runProvisioner, loop.runProviderLoop);
assert.equal(old.provisioner.provisionerTools, loop.providerLoopTools);
assert.equal(old.providerLoop.runProviderLoop, loop.runProviderLoop);
const pkg = JSON.parse(await (await import("node:fs/promises")).readFile(new URL("../package.json", import.meta.url), "utf8"));
assert.equal(pkg.exports["./provider-loop"], "./dist/provider-loop/index.js");
assert.equal(pkg.exports["./provisioner"], pkg.exports["./provider-loop"]);
// The events and tools are what the controller and the operator read on the bus: their names stay.
assert.equal(loop.FAILING_EVENT, "provisioner.failing");
});
+71
View File
@@ -0,0 +1,71 @@
import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { createPrivateKey } from "node:crypto";
import { seal, open, generateKeyPair, type SealedBox } from "../dist/seal/index.js";
import { sealWith } from "../dist/seal/box.js";
// The fixtures the Go package proves itself against: boxes the licence manager's own seal.go made, and
// the one the agent module's TypeScript made. One format, three implementations.
const fixtures = new URL("../go/seal/testdata/", import.meta.url);
const lm = JSON.parse(readFileSync(new URL("sealed-by-licence-manager.json", fixtures), "utf8")) as {
recipient: { publicKey: string; privateKey: string };
sealed: { plaintext: string; box: SealedBox }[];
vectors: { plaintext: string; ephPrivate: string; iv: string; box: SealedBox }[];
};
// PKCS #8 for an X25519 private key is this prefix and the 32 raw bytes.
const pkcs8 = (raw: Buffer) =>
createPrivateKey({ key: Buffer.concat([Buffer.from("302e020100300506032b656e04220420", "hex"), raw]), format: "der", type: "pkcs8" });
test("seal: every box the licence manager sealed opens to its plaintext", () => {
assert.ok(lm.sealed.length > 0);
for (const s of lm.sealed) assert.equal(open(s.box, lm.recipient.privateKey), s.plaintext);
});
test("seal: given the same one-time key and iv, the box is byte for byte the licence manager's", () => {
assert.ok(lm.vectors.length > 0);
for (const v of lm.vectors) {
const box = sealWith(v.plaintext, lm.recipient.publicKey, pkcs8(Buffer.from(v.ephPrivate, "base64")), Buffer.from(v.iv, "base64"));
assert.deepEqual(box, v.box);
assert.equal(open(box, lm.recipient.privateKey), v.plaintext);
}
});
test("seal: the agent module's TypeScript box opens", () => {
const f = JSON.parse(readFileSync(new URL("sealed-by-typescript.json", fixtures), "utf8"));
assert.equal(open(f.box, f.privateKey), f.plaintext);
});
test("seal: a box opens for its recipient only, and two boxes share no one-time key or iv", () => {
const a = generateKeyPair();
const b = generateKeyPair();
const box = seal("a token", a.publicKey);
assert.equal(open(box, a.privateKey), "a token");
assert.throws(() => open(box, b.privateKey));
const again = seal("a token", a.publicKey);
assert.notEqual(again.eph, box.eph);
assert.notEqual(again.iv, box.iv);
});
test("seal: a box changed in any field, or of another version, does not open", () => {
const good = lm.sealed[1].box;
const flip = (s: string) => {
const b = Buffer.from(s, "base64");
b[b.length - 1] ^= 1;
return b.toString("base64");
};
for (const box of [
{ ...good, ct: flip(good.ct) },
{ ...good, tag: flip(good.tag) },
{ ...good, iv: flip(good.iv) },
{ ...good, eph: flip(good.eph) },
{ ...good, v: 2 },
]) assert.throws(() => open(box as SealedBox, lm.recipient.privateKey));
});
test("seal: a key that is not X25519 PEM is refused", () => {
assert.throws(() => seal("x", "not a key"));
assert.throws(() => open(lm.sealed[0].box, lm.recipient.publicKey));
});
+3 -3
View File
@@ -9,9 +9,9 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import {
runProvisioner, classOf, FAILING_EVENT, RECOVERED_EVENT, RETIREMENT_EVENT,
runProviderLoop, classOf, FAILING_EVENT, RECOVERED_EVENT, RETIREMENT_EVENT,
CLASS_CREDENTIALS, CLASS_UNREACHABLE, CLASS_SECRET, CLASS_REFUSED, type Adapter,
} from "../dist/provisioner/index.js";
} from "../dist/provider-loop/index.js";
import { useBroker } from "../dist/messaging/index.js";
const EVERY = 10;
@@ -68,7 +68,7 @@ async function world(opts: { classify?: boolean; announce?: false; stableForMs?:
};
const was = console.error;
console.error = () => {};
const stop = runProvisioner("postgres-database", adapter, {
const stop = runProviderLoop("postgres-database", adapter, {
receives, everyMs: EVERY, verifyEveryMs: 0, node: "home-server",
failingAfterMs: FAILING_AFTER, sayAgainEveryMs: SAY_AGAIN, stableForMs: opts.stableForMs ?? 0,
...(opts.announce === false ? {} : {