Merge pull request 'The runtime speaks the bus its credential names' (#14) from feat/the-runtime-speaks-the-bus-its-credential-names into main

This commit was merged in pull request #14.
This commit is contained in:
2026-09-28 00:42:29 +00:00
+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;