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

52 lines
3.4 KiB
Markdown

---
layer: as-is
status: implemented
code: [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]
updated: 2026-10-04
decisions:
- 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.