The trust bed raises a mesh, and places the bus's users as genesis must

novox/hq 04-ISSUES/146. The bed now does what raising a first node actually
takes: the private network so the authority can certify the address it holds,
and the composed user list placed beside the bus at the two moments an account
comes into existence — when the token is issued, and when the machine enrols.
Neither can arrive in a declaration, because a machine that has not enrolled
gets none.

MESH_LAB_KEEP leaves the machine standing, which is where every answer in this
sequence came from. No 'module issue' for the authority: that delivers a bus
account and it declares none.

The bed still fails, at the machine being enrolled twice from one attempt.
This commit is contained in:
2026-09-29 17:37:08 +02:00
parent f94ee2dd0e
commit f2d6ab3bf9
+39 -2
View File
@@ -118,6 +118,23 @@ async function register(module: string): Promise<void> {
await mesh(`module add /${module}.json`); await mesh(`module add /${module}.json`);
} }
/**
* Put the mesh's composed user list where this machine's bus reads it, and make it re-read.
*
* **What genesis has to do by hand, and only genesis** (novox/hq 04-ISSUES/146). Every account on
* the bus reaches it in the declaration of the machine that runs it — which requires that machine
* to be an enrolled node, and at genesis it is not. So until the bus is a module, the composition
* is placed by whoever is raising the machine: the control plane says what it composed, and this
* writes it beside the bus's configuration. Twice, because two accounts come into existence at
* different moments — the enrolment when the token is issued, and the node's own when it enrols.
*/
async function composeTheBusUsers(): Promise<void> {
await must(
`docker exec mesh-controller /mesh-controller broker accounts > /var/lib/mesh-bus-conf/accounts.conf && ` +
`docker kill -s HUP mesh-broker`,
);
}
/** Dial the authority the way anything on this machine would: verifying, with nothing handed to it. */ /** Dial the authority the way anything on this machine would: verifying, with nothing handed to it. */
async function verifying(): Promise<{ out: string; ok: boolean }> { async function verifying(): Promise<{ out: string; ok: boolean }> {
return on(`curl --silent --show-error --max-time 10 ${AUTHORITY}`); return on(`curl --silent --show-error --max-time 10 ${AUTHORITY}`);
@@ -155,11 +172,20 @@ before(async () => {
await mesh(`node add ${MACHINE}`); await mesh(`node add ${MACHINE}`);
const token = tokenFrom(await mesh(`token issue --node ${MACHINE}`)); const token = tokenFrom(await mesh(`token issue --node ${MACHINE}`));
// The account that token is the password of exists in the mesh's records now; this is what puts
// it on the bus, because nothing else can until this machine is a node.
await composeTheBusUsers();
await must(`${HOST_PATH} enrol --token ${quote(token)}`); await must(`${HOST_PATH} enrol --token ${quote(token)}`);
// And again for the credential enrolment just minted, which the machine's own link connects with.
await composeTheBusUsers();
await must(`nohup ${HOST_PATH} run > /var/log/mesh-host.log 2>&1 & sleep 3`); await must(`nohup ${HOST_PATH} run > /var/log/mesh-host.log 2>&1 & sleep 3`);
}, { timeout: 1_800_000 }); }, { timeout: 1_800_000 });
after(async () => { after(async () => {
if (process.env["MESH_LAB_KEEP"]) {
console.log(`MESH_LAB_KEEP set — leaving ${instanceId} standing`);
return;
}
if (instanceId) await destroy(instanceId); if (instanceId) await destroy(instanceId);
await destroyAll(`${SCENARIO}-`); await destroyAll(`${SCENARIO}-`);
}, { timeout: 600_000 }); }, { timeout: 600_000 });
@@ -167,9 +193,20 @@ after(async () => {
test("the mesh's authority is verified on a machine holding ca-trust, and not on one that is not", { test("the mesh's authority is verified on a machine holding ca-trust, and not on one that is not", {
skip, timeout: 1_800_000, skip, timeout: 1_800_000,
}, async () => { }, async () => {
// The authority first, on its own. Nothing about trust yet. // The private network first. The authority certifies itself for the address it holds there
// (`${machine:at}`), which a machine with no overlay has not got — so this is what the bed needs
// it for, and nothing else.
await mesh(`overlay place ${MACHINE} --hub --endpoint 192.0.2.10:51820 --site lab`);
// The control plane ships this one — its resources are computed per node, so there is no
// manifest to register.
await mesh(`assign ${MACHINE} networking`);
await mesh(`push ${MACHINE}`);
await settled();
// The authority next, on its own. Nothing about trust yet.
await register("step-ca"); await register("step-ca");
await mesh(`module issue step-ca --node ${MACHINE}`); // No `module issue`: that delivers a bus account, and the authority declares none — its own
// secret is the password it initialises itself with, which the mesh mints at assignment.
await mesh(`assign ${MACHINE} step-ca`); await mesh(`assign ${MACHINE} step-ca`);
await mesh(`push ${MACHINE}`); await mesh(`push ${MACHINE}`);
await settled(); await settled();