A refused announcement is said, never fatal (hq issue 217)
The raw subscription that answers discovery ran its loop unguarded, so a refusal escaped as an unhandled rejection and ended the process: every per-module container crash-looped on 2026-10-03 over a subscription that only serves the mesh seeing the runtime. It is now caught, logged, and the runtime serves on. Tested against a bus whose permissions refuse the subject; fails without it.
This commit is contained in:
@@ -361,10 +361,19 @@ export async function connectNats(
|
|||||||
const sub = conn.subscribe(subject);
|
const sub = conn.subscribe(subject);
|
||||||
subs.push(sub);
|
subs.push(sub);
|
||||||
void (async () => {
|
void (async () => {
|
||||||
|
// **A refusal here is said, never fatal** (novox/hq 04-ISSUES/217). This serves discovery —
|
||||||
|
// what the runtime says about itself — not the work; a bus that refuses it costs the mesh
|
||||||
|
// seeing this runtime, not the runtime's tools and handlers. Unhandled, the refusal ended the
|
||||||
|
// process and every per-module container crash-looped on 2026-10-03.
|
||||||
|
try {
|
||||||
for await (const msg of sub) {
|
for await (const msg of sub) {
|
||||||
const body = answer(msg.subject, msg.data);
|
const body = answer(msg.subject, msg.data);
|
||||||
if (body) msg.respond(body);
|
if (body) msg.respond(body);
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.log(`[mesh-tools] the bus refused ${subject}: ${err instanceof Error ? err.message : String(err)}; ` +
|
||||||
|
"discovery will not see this runtime there, and it serves on");
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
return () => sub.unsubscribe();
|
return () => sub.unsubscribe();
|
||||||
},
|
},
|
||||||
|
|||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
# A bus that refuses one subject: what the mesh's grants do to a subject they do not name (issue 217).
|
||||||
|
authorization {
|
||||||
|
users = [
|
||||||
|
{ user: "runtime", password: "runtime", permissions: {
|
||||||
|
publish: { allow: [">"] }
|
||||||
|
subscribe: { allow: [">"], deny: ["$SRV.PING.>"] }
|
||||||
|
} }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
jetstream: enabled
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* A refused announcement is said and never fatal (novox/hq 04-ISSUES/217): against a bus whose
|
||||||
|
* permissions refuse one discovery subject, the runtime's raw subscription is refused, logged, and
|
||||||
|
* the process keeps serving — its tools still answer.
|
||||||
|
*
|
||||||
|
* docker run -d --rm --name t -p 14233:4222 -v $PWD/test/fixtures/refusing-nats.conf:/c.conf nats:2.10-alpine -c /c.conf
|
||||||
|
* MESH_TEST_REFUSING_NATS=nats://127.0.0.1:14233 node --test --experimental-strip-types test/refused.test.ts
|
||||||
|
*/
|
||||||
|
import assert from "node:assert/strict";
|
||||||
|
import { test } from "node:test";
|
||||||
|
import { connectNats } from "../dist/broker-nats.js";
|
||||||
|
|
||||||
|
const url = process.env.MESH_TEST_REFUSING_NATS;
|
||||||
|
|
||||||
|
test("a refused discovery subscription is logged and the runtime serves on", async (t) => {
|
||||||
|
if (!url) return t.skip("MESH_TEST_REFUSING_NATS unset");
|
||||||
|
const bus = await connectNats({ url, user: "runtime", password: "runtime", module: "alpha", node: "anchor" });
|
||||||
|
const said: string[] = [];
|
||||||
|
const log = console.log;
|
||||||
|
console.log = (...a: unknown[]) => said.push(a.join(" "));
|
||||||
|
const crashed: unknown[] = [];
|
||||||
|
const onRejection = (e: unknown) => crashed.push(e);
|
||||||
|
process.on("unhandledRejection", onRejection);
|
||||||
|
try {
|
||||||
|
(bus as unknown as { raw: (s: string, f: () => Uint8Array | undefined) => () => void }).raw("$SRV.PING.>", () => undefined);
|
||||||
|
const stop = await bus.handle("alpha.ping", async () => ({ pong: true }));
|
||||||
|
for (let i = 0; i < 50 && !said.some((s) => s.includes("the bus refused $SRV.PING.>")); i++) await new Promise((r) => setTimeout(r, 50));
|
||||||
|
console.log = log;
|
||||||
|
assert.ok(said.some((s) => /the bus refused \$SRV\.PING\.>.*serves on/.test(s)), said.join("\n"));
|
||||||
|
assert.equal(crashed.length, 0, `the refusal escaped: ${String(crashed[0])}`);
|
||||||
|
stop();
|
||||||
|
} finally {
|
||||||
|
console.log = log;
|
||||||
|
process.off("unhandledRejection", onRejection);
|
||||||
|
await bus.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user