Files
hq/02-DECISIONS/0197-every-tool-announces-itself-on-the-bus-in-the-nats-services-protocol.md
T

4.8 KiB

topic, status, date, deciders, reconstructed, extends
topic status date deciders reconstructed extends
what runs on it accepted 2026-10-03 jochen false 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 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 §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