Commit Graph
60 Commits
Author SHA1 Message Date
jochen d535dd5322 Serve a module's log verbs so its record outlives a bucket's cap and the events' week (ADR 0297, issues 501, 502)
mesh/delivery superseded: a newer head of the same pull request
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
The runtime answers mesh/log.append, read, last and info for a log issued
in the module's membership, by publish, batch direct get and stream info
alone. A stale expect is refused as a lost state update, so a module tells
it by the same code.
2026-10-11 19:18:46 +02:00
jschoubben 541de48e04 node-tools: only a call pattern names a caller, and a fall-back for want of a responder is said (hq issue 365, review)
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery delivered
callerOn reads a caller only under the call kind, so a tool subscription ending
in a wildcard never yields one. A caller-named call that reached nobody is said
once per subject before it is asked on the old subject, so a runtime or grant
that drifted behind is seen rather than a quiet unproven. The caller tests wait
for their bundle's exit, which reads the launcher's restart pauses another test
sets.
2026-10-11 11:22:29 +02:00
jschoubben ab0f888d67 node-tools: a tool call names its caller, and the module is told it from the subject (hq issue 365)
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery-group group fix/365-a-tool-call-names-its-caller delivering: 0 of 4 delivered
mesh/delivery superseded: a newer head of the same pull request
Each tool and seat verb is also served on its caller-named subject, kind call
with the caller's bus user last, which the controller grants each user with its
own name alone; the runtime hands the module that caller in the call's _meta,
never from the body. Calls through the runtime (the console and modules'
invokes) ask the caller-named subject first and the old one only when the first
reached nobody: nothing serves it yet, or the bus refused it because the grant
is not delivered yet, which ends the call at once rather than after 30 s. The
old subjects stay served and say no caller for one release (hq issue 464).
2026-10-11 05:19:08 +02:00
jochen 560876c074 No in-progress after an event is answered (review, hq issue 387)
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
2026-10-10 17:21:54 +02:00
jochen aef908b57b Keep an event alive while its bundle handles it, and hold one at a time (hq issue 387)
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
The consumer's ack wait is 30 seconds and the runtime gives a handler two minutes,
so an event handled longer was offered again while it was handled; events waiting
in the client's buffer ran out their ack wait too, so a burst reached max-deliver
with nothing failed. Say in-progress every 10 seconds, as a worker's work does,
and pull one event at a time.
2026-10-10 17:18:35 +02:00
jochen d0128adf5b Fail the contention test when the two writers never collided: it would prove nothing (the review of 2026-10-10)
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery delivered
mesh/delivery-group group feat/a-counter-by-compare-and-set delivered: every member is delivered
2026-10-10 12:33:43 +02:00
jochen 1eaa1abbe9 Prove a write by compare-and-set loses no update under contention, on a real bus (hq research 042)
mesh/delivery-group group feat/a-counter-by-compare-and-set checking: 0 of 2 member(s) ready
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery superseded: a newer head of the same pull request
The issues module will hand out numbers through mesh/state.create and update. Two writers incrementing one
counter a thousand times each through the module's bus now end at two thousand with every number handed out
once; a stale, zero or future revision is refused with the lost-write code and writes nothing; a deleted key
can be created again; and the lost-write code reaches a bundle wrapped or not, and no other refusal carries it.
Both runtime tests run in the check's bundleless list, so mesh/repo-check runs them.

Part of the issue tracker decision, hq research 042.
2026-10-10 12:31:06 +02:00
jochen 127047edd6 Serve a module from a runtime on its own account instead of switching users, keep bus words from bundles, and never give up a channel's work (hq ADR 0259 revision)
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: THE CHANGE ALTERS ITS OWN CHECK (merge-check.sh): main's version judged it; the change's judges the pull requests after it merges; it…
mesh/delivery delivered
mesh/delivery-group group feat/asks-answered-on-any-channel stopped: a member was stopped
2026-10-09 10:12:39 +02:00
jochen c6087129ce Tell a bundle the kinds its membership names 2026-10-09 10:12:39 +02:00
jochen 7b4428c510 Name the worker a piece of work came from, so a bundle taking two is handed each to its own handler 2026-10-09 10:12:39 +02:00
jochen 6d9906b6c9 Carry a bundle's seat traffic under its own name and kind, and start a module as its own account (hq ADR 0259) 2026-10-09 10:12:39 +02:00
jochen c401bbff2d Say what becomes of an event a handler gives up on (hq issue 330)
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group fix/330-a-message-given-up-on-is-kept delivered: every member is delivered
The comments said a failing event dead-letters, which nothing did; the
controller now keeps it, and delivers it again under a subject this runtime
reads the same key from (ADR 0264).
2026-10-08 17:36:04 +02:00
jochen 90c7871276 Join a paged answer, and say one too large instead of timing out (hq issue 314)
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group fix/314-a-large-answer-is-paged-not-lost delivered: every member is delivered
An answer larger than the bus's max_payload was refused by the client library
inside its holder, and the caller waited out its 30 s and read silence: the
controller's conditions and status for hours on 2026-10-08. The console now asks
for the parts of an answer the controller paged and joins them; a module's answer
too large for one message is answered as an error saying so, at once.
2026-10-08 11:25:50 +02:00
jochen aa209313c2 Rank a search the same whatever order discovery heard things in
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
A tie went to whichever tool discovery listed first, so docker logs found docker_secrets_in_logs
before docker_logs on some runs. A name the query says whole wins, then the shorter name.
2026-10-07 20:27:26 +02:00
jochen 49cb06f6c5 Find a tool by the shell command it replaces, best first (hq ADR 0245)
The agent searched with journalctl, logs and docker ps and found nothing, so it went over ssh. Search
now reads what each verb and tool replaces from the controller's records, matches a command line
against it, ranks the matches, and says the command it matched.
2026-10-07 19:21:13 +02:00
mesh-admin 26a58c7a79 Merge pull request 'Ask a seat once more when the controller refused as handing over (hq issue 289)' (#20) from fix/a-verb-survives-the-handover into main 2026-10-07 00:40:21 +00:00
jochen 2f045e042f Ask a seat once more when the controller refused as handing over (hq issue 289)
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (4 of …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery delivered
mesh/delivery-group group fix/a-verb-survives-the-handover delivered: every member is delivered
A controller being replaced refuses a call it cannot serve, marked retry:
handing-over; the one after it answers the same call.
2026-10-07 02:13:17 +02:00
jochen 0a5dbc7e9a Cite the hq issues by the numbers they were given: 285, 286, 287
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (0 of …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-07 02:00:11 +02:00
jochen dac4d423a8 Reach a seat and a module of one name each: list the seat's verbs, and route a machine's address to the module
mesh/merge-gate pass: builds mesh-tools, node-tools → ace, g14, novox, shanks; no bus step; every machine composes with the change as it did without (0 of …
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
mesh-delivery is the delivery's seat and the module holding it, which answers the seat's verbs with
tools of the same names. Discovery keyed the seat's verbs and the module's tools in one namespace, so
the module's tool took the key and the seat was listed with no verb; and every address with the name
resolved to the seat, so neither the seat's verbs nor the module's tools could be called. The keys are
apart, and `<node>/<module>.<tool>` reaches the module when the module serves it (novox/hq issue 284).
2026-10-07 01:37:46 +02:00
jochen 14bff5dfd7 Check the tool runtime's own code before it merges, every test on a bus of its own (hq ADR 0237)
A merge-check.sh, the repository's layer of the mesh's merge check (mesh/repo-check):
format, vet, and node-tools' suite under the race detector. The tests shared one bus and
had to run one package at a time; like the controller's (#97), each now starts a server
of its own at the release go.mod pins, held to the catalogue's bus image by a test. What
cannot run in the check — bundles that need @novox/mesh-sdk — is said as not tested.
2026-10-06 22:04:46 +02:00
jochen 1792b0d9ba node-tools: say whose words a refused event is (hq issue 276)
"plex did not take radarr.download.completed: Unexpected end of JSON input"
read as the runtime failing to parse the bundle's answer. It was plex's own
handler error, relayed. A bundle's error answer is now a launch.Refused, and
the line says the handler answered an error, quotes it, names the event id
and the delay before the next offer; the runtime's own failures (no answer
in time, bundle exited) are said as before.

The rule, unchanged and now tested: any mesh/event answer that is not an
error takes the event, whatever its result says, including none.
2026-10-06 18:14:50 +02:00
jochen 0307df0850 Say the node tools are there, every minute (hq to-be 45 Phase 1, S11)
A machine whose node-engine is heard and whose runtime is gone is a
machine nobody can ask anything, and nothing said so. The runtime now
says on mesh.control.<node>.tools-alive, every minute, that it is there,
with its interval and build; the controller raises tools-silent after
three missed. Core NATS, like the host's heartbeat: a lost one is the
next one. A heartbeat the bus refuses is logged when that starts and
when it stops.
2026-10-06 10:06:29 +02:00
jochen fce3dcb98d Say a timeout is not a failure, and where the controller keeps the answer (hq issue 265) 2026-10-06 01:14:59 +02:00
jochen 3a7c9645e0 Keep a mesh seat's node argument, and refuse what a call would drop
The console took node out of every schema and every call, so the
controller's push, plan, assign, pin and settings were described without
the machine and called without it: a push naming one machine ran as a
push of every machine behind (hq issue 244). node is now taken out only
where the address names the machine, a different machine there is
refused, and an argument a seat's verb does not declare is refused.
2026-10-06 00:37:15 +02:00
jochen 52a0cc4fcb Forget a runtime's old instance once it answers under a new one, so a restart is not reported as a missed answer 2026-10-05 15:38:30 +02:00
jochen 379d19d907 Wait for every runtime discovery hears from, and name the ones it misses
The console gathered $SRV.INFO answers for a fixed 750 ms. The laptop's
runtime (48 modules, 341 endpoints, 164 kB) answers last every time: its
answer crosses to the broker on another machine and back, a median of
365 ms on a quiet link and 813 ms in one of 25 rounds measured. When it
missed the window the console said its modules ran nowhere ("nothing in
the mesh is called slack") or only on another machine.

Discovery now asks $SRV.PING alongside $SRV.INFO and waits, past the
window and up to 5 s, for every instance that answered PING. A runtime
that never sends what it serves, or answered before and not now, is
named in every answer that might concern it instead of the module being
called missing. An answer still too large after first-line descriptions
drops them, and says so in its metadata.

mesh_runtimes reports, per runtime, its machine, how long its answer
took, its size, its modules and tools, whether it was shortened and when
it was last heard, and the runtimes and machines not heard.
2026-10-05 15:36:10 +02:00
jochen b3ebdd5edd Keep a machine's discovery answer under the bus's message limit
Every tool's schema in one $SRV.INFO reply outgrew max_payload on machines
serving 137-206 tools, and the refused reply was dropped silently, so search
and a machine's view went empty. Module tools now announce without schema;
describe and tools/list ask the module for it. An answer still too large has
its descriptions cut to a line, and a failed reply is logged.
2026-10-04 17:06:20 +02:00
jochen 8405e32efc The console says an account was refused only when the bus refused it
"authorization" alone matched a tool's own answer mentioning the word — the
runtime refusing a state value with an Authorization header read as
"this account may not call".
2026-10-04 11:31:42 +02:00
jochen 779ea67ea6 Module state is hq ADR 0201 after all: the derived-value record moved to 0202 on hq main 2026-10-04 11:02:43 +02:00
mesh-admin b149e9fcd6 Merge pull request 'node-tools provides its endpoint at node scope (hq to-be 40 WP1)' (#34) from feat/claude-code-agent into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-04 09:01:26 +00:00
jochen 70ff0f84af Module state is hq ADR 0202: 0201 landed first for a provider's derivations 2026-10-04 03:44:51 +02:00
jochen 29c24a2367 The TypeScript SDK's state reaches the runtime, a narrowed watch only its keys (novox/hq ADR 0201) 2026-10-04 02:48:10 +02:00
jochen 1adfcad88e Name the watch a state change is for (novox/hq ADR 0201) 2026-10-04 02:45:31 +02:00
jochen dac8812968 gofmt 2026-10-04 02:45:10 +02:00
jochen 6cba894f01 The runtime serves a bundle its module's state (novox/hq ADR 0201)
mesh/state.get, put, delete, keys and watch on the stdio channel, from the
buckets the membership issues. A watch hands the current values without
deletions, then every change, and is answered once the current values are
delivered. Refused with the reason: state not issued, a reader's write, a
value with a credential-named field — the bus alone would answer a refused
write with a timeout.
2026-10-04 02:45:07 +02:00
jochen 85810ccc8c Ask the bus again for a subscription it refused (novox/hq issue 222)
A push sends the machine that needs a grant and the bus's machine in the same breath, and the
runtime can subscribe the moment before the bus reloads its user list. Refused once, the
subscription stayed dead until some later membership re-served it, and a newly assigned module ran
unreachable. A refused subject the runtime answers is now asked for again for about five minutes.
2026-10-04 01:20:44 +02:00
jochen 6604d44372 A refused tool subscription is said, never fatal (novox/hq issue 218)
After the controller stopped granting a mesh seat to claimants that do not hold it, an image built
before the runtime followed its membership still subscribed the seat's subject, and the refusal
ended the process: ace's postgres runtime crash-looped. A subject the grants leave out now costs
that subject only, as an announcement's already did (issue 217).
2026-10-04 00:13:15 +02:00
jochen 0cea8d286e Serve a seat's verbs from the membership once one is issued (novox/hq issue 218)
The runtime added every seat its start-up credential claims even after the mesh issued a
membership without it. A seat held once for the mesh is claimed on every machine running the
module, so ace's postgres announced the store seat the bus then refused it on.
2026-10-03 23:48:15 +02:00
mesh-admin 094a7d0d7c Merge pull request 'The toolchain carries the SDK the mesh last published, and esbuild (hq issue 212, ADR 0193)' (#44) from feat/the-toolchain-follows-the-sdk-and-bundles into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-03 21:31:59 +00:00
jochen b52669577d The toolchain carries the SDK the mesh last published, and esbuild (hq issue 212, ADR 0193)
mesh-tools stands on mesh-sdk's published package: the build receives its exact version and
installs it after the package.json install, so a release is a new argument and the cached layer
cannot keep an older SDK; the planner orders the toolchain after the SDK, and every bundle after the
toolchain (issue 211). esbuild, a development dependency, is what the builder bundles each
TypeScript entrypoint and launcher into one file with.
2026-10-03 23:26:09 +02:00
jochen 6ba0f4dc1e 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.
2026-10-03 23:24:38 +02:00
jochen 4af59636c3 A mesh seat's holder answers for the machine it runs on (hq ADR 0197)
The controller announces the mesh-controller seat without a machine — the seat is the mesh's — and
the console then reported it as not answering on the machine it is assigned to. An announcement that
names no machine now answers for wherever its module is assigned.
2026-10-03 23:22:56 +02:00
jochen 14b6588839 Merge remote-tracking branch 'origin/main' into feat/0198-the-runtime-is-the-bus 2026-10-03 23:16:43 +02:00
jochen f11ac6441c Announce only on the subjects the grants allow (hq ADR 0197)
Both runtimes subscribed $SRV.<verb>.> 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.<verb>, $SRV.<verb>.<name> and $SRV.<verb>.<name>.<id>.
2026-10-03 22:31:02 +02:00
jochen ffe229308c The node's runtime is its modules' bus: it binds each module's consumer and hands its events to the bundle (hq ADR 0198)
A launched bundle's mesh/subscribe binds the module's own durable consumer — EVENTS, <node>_<module>,
by name as the module's own runtime bound it, so nothing is lost or replayed in the move — and every
event goes to each child of the module that subscribed as mesh/event, acknowledged only when all
answered, negatively acknowledged after a short delay when one failed or died, terminated when it is
not an event. mesh/ask calls a tool as the module. Every launched bundle is started again when it
exits, with backoff, since long-running code waits for no call. Requires SDK 0.1.6.
2026-10-03 22:29:20 +02:00
jochen 66e8be0e31 The TypeScript runtime announces what it serves too, in the same services format (hq ADR 0197)
The per-module containers still run this runtime; their tools and the seats they hold (the store's,
the catalogue's) must be found by the console the same way as the node runtime's. It answers
$SRV.PING, $SRV.INFO and $SRV.STATS with one service per process, one endpoint per tool per subject
and per seat verb served, the metadata as the Go runtime writes it.
2026-10-03 22:15:48 +02:00
jochen 7722668220 Every runtime announces what it serves in the NATS services protocol; the console discovers by asking the bus (hq ADR 0197)
The Go runtime answers $SRV.PING, $SRV.INFO and $SRV.STATS (and per name and id) in the
io.nats.micro.v1 format with what it serves at the moment it is asked: one service per runtime
process, since the bus admits one reply per request from each responder, and one endpoint per tool
per subject, its metadata saying module, seat, scope, machine, description, schema and whether the
module is interchangeable. Serving is unchanged.

The console gathers one $SRV.INFO request's answers instead of asking the catalogue's roster and
each module's tools, and reads the controller's records as JSON for what should have answered: an
assignment with tools that did not announce is named, a module without tools never is. The text
parsers of node list and module list are gone. Packages share the test bus: go test -p 1.
2026-10-03 22:14:16 +02:00
jochen e8989f3cf5 Merge remote-tracking branch 'origin/main' into feat/0197-tools-announce-themselves 2026-10-03 22:08:06 +02:00
jochen 486dad99a5 Merge remote-tracking branch 'origin/main' into feat/0193-node-tools-in-go 2026-10-03 22:02:03 +02:00
mesh-admin 65f3b68076 Merge pull request 'node-tools launches every bundle it serves; a child's emit is published as its module (hq ADR 0193)' (#36) from feat/0193-the-runtime-launches-every-bundle into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-03 20:01:58 +00:00