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.
52 lines
3.4 KiB
Markdown
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.
|