--- 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 --- # 197. 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)