C14: the generated Go and TypeScript of every seat follow the definitions

Written by seatgen at c62650e4 (GEN3) with mesh-sdk b19fc7fa (reader release): the seat mesh-grants new,
every TypeScript contract as index.js with index.d.ts, events with subject and stream. mesh-watchdogs gains
its family, which a definition with events needs. Written with the reader as it is: the field needs-approval
(node-login-shell) and the section bodies (mesh-conditions) are unknown to it, so they were laid aside for
the run; nothing generated carries either.

Builds: design 47 G §339, §341, §261, §389

Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
This commit is contained in:
2026-11-11 10:45:30 +01:00
parent 44492ce555
commit e529c372d0
68 changed files with 2951 additions and 969 deletions
+128 -3
View File
@@ -1,6 +1,6 @@
// Code generated by seatgen from the seat definitions; DO NOT EDIT.
// Package nodeengine is the contract of the seat "node-engine", defined whole (seats/node-engine/seat.json): The node seat every node's engine holds (novox/hq ADR 0563, ADR 0618 §3, §4): its hand acts on the operator's warrant, its read-only judgements, its compositions and what it reads of a manifest. Each verb is answered on this node's subject, mesh.seat.node-engine.tool.<verb>.<node>, and each event is published on mesh.seat.node-engine.event.<event>.<node>, the node's name its last token.
// Package nodeengine is the contract of the seat "node-engine", defined whole (seats/node-engine/seat.json): The node seat every node's engine holds (novox/hq ADR 0563, ADR 0618 §3, §4): its hand acts on the operator's warrant, its read-only judgements, its compositions and what it reads of a manifest. Each verb is answered on this node's subject, mesh.seat.node-engine.tool.<verb>.<node>, and each event is published on mesh.seat.node-engine.event.<event>.<node>, the node's name its last token, or, for an event whose subject names <node>, there (composed.<node>, running.<node>.<module>): the grants part grants a node's account only its own node's subjects (ADR 0363 §2).
package nodeengine
import (
@@ -28,6 +28,7 @@ const (
VerbManifestReads = "manifest-reads"
VerbSeatList = "seat-list"
VerbSeatListAgain = "seat-list-again"
VerbProcessBuilds = "process-builds"
)
const (
@@ -35,16 +36,19 @@ const (
EventDone = "done"
EventRefused = "refused"
EventManifestReadsChanged = "manifest-reads-changed"
EventComposed = "composed"
EventRunning = "running"
EventCapabilitiesChanged = "capabilities-changed"
)
// Verbs is every verb the seat serves, in its definition's order: a new copy each call.
func Verbs() []string {
return []string{VerbHandOver, VerbAccessAct, VerbJudgeDirectories, VerbSetuidSearch, VerbRootFree, VerbActs, VerbPackages, VerbComposition, VerbCompose, VerbComposeCandidate, VerbManifestReads, VerbSeatList, VerbSeatListAgain}
return []string{VerbHandOver, VerbAccessAct, VerbJudgeDirectories, VerbSetuidSearch, VerbRootFree, VerbActs, VerbPackages, VerbComposition, VerbCompose, VerbComposeCandidate, VerbManifestReads, VerbSeatList, VerbSeatListAgain, VerbProcessBuilds}
}
// Events is every event published on the seat's subject: a new copy each call.
func Events() []string {
return []string{EventAsked, EventDone, EventRefused, EventManifestReadsChanged}
return []string{EventAsked, EventDone, EventRefused, EventManifestReadsChanged, EventComposed, EventRunning, EventCapabilitiesChanged}
}
// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects.
@@ -67,6 +71,31 @@ func SubjectManifestReadsChanged(caller string) string {
return "mesh.seat.node-engine.event.manifest-reads-changed." + caller
}
// SubjectComposed is where composed is published, on mesh.seat.node-engine.event.composed.<node>.
func SubjectComposed(node string) string { return "mesh.seat.node-engine.event.composed." + node }
// SubjectComposedAll hears every composed.
const SubjectComposedAll = "mesh.seat.node-engine.event.composed.*"
// StreamRunning is the stream running is kept in.
const StreamRunning = "SEAT_node-engine_running"
// SubjectRunning is where running is published, on mesh.seat.node-engine.event.running.<node>.<module>.
func SubjectRunning(node, module string) string {
return "mesh.seat.node-engine.event.running." + node + "." + module
}
// SubjectRunningAll hears every running.
const SubjectRunningAll = "mesh.seat.node-engine.event.running.*.*"
// SubjectCapabilitiesChanged is where capabilities-changed is published, on mesh.seat.node-engine.event.capabilities-changed.<node>.
func SubjectCapabilitiesChanged(node string) string {
return "mesh.seat.node-engine.event.capabilities-changed." + node
}
// SubjectCapabilitiesChangedAll hears every capabilities-changed.
const SubjectCapabilitiesChangedAll = "mesh.seat.node-engine.event.capabilities-changed.*"
const (
StateHave = "have"
StateComposed = "composed"
@@ -523,6 +552,34 @@ type CandidateRefusal struct {
Why string `json:"why"`
}
// CapabilitiesChanged is a node's capabilities changed, which the assigner places by.
type CapabilitiesChanged struct {
// when, RFC 3339
At time.Time `json:"at"`
// its capabilities now
Capabilities []string `json:"capabilities"`
// the node
Node string `json:"node"`
// the layout's version
V int64 `json:"v"`
}
// Composed is an entry composed on a node (design 47 *What each step says*).
type Composed struct {
// when, RFC 3339
At time.Time `json:"at"`
// the digest of the composition (design 47 G 404)
Digest string `json:"digest"`
// the fingerprint of the want composed
Fingerprint string `json:"fingerprint"`
// the module
Module string `json:"module"`
// the node
Node string `json:"node"`
// the layout's version
V int64 `json:"v"`
}
// DeclaredGroup is a group.
type DeclaredGroup struct {
Gid int64 `json:"gid"`
@@ -742,6 +799,38 @@ type PlacedDataItem struct {
Protection string `json:"protection,omitempty"`
}
// ProcessBuild is the build one process of a module was started with.
type ProcessBuild struct {
// the fingerprint handed to it in MESH_FINGERPRINT; empty when it was started without one
Fingerprint string `json:"fingerprint,omitempty"`
// service or container
Kind string `json:"kind"`
// the module
Module string `json:"module"`
// the service's unit or the container's name
Process string `json:"process"`
// when it was started, RFC 3339
Started *time.Time `json:"started,omitempty"`
// why its build is unknown: started before the node-engine handed MESH_FINGERPRINT, or the value could not be read
Unknown string `json:"unknown,omitempty"`
}
// The values of ProcessBuild's kind.
const (
ProcessBuildKindService = "service"
ProcessBuildKindContainer = "container"
)
// ProcessBuilds is what process-builds answers.
type ProcessBuilds struct {
// why they could not be read
Error string `json:"error,omitempty"`
// the node
Node string `json:"node"`
// every process the node-engine started, by module
Processes []ProcessBuild `json:"processes"`
}
// PullWait is the pull from a store a failed apply waits on.
type PullWait struct {
// when the pull began
@@ -796,6 +885,24 @@ type RootVerdict struct {
Why string `json:"why"`
}
// Running is a module running healthy on a node at a build: what the node-engines of other nodes and the grants part decide a seat's pass from (ADR 0660 §2), kept last per subject with no age limit.
type Running struct {
// when it was seen running healthy, RFC 3339
At time.Time `json:"at"`
// the fingerprint of the build it runs; none with stopped
Fingerprint string `json:"fingerprint,omitempty"`
// the digest of that build's manifest, the indexer's manifest of it, read by digest from the catalog half
Manifest string `json:"manifest,omitempty"`
// the module
Module string `json:"module"`
// the node
Node string `json:"node"`
// true when the module no longer runs on the node: no fingerprint or manifest then (design 47 G 365, 404); mesh-sdk's SeatsToPassOn counts such a module as not running there
Stopped bool `json:"stopped,omitempty"`
// the layout's version
V int64 `json:"v"`
}
// Said is the answer of a read-only judgement.
type Said struct {
// the node
@@ -1217,6 +1324,15 @@ type SeatListAgainInput struct {
// SeatListAgainAnswer is what seat-list-again answers.
type SeatListAgainAnswer = SeatList
// ProcessBuildsInput is what process-builds takes.
type ProcessBuildsInput struct {
// only this module's processes
Module string `json:"module,omitempty"`
}
// ProcessBuildsAnswer is what process-builds answers.
type ProcessBuildsAnswer = ProcessBuilds
// AskedEvent is the body of the event asked.
type AskedEvent = ActSaid
@@ -1229,6 +1345,15 @@ type RefusedEvent = ActSaid
// ManifestReadsChangedEvent is the body of the event manifest-reads-changed.
type ManifestReadsChangedEvent = ManifestReadsChanged
// ComposedEvent is the body of the event composed.
type ComposedEvent = Composed
// RunningEvent is the body of the event running.
type RunningEvent = Running
// CapabilitiesChangedEvent is the body of the event capabilities-changed.
type CapabilitiesChangedEvent = CapabilitiesChanged
// HaveByNodeModuleValue is one value of the state have under the key <node>.<module>.
type HaveByNodeModuleValue struct {
Build string `json:"build,omitempty"`
+18
View File
@@ -35,6 +35,18 @@ func TestFixtures(t *testing.T) {
err := json.Unmarshal(b, &v)
return v, err
}, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.manifest-reads-changed\\.[A-Za-z0-9_-]+$")},
{name: "composed is said", subject: "mesh.seat.node-engine.event.composed.ace", given: "{\"at\":\"2026-11-09T20:00:00Z\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"module\":\"nats\",\"node\":\"ace\",\"v\":1}", wire: "{\"at\":\"2026-11-09T20:00:00Z\",\"digest\":\"sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb\",\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"module\":\"nats\",\"node\":\"ace\",\"v\":1}",
decode: func(b []byte) (any, error) { var v ComposedEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.composed\\.[^.*> ]+$")},
{name: "running is said, its node and module in its subject", subject: "mesh.seat.node-engine.event.running.ace.nats", given: "{\"at\":\"2026-11-09T20:01:00Z\",\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"manifest\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"module\":\"nats\",\"node\":\"ace\",\"v\":1}", wire: "{\"at\":\"2026-11-09T20:01:00Z\",\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"manifest\":\"sha256:cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccc\",\"module\":\"nats\",\"node\":\"ace\",\"v\":1}",
decode: func(b []byte) (any, error) { var v RunningEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.running\\.[^.*> ]+\\.[^.*> ]+$")},
{name: "running says a module stopped on the node, with no fingerprint", subject: "mesh.seat.node-engine.event.running.ace.nats", given: "{\"at\":\"2026-11-11T09:00:00Z\",\"module\":\"nats\",\"node\":\"ace\",\"stopped\":true,\"v\":1}", wire: "{\"at\":\"2026-11-11T09:00:00Z\",\"module\":\"nats\",\"node\":\"ace\",\"stopped\":true,\"v\":1}",
decode: func(b []byte) (any, error) { var v RunningEvent; err := json.Unmarshal(b, &v); return v, err }, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.running\\.[^.*> ]+\\.[^.*> ]+$")},
{name: "capabilities-changed is said", subject: "mesh.seat.node-engine.event.capabilities-changed.ace", given: "{\"at\":\"2026-11-09T20:02:00Z\",\"capabilities\":[\"build-go\",\"docker\"],\"node\":\"ace\",\"v\":1}", wire: "{\"at\":\"2026-11-09T20:02:00Z\",\"capabilities\":[\"build-go\",\"docker\"],\"node\":\"ace\",\"v\":1}",
decode: func(b []byte) (any, error) {
var v CapabilitiesChangedEvent
err := json.Unmarshal(b, &v)
return v, err
}, subjectIs: regexp.MustCompile("^mesh\\.seat\\.node-engine\\.event\\.capabilities-changed\\.[^.*> ]+$")},
{name: "the finding of a seat list unread, as the engine writes it", subject: "", given: "{\"at\":\"2026-11-05T14:00:00Z\",\"key\":\"machine.ace.seat-list-unread\",\"kind\":\"seat-list-unread\",\"observer\":\"mesh-host\",\"severity\":\"warning\",\"source\":\"node-engine.seat-list\",\"subject\":{\"id\":\"ace\",\"machine\":\"ace\",\"scope\":\"machine\"},\"summary\":\"the seat list has not been read for five minutes\",\"v\":1}", wire: "{\"at\":\"2026-11-05T14:00:00Z\",\"key\":\"machine.ace.seat-list-unread\",\"kind\":\"seat-list-unread\",\"observer\":\"mesh-host\",\"severity\":\"warning\",\"source\":\"node-engine.seat-list\",\"subject\":{\"id\":\"ace\",\"machine\":\"ace\",\"scope\":\"machine\"},\"summary\":\"the seat list has not been read for five minutes\",\"v\":1}",
decode: func(b []byte) (any, error) { var v FindingsByKeyValue; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "a node's packages, present and absent (captured from ace, shortened)", subject: "", given: "{\"node\":\"ace\",\"packages\":[{\"absent\":false,\"modules\":[\"avahi\"],\"package\":\"avahi\"},{\"absent\":false,\"modules\":[\"mesh-build-go\",\"mesh-build-node\"],\"package\":\"bubblewrap\"},{\"absent\":true,\"modules\":[\"time-sync\"],\"package\":\"ntp\"}]}", wire: "{\"node\":\"ace\",\"packages\":[{\"absent\":false,\"modules\":[\"avahi\"],\"package\":\"avahi\"},{\"absent\":false,\"modules\":[\"mesh-build-go\",\"mesh-build-node\"],\"package\":\"bubblewrap\"},{\"absent\":true,\"modules\":[\"time-sync\"],\"package\":\"ntp\"}]}",
@@ -91,6 +103,12 @@ func TestFixtures(t *testing.T) {
decode: func(b []byte) (any, error) { var v SeatListAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "seat-list-again answers the error when the read failed", subject: "", given: "{\"error\":\"seatlist: no answer\",\"health\":\"the seat list is held from the bootstrap\",\"status\":{\"heard\":false,\"held\":[{\"seat\":\"node-engine\",\"source\":\"bootstrap\"}],\"waiting\":[\"node-engine\"]}}", wire: "{\"error\":\"seatlist: no answer\",\"health\":\"the seat list is held from the bootstrap\",\"status\":{\"heard\":false,\"held\":[{\"seat\":\"node-engine\",\"source\":\"bootstrap\"}],\"waiting\":[\"node-engine\"]}}",
decode: func(b []byte) (any, error) { var v SeatListAgainAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "process-builds asks one module", subject: "", given: "{\"module\":\"nats\"}", wire: "{\"module\":\"nats\"}",
decode: func(b []byte) (any, error) { var v ProcessBuildsInput; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "process-builds answers each process's build", subject: "", given: "{\"node\":\"ace\",\"processes\":[{\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"kind\":\"service\",\"module\":\"nats\",\"process\":\"nats.service\",\"started\":\"2026-11-09T19:00:00Z\"},{\"kind\":\"container\",\"module\":\"route-proxy\",\"process\":\"route-proxy\",\"started\":\"2026-11-01T08:00:00Z\",\"unknown\":\"started before the node-engine handed MESH_FINGERPRINT\"}]}", wire: "{\"node\":\"ace\",\"processes\":[{\"fingerprint\":\"src2:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\",\"kind\":\"service\",\"module\":\"nats\",\"process\":\"nats.service\",\"started\":\"2026-11-09T19:00:00Z\"},{\"kind\":\"container\",\"module\":\"route-proxy\",\"process\":\"route-proxy\",\"started\":\"2026-11-01T08:00:00Z\",\"unknown\":\"started before the node-engine handed MESH_FINGERPRINT\"}]}",
decode: func(b []byte) (any, error) { var v ProcessBuildsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
{name: "process-builds answers an error", subject: "", given: "{\"error\":\"the service manager did not answer\",\"node\":\"ace\",\"processes\":[]}", wire: "{\"error\":\"the service manager did not answer\",\"node\":\"ace\",\"processes\":[]}",
decode: func(b []byte) (any, error) { var v ProcessBuildsAnswer; err := json.Unmarshal(b, &v); return v, err }, subjectIs: nil},
}
for _, c := range cases {
v, err := c.decode([]byte(c.given))