From 1b34821aa000be53e3e63db54a247dfd676ab194 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 3 Oct 2026 22:03:34 +0200 Subject: [PATCH 1/2] ADR 0196: every tool announces itself on the bus in the NATS services protocol --- ...re-found-by-address-not-announced-whole.md | 5 ++ ...n-the-bus-in-the-nats-services-protocol.md | 85 +++++++++++++++++++ 02-DECISIONS/README.md | 1 + 03-DESIGN/01-to-be/34-the-console.md | 6 ++ 4 files changed, 97 insertions(+) create mode 100644 02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md diff --git a/02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md b/02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md index eaf7957..c468a60 100644 --- a/02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md +++ b/02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md @@ -68,6 +68,11 @@ the client reconnecting. The names are the API's kind of name; addresses never h still list it whole, for a person reading it or a client that wants it. An agent pointed at the console sees the five. +> **The mechanism changed — 2026-10-03, by ADR 0196.** Where the console learns what exists: not +> from the catalogue's roster and the controller's printed lists, but from every runtime announcing +> itself on the bus in the NATS services protocol, checked against the controller's records read as +> JSON. The addresses and the five tools stand. + ## Consequences - An agent spends a call or two finding a tool it does not know, and none on one it does; the context diff --git a/02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md b/02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md new file mode 100644 index 0000000..d4cb050 --- /dev/null +++ b/02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md @@ -0,0 +1,85 @@ +--- +topic: what runs on it +status: accepted +date: 2026-10-03 +deciders: jochen +reconstructed: false +extends: 02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md +--- + +# 196. Every tool announces itself on the bus, in the NATS services protocol + +## Context + +[ADR 0195](0195-the-meshs-tools-are-found-by-address-not-announced-whole.md) gave every tool an +address and the console five tools to find them. Where the console learns what exists, it inherited +from [to-be 34](../03-DESIGN/01-to-be/34-the-console.md) §3: ask the catalogue for its roster, ask +each module on the roster for its `tools`, and read the machines and assignments from the +controller's printed `node list` and `module list`. Built that way on 2026-10-03, it worked and showed +what is wrong with it: + +- **It asks what should exist and infers what does.** The roster holds every module the catalogue + ever registered; 47 of them were reported "not answering" on 2026-10-03, most with no tools at all + and several retired. +- **It parses prose.** Two of the controller's answers are text for a person, and a reworded column + breaks discovery. + +The bus already knows what answers. NATS has a services protocol for exactly this: a service answers +`$SRV.PING` and `$SRV.INFO` — every instance, on one request — with its name, its instance, and every +endpoint's subject and metadata, in a published format the NATS tools read. The operator's direction: +*every tool announces itself; the mesh has the full picture, so nothing should be inferred or parsed.* + +## Considered Options + +1. **Keep asking the roster, and give the controller JSON answers.** Fixes the parsing, keeps the + inference. +2. **Re-serve every tool through a NATS services library.** The announcement for free, but every + runtime's serving path rewritten around a library, in two languages, for no change in behaviour. +3. **Every runtime answers the services protocol's discovery subjects with what it serves; serving + is unchanged.** Chosen. + +## Decision + +**1. What answers announces itself.** Every runtime that serves tools — each machine's tool runtime, +the per-module runtimes still in containers, and the controller for the seat it holds — answers +`$SRV.PING` and `$SRV.INFO` in the NATS services format: one service per module or seat it serves, +named for it, its instance the machine; one endpoint per tool, its subject and queue exactly as +served, its metadata the tool's description, argument schema, the machine, the seat and scope where +it is a seat's verb, and whether the module's instances are interchangeable. + +**2. The console finds what exists by asking the bus,** one `$SRV.INFO` request, every answer +gathered for a short window. What it announces through ADR 0195's five tools is what answered. + +**3. What should exist is the mesh's records, read as data.** The controller answers its machines and +modules as JSON, and says which modules declare tools; the console names as *not answering* only an +assignment that declares tools and did not announce them. A module with no tools is never listed. + +**4. The grants say so.** Every principal that serves tools may subscribe the services discovery +subjects for what it serves; the console's and every runtime's account may publish the discovery +request. Replies travel to the asker's own inbox as every reply does. + +## Consequences + +- The standard `nats micro list` and `nats micro info` show the mesh's tools, live, to anybody holding + a credential — the bus's own view, not the mesh's description of it. +- Discovery costs one request and a gathering window, not one request per roster entry. +- What got harder: three runtimes must answer the same format the same way — the Go tool runtime, the + TypeScript runtime the containers still run, and the controller. The format is NATS's, so a test + reads all three with the NATS services client and nothing of the mesh's. + +## How it is checked + +| Rule | Checked by | +|---|---| +| A runtime announces exactly what it serves | each runtime's test: `$SRV.INFO` answered with one service per served module or seat, its endpoints' subjects the subjects served | +| The format is NATS's | the same tests read the answer with the NATS services client's own types | +| A module with no tools is never listed; an assignment with tools that did not answer is | the console's test, against controller records with both | +| Nothing is parsed from prose | the console reads only JSON answers (code review; the text parsers are deleted) | +| Live | `nats micro list` against the mesh's bus lists every machine's tool runtime and the controller | + +## References + +- [ADR 0195](0195-the-meshs-tools-are-found-by-address-not-announced-whole.md), + [ADR 0160](0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md), + [ADR 0175](0175-one-tool-runtime-per-node-serves-every-modules-tools-on-the-host-side.md) +- [to-be 34](../03-DESIGN/01-to-be/34-the-console.md) diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index 04ff2e7..1658378 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -296,6 +296,7 @@ python3 00-META/checks/index.py fail if stale - **0192** — [A tools bundle declares what it is given, and the runtime hands it to that bundle alone](0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md) - **0193** — [Every bundle the runtime serves is launched, and the runtime knows no language](0193-every-bundle-the-runtime-serves-is-launched-and-the-runtime-knows-no-language.md) - **0195** — [The mesh's tools are found by address, not announced whole](0195-the-meshs-tools-are-found-by-address-not-announced-whole.md) +- **0196** — [Every tool announces itself on the bus, in the NATS services protocol](0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md) ### How it is built diff --git a/03-DESIGN/01-to-be/34-the-console.md b/03-DESIGN/01-to-be/34-the-console.md index c34f93f..59f8a5f 100644 --- a/03-DESIGN/01-to-be/34-the-console.md +++ b/03-DESIGN/01-to-be/34-the-console.md @@ -4,6 +4,7 @@ status: designed code: [mesh-catalog, mesh-tools, mesh-controller] updated: 2026-10-03 decisions: + - 02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md - 02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md - 02-DECISIONS/0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md - 02-DECISIONS/0159-a-tool-call-names-the-machine-and-a-holder-serves-its-seats-verbs.md @@ -106,6 +107,11 @@ interchangeable is called with its machine or refused with the machines it runs verb asks the mesh when it is called, so nothing is kept for a session's length; the flat catalogue stays reachable through the `mesh` client and a setting, unannounced. +**What exists is what announced itself** ([ADR 0196](../../02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md)): +every runtime answers the NATS services protocol's `$SRV.INFO` with what it serves, and the console +gathers one request's answers; the controller's records, read as JSON, say which assignments with +tools should have answered. + *Found 2026-10-03, measuring for that record:* §3's statement that a stateful module on two machines is listed once per machine does not hold on the live console — postgres and mssql are listed once, `node` optional, answered by whichever instance replies. The address replaces that statement rather than -- 2.54.0 From fa9e94d863466f47a22e652c5dd0c74e276a0a06 Mon Sep 17 00:00:00 2001 From: jochen Date: Sat, 3 Oct 2026 22:03:58 +0200 Subject: [PATCH 2/2] Renumber to ADR 0197: 0196 landed first on main --- ...he-meshs-tools-are-found-by-address-not-announced-whole.md | 2 +- ...ounces-itself-on-the-bus-in-the-nats-services-protocol.md} | 2 +- 02-DECISIONS/README.md | 2 +- 03-DESIGN/01-to-be/34-the-console.md | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename 02-DECISIONS/{0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md => 0197-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md} (98%) diff --git a/02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md b/02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md index c468a60..fb17d83 100644 --- a/02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md +++ b/02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md @@ -68,7 +68,7 @@ the client reconnecting. The names are the API's kind of name; addresses never h still list it whole, for a person reading it or a client that wants it. An agent pointed at the console sees the five. -> **The mechanism changed — 2026-10-03, by ADR 0196.** Where the console learns what exists: not +> **The mechanism changed — 2026-10-03, by ADR 0197.** Where the console learns what exists: not > from the catalogue's roster and the controller's printed lists, but from every runtime announcing > itself on the bus in the NATS services protocol, checked against the controller's records read as > JSON. The addresses and the five tools stand. diff --git a/02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md b/02-DECISIONS/0197-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md similarity index 98% rename from 02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md rename to 02-DECISIONS/0197-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md index d4cb050..d3ec909 100644 --- a/02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md +++ b/02-DECISIONS/0197-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md @@ -7,7 +7,7 @@ reconstructed: false extends: 02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md --- -# 196. Every tool announces itself on the bus, in the NATS services protocol +# 197. Every tool announces itself on the bus, in the NATS services protocol ## Context diff --git a/02-DECISIONS/README.md b/02-DECISIONS/README.md index 1658378..948c0f6 100644 --- a/02-DECISIONS/README.md +++ b/02-DECISIONS/README.md @@ -296,7 +296,7 @@ python3 00-META/checks/index.py fail if stale - **0192** — [A tools bundle declares what it is given, and the runtime hands it to that bundle alone](0192-a-tools-bundle-declares-what-it-is-given-and-the-runtime-hands-it-to-that-bundle-alone.md) - **0193** — [Every bundle the runtime serves is launched, and the runtime knows no language](0193-every-bundle-the-runtime-serves-is-launched-and-the-runtime-knows-no-language.md) - **0195** — [The mesh's tools are found by address, not announced whole](0195-the-meshs-tools-are-found-by-address-not-announced-whole.md) -- **0196** — [Every tool announces itself on the bus, in the NATS services protocol](0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md) +- **0197** — [Every tool announces itself on the bus, in the NATS services protocol](0197-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md) ### How it is built diff --git a/03-DESIGN/01-to-be/34-the-console.md b/03-DESIGN/01-to-be/34-the-console.md index 59f8a5f..c74355c 100644 --- a/03-DESIGN/01-to-be/34-the-console.md +++ b/03-DESIGN/01-to-be/34-the-console.md @@ -4,7 +4,7 @@ status: designed code: [mesh-catalog, mesh-tools, mesh-controller] updated: 2026-10-03 decisions: - - 02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md + - 02-DECISIONS/0197-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md - 02-DECISIONS/0195-the-meshs-tools-are-found-by-address-not-announced-whole.md - 02-DECISIONS/0160-the-mesh-issues-an-assignments-subjects-and-a-runtime-serves-what-it-is-issued.md - 02-DECISIONS/0159-a-tool-call-names-the-machine-and-a-holder-serves-its-seats-verbs.md @@ -107,7 +107,7 @@ interchangeable is called with its machine or refused with the machines it runs verb asks the mesh when it is called, so nothing is kept for a session's length; the flat catalogue stays reachable through the `mesh` client and a setting, unannounced. -**What exists is what announced itself** ([ADR 0196](../../02-DECISIONS/0196-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md)): +**What exists is what announced itself** ([ADR 0197](../../02-DECISIONS/0197-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md)): every runtime answers the NATS services protocol's `$SRV.INFO` with what it serves, and the console gathers one request's answers; the controller's records, read as JSON, say which assignments with tools should have answered. -- 2.54.0