Files
hq/03-DESIGN/00-as-is/14-a-modules-state.md
T
jochen 1faa63b2d5 As-is: a module's state and the agent with its licences; designs 36, 39 and 40 implemented
What runs since 2026-10-04 and what its first live use showed: refused
requests as timeouts, a late machine reading the whole set, the partial
secrets guard; the agent module and the licence manager on every machine.
2026-10-04 16:32:29 +02:00

3.4 KiB

layer, status, code, updated, decisions
layer status code updated decisions
as-is implemented
mesh-controller internal/catalogue/state.go
mesh-controller internal/broker/state.go
mesh-controller cmd/mesh-controller/push.go
mesh-tools node-tools/internal/bus/state.go
mesh-tools node-tools/internal/launch/launch.go
mesh-sdk src/state
mesh-sdk go/state.go
2026-10-04
02-DECISIONS/0201-a-module-keeps-its-current-state-in-key-value-buckets-it-declares-and-reaches-through-the-runtime.md

A module's state, as it runs

A module keeps the current value of something on the bus, and every machine sees it — including one that joins later. Since 2026-10-04 a manifest may say state (buckets the module owns) and reads (another module's, as <module>.<name>). The first two modules to use it are the operator's agent on a machine and its licence manager; on the day this was written, three buckets existed on the bus.

What runs

  • The controller creates a key-value bucket <module>_<name> for every declared state, from the catalogue — on every start and, since the first module that declared state found it missing, on every push before the memberships that name it. A bucket nothing declares any more is reported and kept. Every bucket carries the mesh's caps: 256 KiB a value, 64 MiB a bucket.
  • The grants: the machine's runtime is granted, for each bucket a module it carries owns, writing under the bucket's own subjects and reading; for a bucket it only reads, reading. Measured once built, with the composed grants loaded into a server: a reader's write is refused by the server.
  • The membership issued to each assignment lists its buckets by the names the module uses, and whether it may write.
  • The runtime answers mesh/state.get, put, delete, keys and watch on the bundle's channel. A watch hands the current values — none that is deleted — then every change, each naming the watch it belongs to; it is answered once the current values are delivered. The runtime refuses, with the reason, a state the module was not issued, a reader's write, a key the bus cannot hold, and a value carrying a field named like a credential.
  • The SDKs: state(name) in TypeScript, stdio.State(name) in Go (tag go/v0.1.7 and later).

What the first live use showed

  • A refused request is a timeout, not a refusal. The bus reloads a machine's grants a moment after the push that changed them; a bundle that asks in between waits out its deadline. A module that watches at start therefore watches beside its handshake and asks again until the state answers — the agent module needed two to seven attempts on its first start on each machine.
  • A late machine reads the whole set. A server registered for every machine before one machine was assigned the module reached that machine from the current values at its start.
  • The secrets guard is partial and works for what it covers: an entry carrying an Authorization header was refused on the live bus. A sealed value is plain text to an inspector, and is not caught.

How it is checked

The controller's catalogue and broker tests (names, grants, memberships, a bucket asserted in place against a real server); the runtime's tests over a real bus (current values without deletions, refusals, the TypeScript SDK through the runtime); module check names a read whose owner on the shelf keeps no such state.