From f11ac6441c23d0db415bcfbb44a0cd5e2703c1bc Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 3 Oct 2026 22:31:02 +0200 Subject: [PATCH] Announce only on the subjects the grants allow (hq ADR 0197) Both runtimes subscribed $SRV..> as a wildcard; the grants allow the bare question and the service's own name and instance. The bus refused the wildcard, and the TypeScript runtime treats a refused subscription as fatal, so every per-module container crash-looped after the image rolled. They now subscribe exactly $SRV., $SRV.. and $SRV.... --- node-tools/internal/announce/announce.go | 4 +++- node-tools/src/announce.ts | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/node-tools/internal/announce/announce.go b/node-tools/internal/announce/announce.go index c9f7f97..247e35c 100644 --- a/node-tools/internal/announce/announce.go +++ b/node-tools/internal/announce/announce.go @@ -158,7 +158,9 @@ func Serve(conn *bus.Conn, s Service, current func() []Endpoint) (func(), error) } var stops []func() for _, verb := range []string{"PING", "INFO", "STATS"} { - for _, subject := range []string{"$SRV." + verb, "$SRV." + verb + ".>"} { + // Exactly the questions asked of every service and of this one by name and instance — what the + // grants allow (novox/hq ADR 0197). A wildcard is refused by the bus. + for _, subject := range []string{"$SRV." + verb, "$SRV." + verb + "." + s.Name, "$SRV." + verb + "." + s.Name + "." + s.ID} { stop, err := conn.Raw(subject, answer) if err != nil { for _, st := range stops { diff --git a/node-tools/src/announce.ts b/node-tools/src/announce.ts index a152a51..6641d11 100644 --- a/node-tools/src/announce.ts +++ b/node-tools/src/announce.ts @@ -127,8 +127,13 @@ export function announce(broker: RuntimeBroker, s: Service, current: () => Endpo return sc.encode(JSON.stringify(v)); }; const stops: (() => void)[] = []; + // Exactly the questions asked of every service and of this one by name and instance — what the + // grants allow (novox/hq ADR 0197). A wildcard is refused by the bus, and a refused subscription + // ends a runtime: on 2026-10-03 it crash-looped every container that announced itself. for (const verb of ["PING", "INFO", "STATS"]) { - for (const subject of [`$SRV.${verb}`, `$SRV.${verb}.>`]) stops.push(broker.raw!(subject, (subj) => answer(subj))); + for (const subject of [`$SRV.${verb}`, `$SRV.${verb}.${s.name}`, `$SRV.${verb}.${s.name}.${s.id}`]) { + stops.push(broker.raw!(subject, (subj) => answer(subj))); + } } return () => stops.forEach((stop) => stop()); }