Commit Graph
73 Commits
Author SHA1 Message Date
jschoubben 76902998cd Digest an ask and an act over named fields in a canonical encoding, not a language's encoding of a struct
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group feat/asks-answered-on-any-channel rejected: a member's own check failed
mesh/delivery superseded: a newer head of the same pull request
The review of 2026-10-09: a digest over Go's JSON of a struct changes when a tag is renamed or a field
added, and another language cannot reproduce it. An act is now a flat map of named strings, and both
digests are over length-prefixed values in a fixed order, versioned; a test fails when a field is added
to Ask or Option without being added to the digest.
2026-10-09 11:59:28 +02:00
jschoubben d4077b473e Bind a warrant to the exact ask and each option to the act it performs (hq ADR 0259 §6)
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group feat/asks-answered-on-any-channel rejected: a member's own check failed
mesh/delivery superseded: a newer head of the same pull request
A warrant named the ask by id and the option by id, and the asker acted on whatever its record held
for them. Now the ask carries, for every option above acknowledge, the digest of exactly what it
performs (Option.Binds, ActDigest), the router puts the digest of the ask it took in the warrant
(Warrant.AskDigest), For refuses a warrant whose digest is not that of the ask the asker keeps, and
Option.Performs refuses an act the option did not bind. A warrant then authorises one act, as the
person was shown it, once.
2026-10-09 10:15:03 +02:00
jochen 7c32dfbdf9 Mark a message a channel shows once and keeps no copy of
mesh/delivery-group group feat/asks-answered-on-any-channel rejected: a member's own check failed
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-08 18:39:24 +02:00
jochen 55090da7e0 Give an asking module a client that acts on each warrant once, and tell a lost compare-and-set by its code
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group feat/asks-answered-on-any-channel checking: 3 of 4 member(s) ready
mesh/delivery superseded: a newer head of the same pull request
2026-10-08 18:20:31 +02:00
jochen 62367ce15a Say which linked account a message is for
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-08 16:50:04 +02:00
jochen 2f28404888 Say a channel's failure, its standing, and what a message replies to
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery superseded: a newer head of the same pull request
2026-10-08 16:41:36 +02:00
jochen 3837f86eb6 Read the kinds a module's membership names
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group feat/asks-answered-on-any-channel rejected: a member's own check failed
mesh/delivery superseded: a newer head of the same pull request
2026-10-08 16:37:36 +02:00
jochen 0292772f58 Give modules the seat traffic and the contract to ask a person and answer on a channel (hq ADR 0259)
mesh/merge-gate pass: builds mesh-sdk, sent nowhere; no bus step; every machine composes with the change as it did without (4 of 4 compose)
mesh/repo-check pass: its merge-check.sh passed
mesh/delivery-group group feat/asks-answered-on-any-channel checking: 2 of 3 member(s) ready
mesh/delivery superseded: a newer head of the same pull request
2026-10-08 16:34:16 +02:00
mesh-admin 97f75e5400 Merge pull request 'A merge check of its own (hq ADR 0238)' (#11) from feat/a-merge-check-of-its-own into main 2026-10-06 21:00:37 +00:00
jochen 43b0dde771 Check the SDK's own code before it merges (hq ADR 0237)
A merge-check.sh, the repository's layer of the mesh's merge check (mesh/repo-check), in
the TypeScript toolchain: install from the lock file, type-check, run the suite. The Go,
Python, Rust and C bindings are said as not tested there, never passed silently.
2026-10-06 22:05:24 +02:00
mesh-admin 89da342b9a Merge pull request '0.1.13: one rule for taking an event, and no handler left by a refused subscribe (hq issue 276)' (#10) from fix/an-event-handler-answers-what-it-did into main 2026-10-06 16:17:06 +00:00
jochen ec80dcfc22 0.1.13: one rule for taking an event, and no handler left by a refused subscribe (hq issue 276)
A handler takes an event by returning and asks for it again by throwing;
any mesh/event answer but an error is taken. Written down in the README
where event handling is, with what follows for a handler: throw only when
the work was not done, and be safe to run twice.

Both SDKs added the handler before asking the runtime and kept it when the
runtime refused, so a module that asks again until it is taken (every Go
consumer does) ran its handler once per attempt for every event. A refused
subscribe now removes its handler; one the Go runtime did not answer in
time is kept, since the runtime may have bound it.
2026-10-06 18:14:58 +02:00
mesh-admin a5fc8308d4 Merge pull request '0.1.12: retire a consumer after five stable passes, wait for a person over the bound (hq ADR 0230)' (#9) from feat/retired-consumers into main 2026-10-06 12:51:36 +00:00
jochen 0704908d0b Retire only after ten minutes as well as five passes, and never through remove
Five passes are 25 s, shorter than a real hiccup. And a provider's remove can
destroy — a ledger file, a public DNS record — so an adapter without a retire
of its own is mark-only: the consumer keeps its access, is marked and said, and
remove is reached only by a person's delete (hq ADR 0230).
2026-10-06 14:30:39 +02:00
jochen e0453316b8 0.1.12: retire a consumer the mesh stops asking for, after five stable passes, and wait for a person over the bound
The hourly release let a mistaken bulk unassignment drain one consumer an hour
with nobody deciding. Per novox/hq ADR 0230 a consumer is retired (disabled,
marked, data kept) only after the same unasked set in five consecutive passes;
more than 3, or more than half of those held, waits for approve or reject.
Deletion is a separate act of a person, of a retired consumer only.
2026-10-06 13:47:51 +02:00
mesh-admin a6b7b566b0 Merge pull request '0.1.11: a provisioner brakes a withdrawal larger than its bound (hq to-be 45 Phase 2)' (#8) from feat/a-core-that-cannot-fail-silently-phase-2 into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-06 10:43:12 +00:00
jochen 8d70726038 0.1.11: a provisioner brakes a withdrawal larger than its bound (hq to-be 45 Phase 2)
Issue 241 withdrew seven consumers in one pass; 0.1.10 refuses a file it
cannot read, and a file read whole that names nobody still withdrew
everybody. A pass that would withdraw more than withdrawAtOnce consumers
(1), or more than withdrawFraction (0.5) of those held, now withdraws
nothing and says so; while the mesh goes on not asking, one is let go every
releaseEveryMs (an hour). A consumer asked for again is kept.
2026-10-06 12:15:45 +02:00
jschoubben 1e45f1c95a Merge pull request 'A provisioner removes nothing while its contributions file cannot be used (0.1.10)' (#7) from fix/an-unreadable-grants-file-removes-nothing into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-04 23:31:23 +00:00
jschoubben a5778390b4 0.1.10: a provisioner removes nothing while its contributions file cannot be used 2026-10-05 01:30:52 +02:00
jschoubben 2865d53991 A provisioner removes nothing while its contributions file cannot be used
An unreadable, unparsable or unrecognised contributions file was read as "no consumer asks", and the
reconcile pass then removed every consumer it had applied. On 2026-10-04 one such read made the
postgres provider drop every database on the control node. Such a pass now applies and removes
nothing and says why, once; only a file read with a given list withdraws a consumer, and every
removal is logged before it is made.
2026-10-05 01:28:57 +02:00
mesh-admin c253d76321 Merge pull request 'A secret that will never be readable stops calling itself a race (hq issue 225), 0.1.9' (#22) from fix/a-secret-that-never-becomes-readable-says-so into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-04 10:27:37 +00:00
jschoubben c77fc500f5 A secret that will never be readable stops calling itself a race (hq issue 225)
The harness says 'secret not readable yet' when it cannot open a consumer's
sealed credential, which is true and ordinary on the first pass. It said it
4330 times in three hours on the control machine while two consumers were
never provisioned, because a permission the host will never grant reads
exactly like a file that has not arrived.

After about a minute it says so plainly instead, once and then rarely, and
names what to look at: who owns the file and who this process runs as. The
ownership itself is the controller's to fix.

0.1.9.
2026-10-04 12:13:54 +02:00
mesh-admin 41ec9806db Merge pull request 'Module state is hq ADR 0201 after all' (#21) from fix/module-state-is-0201 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:03:21 +00:00
jochen a75add0d59 Module state is hq ADR 0201 after all: the derived-value record moved to 0202 on hq main 2026-10-04 11:02:45 +02:00
mesh-admin dd3ec8da73 Merge pull request 'Module state is hq ADR 0202 (0201 landed first for a provider's derivations)' (#19) from fix/adr-0202-module-state 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:24 +00:00
mesh-admin f7a3ffe1bf Merge pull request '0.1.8: 0.1.7 was published twice, and the first one won' (#20) from fix/sdk-0-1-8-after-a-version-collision into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-04 02:05:20 +00:00
jschoubben b55b326201 0.1.8: 0.1.7 was published twice, and the first one won
Two pull requests bumped 0.1.6 to 0.1.7 within four minutes of each other —
#18 (a module's state through the runtime) and #10 (a provisioner is told
what the mesh derived). Both built and both published @novox/mesh-sdk@0.1.7,
with different contents. The store kept the first.

So the toolchain installs a 0.1.7 that has no `derived`, and every module
compiled in it fails on a field main's source says is there. minio is the
first to hit it; novox's push is refused because of it.

A version is a name for one set of bytes. This takes the next one.
2026-10-04 04:04:58 +02:00
mesh-admin 9f875b3d5c Merge pull request 'A provisioner is told what the mesh derived for each consumer (hq ADR 0201, issue 124)' (#10) from feat/a-provider-declares-what-it-derives into main
mesh/delivery delivered
2026-10-04 01:47:42 +00:00
jochen d937c7bc2f Module state is hq ADR 0202: 0201 landed first for a provider's derivations 2026-10-04 03:44:56 +02:00
mesh-admin 0677cebb9b Merge pull request 'A module's state through the runtime, in TypeScript and Go (hq ADR 0201)' (#18) from feat/module-state-on-the-bus into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-04 01:43:35 +00:00
jochen 096c5562a4 A module's state through the runtime, in TypeScript and Go (novox/hq ADR 0201)
state(name) / stdio.State(name): get, put, delete, keys and watch over the
runtime's mesh/state verbs. A watch's handler is registered under the id it
is asked with, so its current values — which arrive before the watch is
answered — and its changes reach it and no other watch.
2026-10-04 02:47:31 +02:00
jschoubben f1485257a8 A provisioner is told what the mesh derived for each consumer (hq ADR 0201)
Provision gains `derived`; the harness keeps it per applied login so remove()
is told it too, and includes it in the change hash — a provider that renames
what it derives has given the consumer a different resource.

Version 0.1.7. Rebased onto main after the bundles refactor; the record this
cites was written as 0188 and renumbered to 0201, because ADR 0188 on main is
now the bundles record.
2026-10-04 02:44:50 +02:00
mesh-admin 0b018562e3 Merge pull request 'The Go SDK subscribes and asks through the runtime (hq ADR 0198)' (#17) from feat/go-sdk-subscribes-and-asks 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:20:04 +00:00
jochen 81ca305ddd The Go SDK subscribes and asks through the runtime (hq ADR 0198)
Subscribe(pattern, handler) asks the runtime for the module's events and answers each mesh/event
only after the matching handlers returned nil, so the runtime acknowledges it on the bus then and
not before; Ask(key, body) calls a tool as the module. Topic matching is the TypeScript SDK's.
2026-10-03 23:19:54 +02:00
mesh-admin 7b728a8f9f Merge pull request 'A launched bundle subscribes and asks through its runtime (hq ADR 0198)' (#15) from feat/0198-the-runtime-is-the-bus 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:16:34 +00:00
mesh-admin 7f1bbdd234 Merge pull request 'The Go SDK serves as the module the runtime names, answers calls concurrently, and emits through the runtime (hq ADR 0193)' (#16) from feat/go-sdk-serves-like-the-typescript 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:24:29 +00:00
jochen b95448be60 The Go SDK serves as the module the runtime names, answers calls concurrently, and emits through the runtime (hq ADR 0193)
What the TypeScript SDK's stdio loop does, in Go: an empty name serves as MESH_SERVED_MODULE; each
call runs beside the loop so a tool waiting on the runtime does not deadlock it; Emit asks the
runtime (mesh/publish) with the same envelope and headers. README says how to install it and how
releases are tagged.
2026-10-03 22:24:16 +02:00
jochen 1f936dc4a6 A launched bundle subscribes and asks through its runtime, and its code may do so while being imported (hq ADR 0198)
The stdio channel is the bundle's bus: subscribe asks the runtime (mesh/subscribe), which binds the
module's durable consumer and hands each event over as mesh/event — answered once every matching
handler has taken it, as an error when one failed so the runtime does not acknowledge it; request
asks the runtime (mesh/ask). The session starts as the stdio module loads when the runtime launched
the process, because a handler module subscribes at import, before the launcher says what it
serves; initialize and tools/list are answered once it has. topicMatches as the runtime's. 0.1.6.
2026-10-03 22:23:16 +02:00
mesh-admin 16370de2be Merge pull request 'A launched tool emits through its runtime, and calls run beside the stdio loop (hq ADR 0193)' (#14) from feat/0193-a-launched-bundle-emits-through-the-runtime into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-03 19:12:26 +00:00
jochen fdd5c58a87 A launched tool emits through its runtime, and calls run beside the stdio loop (hq ADR 0193)
A launched bundle holds no bus connection. The stdio loop binds a broker whose publish asks the
runtime (mesh/publish) and resolves when the runtime has published; asking, answering and
subscribing are refused by name. Tool calls run beside the loop, so a tool awaiting the runtime's
answer to its emit does not deadlock the line that carries it. 0.1.5.
2026-10-03 21:12:14 +02:00
mesh-admin 9c2399d595 Merge pull request 'A launched bundle serves under the module the runtime names (hq ADR 0193)' (#13) from feat/0193-served-as-its-module into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-03 19:05:47 +00:00
jochen dc59ec0e1c A launched bundle serves under the module the runtime names (hq ADR 0193)
serveRegisteredOverStdio lists the registrations of the module named by MESH_SERVED_MODULE by
their own names and every other registration as <seat>.<verb>. Before, the first registration
was taken for the module's own, so a bundle registering its seat first listed the seat's verbs as
its own tools. Contributors are given the process's environment, the bundle's own. 0.1.4.
2026-10-03 21:05:38 +02:00
mesh-admin 66a3e223f1 Merge pull request 'collectToolsEach: each registration is given its own environment (hq ADR 0192)' (#12) from feat/0192-each-registration-its-own-env into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-03 13:32:34 +00:00
jochen e63dfc68cd collectToolsEach: each registration is given its own environment (hq ADR 0192)
A node's runtime serves many modules in one process; a module's composed paths and words must
reach its own tools and no other's. The runtime knows which bundle made which registration, so
it says which environment each contributor gets. collectTools(env) is unchanged. 0.1.3.
2026-10-03 15:28:48 +02:00
mesh-admin 67359131e9 Merge pull request 'Cite ADR 0188, not 0187: the record was renumbered before it merged' (#11) from fix/cite-adr-0188 into main
mesh/delivery delivered
2026-10-02 19:29:32 +00:00
jochen 6a1f3fb51f Cite ADR 0188, not 0187: the record was renumbered before it merged (0187 is the dead-tracker record) 2026-10-02 21:29:07 +02:00
mesh-admin e08a10f55f Merge pull request 'A tools bundle speaks MCP over stdio: the loop in TypeScript, and skeleton SDKs for Python, Go, Rust and C (hq ADR 0187)' (#9) from feat/the-operators-machine into main
mesh/delivery held for a person: merged without a passing check: only a person decides that it goes on
2026-10-02 19:27:47 +00:00
jochen 78817c949e A tools bundle speaks MCP over stdio: the loop in TypeScript, and skeleton SDKs for Python, Go, Rust and C (hq ADR 0187)
The node's runtime launches a tools bundle as a process and speaks MCP over stdio to it, so a tool
may be written in any language and the transport stays in the runtime (ADR 0039's refusal, kept).
Each SDK is the loop and the tool type and nothing else: `@novox/mesh-sdk/stdio` (serveStdio, and
serveRegisteredOverStdio for a bundle that already registers the in-process way), python/, go/,
rust/ and c/ beside it, each with an example answering `greet` and the seat verb `node-lamp.on`.
Skeletons, by the operator's direction: the bare minimum one bundle per language needs to be built
and answer, proven here by each example answering tools/list and tools/call on stdio.
2026-10-02 20:34:19 +02:00
jschoubben 163f85ac35 Merge pull request 'The sdk names no transport, and fixtures say what two implementations may not disagree about' (#8) from feat/nats-genesis into main 2026-09-27 17:08:39 +00:00
jschoubben 8f5b786a6b Conformance fixtures: what two implementations may not disagree about
Tasks 3.1 and 3.3 of novox/hq ADR 0116. One fixture directory, read by
every implementation's own runner rather than copied into each — a fixture
copied twice is two fixtures, and two fixtures drift.

The README settles what ADR 0074's byte-for-byte can and cannot mean. The
envelope is exact: the subject, the required headers, each name and format,
because those are what two implementations get wrong invisibly. The body is
not: it is the module's payload, and Go sorts a map's keys where JavaScript
keeps insertion order, so demanding identical bytes would commit every
implementation to a canonical JSON encoder to buy a property the mesh never
uses. Said plainly, because read strictly it would have sent somebody
writing one.
2026-09-26 23:40:59 +02:00