The runtime speaks the bus its credential names

A module moved to the bus being built was handed a credential for it — nats://
with user, password and fingerprint beside the address — and nothing else in its
environment changed. The runtime always dialled the old bus, so every moved module
kept serving and answered nobody. The scheme in the credential is enough to know
which bus to speak; the nats broker was already written and never chosen.
This commit is contained in:
2026-09-28 02:42:26 +02:00
parent a4447f1251
commit 9e3ff6fa45
+17 -2
View File
@@ -22,7 +22,8 @@
import { readFileSync } from "node:fs"; import { readFileSync } from "node:fs";
import { pathToFileURL } from "node:url"; import { pathToFileURL } from "node:url";
import { connectAmqp, fatalBrokerReason } from "./broker-amqp.js"; import { connectAmqp, fatalBrokerReason as fatalAmqpReason } from "./broker-amqp.js";
import { connectNats, fatalBrokerReason as fatalNatsReason } from "./broker-nats.js";
import type { Credential } from "./broker-amqp.js"; import type { Credential } from "./broker-amqp.js";
import { runTools } from "./runtime.js"; import { runTools } from "./runtime.js";
import { invokeTool } from "@novox/mesh-sdk/tools"; import { invokeTool } from "@novox/mesh-sdk/tools";
@@ -35,6 +36,12 @@ import { emit } from "@novox/mesh-sdk/events";
* over the plain bootstrap URL otherwise. A scoped module assumes the foundation's exchanges exist — * over the plain bootstrap URL otherwise. A scoped module assumes the foundation's exchanges exist —
* its account may not declare them (ADR 0043). * its account may not declare them (ADR 0043).
*/ */
// fatalBrokerReasonFor is the reason a connection failure is final rather than "not yet", for
// whichever bus this runtime is on — each transport knows its own refusals.
function fatalBrokerReasonFor(err: unknown): string | null {
return fatalNatsReason(err) ?? fatalAmqpReason(err);
}
async function connectBroker(): Promise<Broker> { async function connectBroker(): Promise<Broker> {
const file = process.env.MESH_BROKER_FILE; const file = process.env.MESH_BROKER_FILE;
if (file) { if (file) {
@@ -54,6 +61,14 @@ async function connectBroker(): Promise<Broker> {
// what the environment says. // what the environment says.
if (credential.node) process.env.MESH_NODE = credential.node; if (credential.node) process.env.MESH_NODE = credential.node;
if (credential.module) process.env.MESH_MODULE = credential.module; if (credential.module) process.env.MESH_MODULE = credential.module;
// **The credential names the bus.** A module moved to the bus being built was handed a
// credential for it — `nats://…` with user, password and fingerprint beside the address — and
// nothing else in its environment changed (design 25; novox/hq design 28 task 5.2). The scheme
// is enough to know which bus to speak; a runtime that always dialled the old one would keep
// serving and answer nobody.
if (credential.url.startsWith("nats://")) {
return connectNats(credential);
}
return connectAmqp(credential, { assumeExchanges: true }); return connectAmqp(credential, { assumeExchanges: true });
} }
const url = process.env.MESH_BROKER_URL; const url = process.env.MESH_BROKER_URL;
@@ -83,7 +98,7 @@ async function connectBrokerPatiently(): Promise<Broker> {
try { try {
return await connectBroker(); return await connectBroker();
} catch (err) { } catch (err) {
const fatal = fatalBrokerReason(err); const fatal = fatalBrokerReasonFor(err);
if (fatal !== null) { if (fatal !== null) {
console.error(`mesh-tools: ${fatal} — waiting will not fix this; giving up`); console.error(`mesh-tools: ${fatal} — waiting will not fix this; giving up`);
throw err; throw err;