From 211bdcaa5ece175170e02f0d325dbcc99f6b5b9f Mon Sep 17 00:00:00 2001 From: jochen Date: Thu, 5 Nov 2026 13:19:37 +0100 Subject: [PATCH] Regenerate the domain contracts; hand-acts and conditions clients; operator-channel client moves; the seat-set package ends (ADR 0634, ADR 0635; issue 1966) Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- go/go.mod | 3 - go/seats/meshconfig/contract.go | 68 + go/seats/nodeengine/contract.go | 324 + go/seats/operatorchannel/go.mod | 3 - go/seats/seats.go | 765 -- .../go/clients/meshconditionsclient/client.go | 172 + .../meshconditionsclient/client_test.go | 62 + .../go/clients/meshconditionsclient/go.mod | 5 + .../go/clients/meshhandactsclient/client.go | 88 + .../clients/meshhandactsclient/client_test.go | 73 + .../go/clients/meshhandactsclient/go.mod | 5 + .../go/seats/meshconditions/contract.go | 384 + .../go/seats/meshconditions/go.mod | 3 + .../go/seats/meshhandacts/contract.go | 222 + .../go/seats/meshhandacts/go.mod | 3 + .../go/seats/meshhealers/contract.go | 197 + health-and-repair/go/seats/meshhealers/go.mod | 3 + .../go/seats/meshselfcheck/contract.go | 101 + .../go/seats/meshselfcheck/go.mod | 3 + .../go/seats/meshwatchdogs/contract.go | 266 + .../go/seats/meshwatchdogs/go.mod | 3 + health-and-repair/seats/index.json | 10 + .../ts/seats/meshconditions/package.json | 19 + .../ts/seats/meshconditions/src/index.ts | 280 + .../ts/seats/meshconditions}/tsconfig.json | 0 .../ts/seats/meshhandacts/package.json | 19 + .../ts/seats/meshhandacts/src/index.ts | 160 + .../ts/seats/meshhandacts}/tsconfig.json | 0 .../ts/seats/meshhealers}/package.json | 4 +- .../ts/seats/meshhealers/src/index.ts | 127 + .../ts/seats/meshhealers/tsconfig.json | 14 + .../ts/seats/meshselfcheck/package.json | 19 + .../ts/seats/meshselfcheck/src/index.ts | 70 + .../ts/seats/meshselfcheck/tsconfig.json | 14 + .../ts/seats/meshwatchdogs/package.json | 19 + .../ts/seats/meshwatchdogs/src/index.ts | 182 + .../ts/seats/meshwatchdogs/tsconfig.json | 14 + .../go}/clients/operatorchannelclient/ask.go | 2 +- .../clients/operatorchannelclient/ask_test.go | 2 +- .../clients/operatorchannelclient/client.go | 2 +- .../operatorchannelclient/client_test.go | 2 +- .../operatorchannelclient/condition.go | 2 +- .../operatorchannelclient/condition_test.go | 2 +- .../go}/clients/operatorchannelclient/go.mod | 5 +- .../go}/clients/operatorchannelclient/go.sum | 0 .../go}/seats/operatorchannel/contract.go | 49 +- .../seats/operatorchannel/contract_test.go | 0 .../go/seats/operatorchannel/go.mod | 3 + operator-and-conversation/seats/index.json | 6 + .../ts}/seats/operatorchannel/package.json | 2 +- .../ts}/seats/operatorchannel/src/index.ts | 27 +- .../ts/seats/operatorchannel/tsconfig.json | 14 + .../go/seats/meshprovisioner/contract.go | 285 + provisioning/go/seats/meshprovisioner/go.mod | 3 + provisioning/seats/index.json | 9 + .../ts/seats/meshprovisioner/package.json | 19 + .../ts/seats/meshprovisioner/src/index.ts | 188 + .../ts/seats/meshprovisioner/tsconfig.json | 14 + ts/seats/meshconfig/src/index.ts | 41 + ts/seats/nodeengine/src/index.ts | 215 + ts/src/index.ts | 3 - ts/src/seats.ts | 8185 ----------------- 62 files changed, 3808 insertions(+), 8976 deletions(-) delete mode 100644 go/go.mod delete mode 100644 go/seats/operatorchannel/go.mod delete mode 100644 go/seats/seats.go create mode 100644 health-and-repair/go/clients/meshconditionsclient/client.go create mode 100644 health-and-repair/go/clients/meshconditionsclient/client_test.go create mode 100644 health-and-repair/go/clients/meshconditionsclient/go.mod create mode 100644 health-and-repair/go/clients/meshhandactsclient/client.go create mode 100644 health-and-repair/go/clients/meshhandactsclient/client_test.go create mode 100644 health-and-repair/go/clients/meshhandactsclient/go.mod create mode 100644 health-and-repair/go/seats/meshconditions/contract.go create mode 100644 health-and-repair/go/seats/meshconditions/go.mod create mode 100644 health-and-repair/go/seats/meshhandacts/contract.go create mode 100644 health-and-repair/go/seats/meshhandacts/go.mod create mode 100644 health-and-repair/go/seats/meshhealers/contract.go create mode 100644 health-and-repair/go/seats/meshhealers/go.mod create mode 100644 health-and-repair/go/seats/meshselfcheck/contract.go create mode 100644 health-and-repair/go/seats/meshselfcheck/go.mod create mode 100644 health-and-repair/go/seats/meshwatchdogs/contract.go create mode 100644 health-and-repair/go/seats/meshwatchdogs/go.mod create mode 100644 health-and-repair/seats/index.json create mode 100644 health-and-repair/ts/seats/meshconditions/package.json create mode 100644 health-and-repair/ts/seats/meshconditions/src/index.ts rename {ts/seats/operatorchannel => health-and-repair/ts/seats/meshconditions}/tsconfig.json (100%) create mode 100644 health-and-repair/ts/seats/meshhandacts/package.json create mode 100644 health-and-repair/ts/seats/meshhandacts/src/index.ts rename {ts => health-and-repair/ts/seats/meshhandacts}/tsconfig.json (100%) rename {ts => health-and-repair/ts/seats/meshhealers}/package.json (60%) create mode 100644 health-and-repair/ts/seats/meshhealers/src/index.ts create mode 100644 health-and-repair/ts/seats/meshhealers/tsconfig.json create mode 100644 health-and-repair/ts/seats/meshselfcheck/package.json create mode 100644 health-and-repair/ts/seats/meshselfcheck/src/index.ts create mode 100644 health-and-repair/ts/seats/meshselfcheck/tsconfig.json create mode 100644 health-and-repair/ts/seats/meshwatchdogs/package.json create mode 100644 health-and-repair/ts/seats/meshwatchdogs/src/index.ts create mode 100644 health-and-repair/ts/seats/meshwatchdogs/tsconfig.json rename {go => operator-and-conversation/go}/clients/operatorchannelclient/ask.go (99%) rename {go => operator-and-conversation/go}/clients/operatorchannelclient/ask_test.go (99%) rename {go => operator-and-conversation/go}/clients/operatorchannelclient/client.go (98%) rename {go => operator-and-conversation/go}/clients/operatorchannelclient/client_test.go (98%) rename {go => operator-and-conversation/go}/clients/operatorchannelclient/condition.go (97%) rename {go => operator-and-conversation/go}/clients/operatorchannelclient/condition_test.go (98%) rename {go => operator-and-conversation/go}/clients/operatorchannelclient/go.mod (59%) rename {go => operator-and-conversation/go}/clients/operatorchannelclient/go.sum (100%) rename {go => operator-and-conversation/go}/seats/operatorchannel/contract.go (87%) rename {go => operator-and-conversation/go}/seats/operatorchannel/contract_test.go (100%) create mode 100644 operator-and-conversation/go/seats/operatorchannel/go.mod create mode 100644 operator-and-conversation/seats/index.json rename {ts => operator-and-conversation/ts}/seats/operatorchannel/package.json (80%) rename {ts => operator-and-conversation/ts}/seats/operatorchannel/src/index.ts (89%) create mode 100644 operator-and-conversation/ts/seats/operatorchannel/tsconfig.json create mode 100644 provisioning/go/seats/meshprovisioner/contract.go create mode 100644 provisioning/go/seats/meshprovisioner/go.mod create mode 100644 provisioning/seats/index.json create mode 100644 provisioning/ts/seats/meshprovisioner/package.json create mode 100644 provisioning/ts/seats/meshprovisioner/src/index.ts create mode 100644 provisioning/ts/seats/meshprovisioner/tsconfig.json delete mode 100644 ts/src/index.ts delete mode 100644 ts/src/seats.ts diff --git a/go/go.mod b/go/go.mod deleted file mode 100644 index efb2a0d..0000000 --- a/go/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module git.novox.be/novox/mesh-contracts/go - -go 1.26.0 diff --git a/go/seats/meshconfig/contract.go b/go/seats/meshconfig/contract.go index 0ab1684..3c126d1 100644 --- a/go/seats/meshconfig/contract.go +++ b/go/seats/meshconfig/contract.go @@ -3,6 +3,13 @@ // Package meshconfig is the contract of the seat "mesh-config", defined whole (seats/mesh-config/seat.json): The mesh's config values: read and set one, and the renames still open. mesh-config's own events stay under its own subjects (ADR 0620 §2). package meshconfig +import ( + "bytes" + "encoding/json" + "errors" + "strings" +) + // Seat is the seat's name. const Seat = "mesh-config" @@ -38,6 +45,53 @@ func HolderSubjectRetiring(holder string) string { return "mesh.stream." + holde // HolderSubjectRetired is where the holder publishes retired, kept in config.events. func HolderSubjectRetired(holder string) string { return "mesh.stream." + holder + ".event.retired" } +const ( + StateResolved = "resolved" +) + +// ResolvedByNodeModuleKeyKey is the key of one value of the state resolved under the form "..". +func ResolvedByNodeModuleKeyKey(node, module, key string) string { + return node + "." + module + "." + key +} + +// ParseResolvedByNodeModuleKeyKey reads the parts of a key of the form "..", in the order of the form; the last result is false for a key of another form. +func ParseResolvedByNodeModuleKeyKey(key string) (string, string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", "", false + } + rest = after0 + v2, after2, found2 := strings.Cut(rest, ".") + if !found2 || v2 == "" { + return "", "", "", false + } + rest = after2 + if rest == "" { + return "", "", "", false + } + v4 := rest + rest = "" + if rest != "" { + return "", "", "", false + } + return v0, v2, v4, true +} + +// DecodeResolvedByNodeModuleKeyValue reads one value of the state resolved under the form "..", refusing a field ResolvedByNodeModuleKeyValue does not know and any text after the value. +func DecodeResolvedByNodeModuleKeyValue(raw []byte) (ResolvedByNodeModuleKeyValue, error) { + var v ResolvedByNodeModuleKeyValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + // ConfigInput is what config takes. type ConfigInput struct { // list (when not given), set, clear or history @@ -129,3 +183,17 @@ type RetiredHolderEvent struct { // the node Node string `json:"node,omitempty"` } + +// ResolvedByNodeModuleKeyValue is one value of the state resolved under the key ... +type ResolvedByNodeModuleKeyValue struct { + Key string `json:"key"` + Module string `json:"module"` + Node string `json:"node"` + // the predefined key the value falls back to + Predefined string `json:"predefined,omitempty"` + // an unset value is the operator's own to set + Required bool `json:"required,omitempty"` + Source string `json:"source"` + Trusted bool `json:"trusted,omitempty"` + Value any `json:"value,omitempty"` +} diff --git a/go/seats/nodeengine/contract.go b/go/seats/nodeengine/contract.go index 8e6a947..b06da58 100644 --- a/go/seats/nodeengine/contract.go +++ b/go/seats/nodeengine/contract.go @@ -3,6 +3,13 @@ // 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.., and each event is published on mesh.seat.node-engine.event.., the node's name its last token. package nodeengine +import ( + "bytes" + "encoding/json" + "errors" + "strings" +) + // Seat is the seat's name. const Seat = "node-engine" @@ -57,6 +64,222 @@ func SubjectManifestReadsChanged(caller string) string { return "mesh.seat.node-engine.event.manifest-reads-changed." + caller } +const ( + StateHave = "have" + StateComposed = "composed" + StateData = "data" + StatePackages = "packages" + StateHealth = "health" +) + +// HaveByNodeModuleKey is the key of one value of the state have under the form ".". +func HaveByNodeModuleKey(node, module string) string { return node + "." + module } + +// ParseHaveByNodeModuleKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. +func ParseHaveByNodeModuleKey(key string) (string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", false + } + rest = after0 + if rest == "" { + return "", "", false + } + v2 := rest + rest = "" + if rest != "" { + return "", "", false + } + return v0, v2, true +} + +// DecodeHaveByNodeModuleValue reads one value of the state have under the form ".", refusing a field HaveByNodeModuleValue does not know and any text after the value. +func DecodeHaveByNodeModuleValue(raw []byte) (HaveByNodeModuleValue, error) { + var v HaveByNodeModuleValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// HaveByNodeNodeKey is the key of one value of the state have under the form ".node". +func HaveByNodeNodeKey(node string) string { return node + ".node" } + +// ParseHaveByNodeNodeKey reads the parts of a key of the form ".node", in the order of the form; the last result is false for a key of another form. +func ParseHaveByNodeNodeKey(key string) (string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".node") + if !found0 || v0 == "" { + return "", false + } + rest = after0 + if rest != "" { + return "", false + } + return v0, true +} + +// DecodeHaveByNodeNodeValue reads one value of the state have under the form ".node", refusing a field HaveByNodeNodeValue does not know and any text after the value. +func DecodeHaveByNodeNodeValue(raw []byte) (HaveByNodeNodeValue, error) { + var v HaveByNodeNodeValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// ComposedByNodeModuleFingerprintKey is the key of one value of the state composed under the form "..". +func ComposedByNodeModuleFingerprintKey(node, module, fingerprint string) string { + return node + "." + module + "." + fingerprint +} + +// ParseComposedByNodeModuleFingerprintKey reads the parts of a key of the form "..", in the order of the form; the last result is false for a key of another form. +func ParseComposedByNodeModuleFingerprintKey(key string) (string, string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", "", false + } + rest = after0 + v2, after2, found2 := strings.Cut(rest, ".") + if !found2 || v2 == "" { + return "", "", "", false + } + rest = after2 + if rest == "" { + return "", "", "", false + } + v4 := rest + rest = "" + if rest != "" { + return "", "", "", false + } + return v0, v2, v4, true +} + +// DecodeComposedByNodeModuleFingerprintValue reads one value of the state composed under the form "..", refusing a field ComposedByNodeModuleFingerprintValue does not know and any text after the value. +func DecodeComposedByNodeModuleFingerprintValue(raw []byte) (ComposedByNodeModuleFingerprintValue, error) { + var v ComposedByNodeModuleFingerprintValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// DataByNodeModuleKey is the key of one value of the state data under the form ".". +func DataByNodeModuleKey(node, module string) string { return node + "." + module } + +// ParseDataByNodeModuleKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. +func ParseDataByNodeModuleKey(key string) (string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", false + } + rest = after0 + if rest == "" { + return "", "", false + } + v2 := rest + rest = "" + if rest != "" { + return "", "", false + } + return v0, v2, true +} + +// DecodeDataByNodeModuleValue reads one value of the state data under the form ".", refusing a field DataByNodeModuleValue does not know and any text after the value. +func DecodeDataByNodeModuleValue(raw []byte) (DataByNodeModuleValue, error) { + var v DataByNodeModuleValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// PackagesByNodeKey is the key of one value of the state packages under the form "". +func PackagesByNodeKey(node string) string { return node } + +// ParsePackagesByNodeKey reads the parts of a key of the form "", in the order of the form; the last result is false for a key of another form. +func ParsePackagesByNodeKey(key string) (string, bool) { + rest := key + if rest == "" { + return "", false + } + v0 := rest + rest = "" + if rest != "" { + return "", false + } + return v0, true +} + +// DecodePackagesByNodeValue reads one value of the state packages under the form "", refusing a field PackagesByNodeValue does not know and any text after the value. +func DecodePackagesByNodeValue(raw []byte) (PackagesByNodeValue, error) { + var v PackagesByNodeValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// HealthByNodeKey is the key of one value of the state health under the form "". +func HealthByNodeKey(node string) string { return node } + +// ParseHealthByNodeKey reads the parts of a key of the form "", in the order of the form; the last result is false for a key of another form. +func ParseHealthByNodeKey(key string) (string, bool) { + rest := key + if rest == "" { + return "", false + } + v0 := rest + rest = "" + if rest != "" { + return "", false + } + return v0, true +} + +// DecodeHealthByNodeValue reads one value of the state health under the form "", refusing a field HealthByNodeValue does not know and any text after the value. +func DecodeHealthByNodeValue(raw []byte) (HealthByNodeValue, error) { + var v HealthByNodeValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + // Act is one act asked through the seat, as acts reads it (ADR 0563 §9). type Act struct { // the act: hand-over, access-act, judge-directories or setuid-search @@ -561,3 +784,104 @@ type RefusedEvent = ActSaid // ManifestReadsChangedEvent is the body of the event manifest-reads-changed. type ManifestReadsChangedEvent = ManifestReadsChanged + +// HaveByNodeModuleValue is one value of the state have under the key .. +type HaveByNodeModuleValue struct { + Build string `json:"build,omitempty"` + BuildAlone bool `json:"build-alone,omitempty"` + // the composition's digest + Composed string `json:"composed,omitempty"` + Detail string `json:"detail,omitempty"` + // the fingerprint held + Fp string `json:"fp,omitempty"` + // the accounts handed + Handed map[string]any `json:"handed,omitempty"` + // how it is + Health string `json:"health"` + Judgement string `json:"judgement,omitempty"` + Kept []string `json:"kept,omitempty"` + // how far it has come + Phase string `json:"phase"` + // a pull the engine waits for + PullWait map[string]any `json:"pull-wait,omitempty"` + Sealed []string `json:"sealed,omitempty"` + SealedAt map[string]any `json:"sealed-at,omitempty"` + // the user list written + UserList map[string]any `json:"user-list,omitempty"` + V int64 `json:"v"` + // what it waits for + Waits string `json:"waits,omitempty"` +} + +// HaveByNodeNodeValue is one value of the state have under the key .node. +type HaveByNodeNodeValue struct { + Busy map[string]any `json:"busy,omitempty"` + Candidates map[string]map[string]any `json:"candidates,omitempty"` + Composed string `json:"composed,omitempty"` + // own, or the controller's + Composes string `json:"composes,omitempty"` + ComposesWhy string `json:"composes-why,omitempty"` + Filters map[string]any `json:"filters,omitempty"` + Profile map[string]any `json:"profile,omitempty"` + // the node's record: its tunnel and certificate + Record map[string]any `json:"record,omitempty"` + Sealed []string `json:"sealed,omitempty"` + SealedAt map[string]any `json:"sealed-at,omitempty"` + Strays map[string]any `json:"strays,omitempty"` + V int64 `json:"v"` + Windows map[string]any `json:"windows,omitempty"` +} + +// ComposedByNodeModuleFingerprintValue is one value of the state composed under the key ... +type ComposedByNodeModuleFingerprintValue struct { + Data bool `json:"Data,omitempty"` + FP string `json:"FP"` + Filled []string `json:"Filled,omitempty"` + Module string `json:"Module"` + Node string `json:"Node"` + // the node's own adoption, identities, groups, libraries and provisioner account + Own map[string]any `json:"Own,omitempty"` + // the module's plan + Plan map[string]any `json:"Plan,omitempty"` + // why the composition was refused + Refused string `json:"Refused,omitempty"` + Resources []map[string]any `json:"Resources,omitempty"` + V int64 `json:"V,omitempty"` +} + +// DataByNodeModuleValue is one value of the state data under the key .. +type DataByNodeModuleValue struct { + Digest string `json:"digest"` + Items []map[string]any `json:"items"` + Module string `json:"module"` + Node string `json:"node"` + V int64 `json:"v,omitempty"` +} + +// PackagesByNodeValuePackagesItem is a type of the contract. +type PackagesByNodeValuePackagesItem struct { + Absent bool `json:"absent,omitempty"` + Modules []string `json:"modules,omitempty"` + Package string `json:"package,omitempty"` +} + +// PackagesByNodeValue is one value of the state packages under the key . +type PackagesByNodeValue struct { + Node string `json:"node"` + Packages []PackagesByNodeValuePackagesItem `json:"packages"` +} + +// HealthByNodeValue is one value of the state health under the key . +type HealthByNodeValue struct { + At string `json:"at"` + Contract int64 `json:"contract,omitempty"` + Fallback map[string]any `json:"fallback,omitempty"` + Found []map[string]any `json:"found,omitempty"` + Network map[string]any `json:"network,omitempty"` + Node string `json:"node"` + Peers []map[string]any `json:"peers,omitempty"` + Relogin []map[string]any `json:"relogin,omitempty"` + Root []map[string]any `json:"root,omitempty"` + Units map[string]any `json:"units,omitempty"` + V int64 `json:"v"` +} diff --git a/go/seats/operatorchannel/go.mod b/go/seats/operatorchannel/go.mod deleted file mode 100644 index 894ee5e..0000000 --- a/go/seats/operatorchannel/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module git.novox.be/novox/mesh-contracts/go/seats/operatorchannel - -go 1.26.0 diff --git a/go/seats/seats.go b/go/seats/seats.go deleted file mode 100644 index a545cdd..0000000 --- a/go/seats/seats.go +++ /dev/null @@ -1,765 +0,0 @@ -// Code generated by seatgen from the seat definitions; DO NOT EDIT. - -// Package seats is the mesh's seat list, generated from every seat's definition (design 26 *The seat set has one -// source*): what each seat is, where it is held, the provision it delivers, its verbs with their inputs, what it -// accepts and emits, what it receives and the paths it owns. mesh-sdk's runtime reads it; a seat its holder declares -// itself has a name here and no entry in the list. -package seats - -// Seat is one role the mesh defines. -type Seat struct { - Name string - Scope string - Grain string - Replicated bool - Delivers string - Accepts []string - Emits []string - PulledByKind bool - Yields bool - Serves []Verb - Receives []Receivable - Paths []Path - HeldByTheEngine bool - Decision string -} - -// Verb is one verb a seat serves: its input and answer as JSON Schema. -type Verb struct { - Name string - Description string - Input map[string]any - Output map[string]any - Optional bool - Replaces []string -} - -// Receivable is one kind of contribution a seat receives. -type Receivable struct { - Kind string - Comment string - Dirs bool - Shape *Shape - Offered bool -} - -// Shape is a kind given as data. Its checks are the runtime's. -type Shape struct { - Places []PlaceField - Order string - Examples []map[string]any - Text bool -} - -// PlaceField is one field a holder places by, and the values it takes. -type PlaceField struct { - Field string - Values []string -} - -// Path is one path on its machine a seat's holder owns. -type Path struct { - Path string - StepsBackFor string -} - -// The name of every seat defined. -const ( - MeshStore = "mesh-store" - MeshBroker = "mesh-broker" - MeshVault = "mesh-vault" - MeshConfig = "mesh-config" - MeshProvisioner = "mesh-provisioner" - MeshArtifactStore = "mesh-artifact-store" - MeshCatalog = "mesh-catalog" - MeshDelivery = "mesh-delivery" - MeshPlacement = "mesh-placement" - MeshDeliveryChecker = "mesh-delivery-checker" - MeshConditions = "mesh-conditions" - MeshWatchdogs = "mesh-watchdogs" - MeshSelfCheck = "mesh-self-check" - MeshHealers = "mesh-healers" - MeshWants = "mesh-wants" - MeshHandActs = "mesh-hand-acts" - MeshEnrolment = "mesh-enrolment" - MeshArtifactSweep = "mesh-artifact-sweep" - OperatorChannel = "operator-channel" - NpmPackageRegistry = "npm-package-registry" - GoPackageRegistry = "go-package-registry" - ContainerImageRegistry = "container-image-registry" - OciArtifactRegistry = "oci-artifact-registry" - Git = "git" - NodeBuildGo = "node-build-go" - NodeBuildNode = "node-build-node" - NodeBuildImage = "node-build-image" - MeshDnsResolver = "mesh-dns-resolver" - NodeHostname = "node-hostname" - NodeIntrusionPrevention = "node-intrusion-prevention" - NodePacketFilter = "node-packet-filter" - NodeServiceManager = "node-service-manager" - NodePackageManager = "node-package-manager" - NodeContainerRuntime = "node-container-runtime" - NodeEnvironment = "node-environment" - NodeProvisioner = "node-provisioner" - NodeLoginShell = "node-login-shell" - NodePower = "node-power" - NodeHotkeys = "node-hotkeys" - NodeMessageBus = "node-message-bus" - NodeBackup = "node-backup" - ThePrivateNetwork = "the-private-network" - NodeUplink = "node-uplink" - NodeResolver = "node-resolver" - NodeNfsServer = "node-nfs-server" - NodeMounts = "node-mounts" - NodeReads = "node-reads" - NodeFiles = "node-files" - NodeEngine = "node-engine" - NodeLoginManager = "node-login-manager" - NodeDisplayServer = "node-display-server" - NodeDisplaySession = "node-display-session" - NodeTerminalEmulator = "node-terminal-emulator" - NodeLauncher = "node-launcher" - NodeNotifier = "node-notifier" - NodeLockScreen = "node-lock-screen" - NodeClipboard = "node-clipboard" - NodeBar = "node-bar" - NodeCompositor = "node-compositor" - NodeSecretService = "node-secret-service" -) - -// Names is every seat defined, in reading order, the ones their holders declare included. -func Names() []string { - return []string{MeshStore, MeshBroker, MeshVault, MeshConfig, MeshProvisioner, MeshArtifactStore, MeshCatalog, MeshDelivery, MeshPlacement, MeshDeliveryChecker, MeshConditions, MeshWatchdogs, MeshSelfCheck, MeshHealers, MeshWants, MeshHandActs, MeshEnrolment, MeshArtifactSweep, OperatorChannel, NpmPackageRegistry, GoPackageRegistry, ContainerImageRegistry, OciArtifactRegistry, Git, NodeBuildGo, NodeBuildNode, NodeBuildImage, MeshDnsResolver, NodeHostname, NodeIntrusionPrevention, NodePacketFilter, NodeServiceManager, NodePackageManager, NodeContainerRuntime, NodeEnvironment, NodeProvisioner, NodeLoginShell, NodePower, NodeHotkeys, NodeMessageBus, NodeBackup, ThePrivateNetwork, NodeUplink, NodeResolver, NodeNfsServer, NodeMounts, NodeReads, NodeFiles, NodeEngine, NodeLoginManager, NodeDisplayServer, NodeDisplaySession, NodeTerminalEmulator, NodeLauncher, NodeNotifier, NodeLockScreen, NodeClipboard, NodeBar, NodeCompositor, NodeSecretService} -} - -// List is the seat list, in reading order: a new copy each call. -func List() []Seat { - return []Seat{ - {Name: MeshStore, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0079", Delivers: "postgres-database", - Serves: []Verb{ - {Name: "databases", Description: "Every database the store holds, with its on-disk size.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, Replaces: []string{"psql -l"}}, - {Name: "query", Description: "One read-only statement against one database the store holds.", - Input: map[string]any{"properties": map[string]any{"database": map[string]any{"description": "the database to query", "type": "string"}, "sql": map[string]any{"description": "the read-only statement", "type": "string"}}, "required": []string{"database", "sql"}, "type": "object"}, Replaces: []string{"psql"}}, - }}, - {Name: MeshBroker, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0079", Delivers: "mesh-bus", Emits: []string{"created", "refused", "drifted", "drift-cleared", "full", "full-cleared", "orphaned", "recreated", "dropped"}, - Serves: []Verb{ - {Name: "list", Description: "Every entry the bus module created, its template, its settings and its fill against its cap.", Optional: true, - Input: map[string]any{"properties": map[string]any{"module": map[string]any{"description": "one module (optional)", "type": "string"}}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"entries": map[string]any{"description": "every entry", "items": map[string]any{"description": "one entry as the bus holds it", "properties": map[string]any{"bus-name": map[string]any{"description": "its name on the bus, _", "type": "string"}, "configuration": map[string]any{"description": "its own configuration, where no template fits", "type": "object"}, "fill": map[string]any{"properties": map[string]any{"bytes": map[string]any{"description": "bytes held", "type": "integer"}, "cap": map[string]any{"description": "its cap in bytes", "type": "integer"}, "count": map[string]any{"description": "messages, keys or objects held", "type": "integer"}}, "required": []string{"count", "bytes", "cap"}, "type": "object"}, "local-name": map[string]any{"description": "its local name in the module's manifest", "type": "string"}, "module": map[string]any{"description": "the module asking it", "type": "string"}, "options": map[string]any{"description": "the template's options the module set, within their bounds", "type": "object"}, "settings": map[string]any{"description": "the settings the bus holds", "type": "object"}, "state": map[string]any{"description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", "enum": []string{"held", "refused", "waiting", "drifted", "full", "orphaned"}, "type": "string"}, "storage-type": map[string]any{"description": "how the bus keeps it", "enum": []string{"stream", "key-value", "object-store", "ledger"}, "type": "string"}, "template": map[string]any{"description": "its template; empty for a configuration of its own", "enum": []string{"state", "events", "work-queue", "log", "objects", "ledger", ""}, "type": "string"}, "why": map[string]any{"description": "why it is in its state, in plain words; empty when held as asked", "type": "string"}}, "required": []string{"module", "local-name", "storage-type", "bus-name"}, "type": "object"}, "type": "array"}}, "required": []string{"entries"}, "type": "object"}}, - {Name: "show", Description: "One entry, its template, settings and fill.", Optional: true, - Input: map[string]any{"properties": map[string]any{"local-name": map[string]any{"description": "the entry's local name", "type": "string"}, "module": map[string]any{"description": "the module", "type": "string"}}, "required": []string{"module", "local-name"}, "type": "object"}, - Output: map[string]any{"description": "one entry as the bus holds it", "properties": map[string]any{"bus-name": map[string]any{"description": "its name on the bus, _", "type": "string"}, "configuration": map[string]any{"description": "its own configuration, where no template fits", "type": "object"}, "fill": map[string]any{"properties": map[string]any{"bytes": map[string]any{"description": "bytes held", "type": "integer"}, "cap": map[string]any{"description": "its cap in bytes", "type": "integer"}, "count": map[string]any{"description": "messages, keys or objects held", "type": "integer"}}, "required": []string{"count", "bytes", "cap"}, "type": "object"}, "local-name": map[string]any{"description": "its local name in the module's manifest", "type": "string"}, "module": map[string]any{"description": "the module asking it", "type": "string"}, "options": map[string]any{"description": "the template's options the module set, within their bounds", "type": "object"}, "settings": map[string]any{"description": "the settings the bus holds", "type": "object"}, "state": map[string]any{"description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", "enum": []string{"held", "refused", "waiting", "drifted", "full", "orphaned"}, "type": "string"}, "storage-type": map[string]any{"description": "how the bus keeps it", "enum": []string{"stream", "key-value", "object-store", "ledger"}, "type": "string"}, "template": map[string]any{"description": "its template; empty for a configuration of its own", "enum": []string{"state", "events", "work-queue", "log", "objects", "ledger", ""}, "type": "string"}, "why": map[string]any{"description": "why it is in its state, in plain words; empty when held as asked", "type": "string"}}, "required": []string{"module", "local-name", "storage-type", "bus-name"}, "type": "object"}}, - {Name: "check", Description: "Compare what the bus holds with what was asked, entry by entry; a difference that only grows is said as such.", Optional: true, - Input: map[string]any{"properties": map[string]any{"module": map[string]any{"description": "one module (optional)", "type": "string"}}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"differences": map[string]any{"description": "every difference", "items": map[string]any{"properties": map[string]any{"asked": map[string]any{"description": "asked", "type": "object"}, "grows": map[string]any{"description": "the difference only grows: apply may act on it", "type": "boolean"}, "held": map[string]any{"description": "held", "type": "object"}, "local-name": map[string]any{"description": "the entry", "type": "string"}, "module": map[string]any{"description": "the module", "type": "string"}}, "required": []string{"module", "local-name"}, "type": "object"}, "type": "array"}}, "required": []string{"differences"}, "type": "object"}}, - {Name: "orphans", Description: "Entries the bus holds that no manifest asks for; never one whose manifest the judge refused or outgrew, nor one of the foundation's old sections.", Optional: true, - Input: map[string]any{"description": "no arguments", "properties": map[string]any{}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"entries": map[string]any{"description": "every orphan", "items": map[string]any{"description": "one entry as the bus holds it", "properties": map[string]any{"bus-name": map[string]any{"description": "its name on the bus, _", "type": "string"}, "configuration": map[string]any{"description": "its own configuration, where no template fits", "type": "object"}, "fill": map[string]any{"properties": map[string]any{"bytes": map[string]any{"description": "bytes held", "type": "integer"}, "cap": map[string]any{"description": "its cap in bytes", "type": "integer"}, "count": map[string]any{"description": "messages, keys or objects held", "type": "integer"}}, "required": []string{"count", "bytes", "cap"}, "type": "object"}, "local-name": map[string]any{"description": "its local name in the module's manifest", "type": "string"}, "module": map[string]any{"description": "the module asking it", "type": "string"}, "options": map[string]any{"description": "the template's options the module set, within their bounds", "type": "object"}, "settings": map[string]any{"description": "the settings the bus holds", "type": "object"}, "state": map[string]any{"description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", "enum": []string{"held", "refused", "waiting", "drifted", "full", "orphaned"}, "type": "string"}, "storage-type": map[string]any{"description": "how the bus keeps it", "enum": []string{"stream", "key-value", "object-store", "ledger"}, "type": "string"}, "template": map[string]any{"description": "its template; empty for a configuration of its own", "enum": []string{"state", "events", "work-queue", "log", "objects", "ledger", ""}, "type": "string"}, "why": map[string]any{"description": "why it is in its state, in plain words; empty when held as asked", "type": "string"}}, "required": []string{"module", "local-name", "storage-type", "bus-name"}, "type": "object"}, "type": "array"}}, "required": []string{"entries"}, "type": "object"}}, - {Name: "create", Description: "Create one entry now.", Optional: true, - Input: map[string]any{"description": "one piece of bus state as a manifest asks it", "properties": map[string]any{"configuration": map[string]any{"description": "its own configuration, where no template fits", "type": "object"}, "local-name": map[string]any{"description": "its local name in the module's manifest", "type": "string"}, "module": map[string]any{"description": "the module asking it", "type": "string"}, "options": map[string]any{"description": "the template's options the module set, within their bounds", "type": "object"}, "storage-type": map[string]any{"description": "how the bus keeps it", "enum": []string{"stream", "key-value", "object-store", "ledger"}, "type": "string"}, "template": map[string]any{"description": "its template; empty for a configuration of its own", "enum": []string{"state", "events", "work-queue", "log", "objects", "ledger", ""}, "type": "string"}}, "required": []string{"module", "local-name", "storage-type"}, "type": "object"}, - Output: map[string]any{"description": "one entry as the bus holds it", "properties": map[string]any{"bus-name": map[string]any{"description": "its name on the bus, _", "type": "string"}, "configuration": map[string]any{"description": "its own configuration, where no template fits", "type": "object"}, "fill": map[string]any{"properties": map[string]any{"bytes": map[string]any{"description": "bytes held", "type": "integer"}, "cap": map[string]any{"description": "its cap in bytes", "type": "integer"}, "count": map[string]any{"description": "messages, keys or objects held", "type": "integer"}}, "required": []string{"count", "bytes", "cap"}, "type": "object"}, "local-name": map[string]any{"description": "its local name in the module's manifest", "type": "string"}, "module": map[string]any{"description": "the module asking it", "type": "string"}, "options": map[string]any{"description": "the template's options the module set, within their bounds", "type": "object"}, "settings": map[string]any{"description": "the settings the bus holds", "type": "object"}, "state": map[string]any{"description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", "enum": []string{"held", "refused", "waiting", "drifted", "full", "orphaned"}, "type": "string"}, "storage-type": map[string]any{"description": "how the bus keeps it", "enum": []string{"stream", "key-value", "object-store", "ledger"}, "type": "string"}, "template": map[string]any{"description": "its template; empty for a configuration of its own", "enum": []string{"state", "events", "work-queue", "log", "objects", "ledger", ""}, "type": "string"}, "why": map[string]any{"description": "why it is in its state, in plain words; empty when held as asked", "type": "string"}}, "required": []string{"module", "local-name", "storage-type", "bus-name"}, "type": "object"}}, - {Name: "apply", Description: "Apply a difference that only grows: a larger cap, a longer age.", Optional: true, - Input: map[string]any{"properties": map[string]any{"local-name": map[string]any{"description": "the entry's local name", "type": "string"}, "module": map[string]any{"description": "the module", "type": "string"}}, "required": []string{"module", "local-name"}, "type": "object"}, - Output: map[string]any{"description": "one entry as the bus holds it", "properties": map[string]any{"bus-name": map[string]any{"description": "its name on the bus, _", "type": "string"}, "configuration": map[string]any{"description": "its own configuration, where no template fits", "type": "object"}, "fill": map[string]any{"properties": map[string]any{"bytes": map[string]any{"description": "bytes held", "type": "integer"}, "cap": map[string]any{"description": "its cap in bytes", "type": "integer"}, "count": map[string]any{"description": "messages, keys or objects held", "type": "integer"}}, "required": []string{"count", "bytes", "cap"}, "type": "object"}, "local-name": map[string]any{"description": "its local name in the module's manifest", "type": "string"}, "module": map[string]any{"description": "the module asking it", "type": "string"}, "options": map[string]any{"description": "the template's options the module set, within their bounds", "type": "object"}, "settings": map[string]any{"description": "the settings the bus holds", "type": "object"}, "state": map[string]any{"description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", "enum": []string{"held", "refused", "waiting", "drifted", "full", "orphaned"}, "type": "string"}, "storage-type": map[string]any{"description": "how the bus keeps it", "enum": []string{"stream", "key-value", "object-store", "ledger"}, "type": "string"}, "template": map[string]any{"description": "its template; empty for a configuration of its own", "enum": []string{"state", "events", "work-queue", "log", "objects", "ledger", ""}, "type": "string"}, "why": map[string]any{"description": "why it is in its state, in plain words; empty when held as asked", "type": "string"}}, "required": []string{"module", "local-name", "storage-type", "bus-name"}, "type": "object"}}, - {Name: "recreate", Description: "Rebuild an entry whose settings differ in a way that cannot grow, from its data where the storage type allows, after a stream snapshot.", Optional: true, - Input: map[string]any{"properties": map[string]any{"local-name": map[string]any{"description": "the entry's local name", "type": "string"}, "module": map[string]any{"description": "the module", "type": "string"}, "warrant": map[string]any{"description": "the operator's warrant, as the operator channel decided it (ADR 0259 §6)", "type": "string"}, "why": map[string]any{"description": "why, kept in the record", "type": "string"}}, "required": []string{"module", "local-name", "warrant", "why"}, "type": "object"}, - Output: map[string]any{"description": "one entry as the bus holds it", "properties": map[string]any{"bus-name": map[string]any{"description": "its name on the bus, _", "type": "string"}, "configuration": map[string]any{"description": "its own configuration, where no template fits", "type": "object"}, "fill": map[string]any{"properties": map[string]any{"bytes": map[string]any{"description": "bytes held", "type": "integer"}, "cap": map[string]any{"description": "its cap in bytes", "type": "integer"}, "count": map[string]any{"description": "messages, keys or objects held", "type": "integer"}}, "required": []string{"count", "bytes", "cap"}, "type": "object"}, "local-name": map[string]any{"description": "its local name in the module's manifest", "type": "string"}, "module": map[string]any{"description": "the module asking it", "type": "string"}, "options": map[string]any{"description": "the template's options the module set, within their bounds", "type": "object"}, "settings": map[string]any{"description": "the settings the bus holds", "type": "object"}, "state": map[string]any{"description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", "enum": []string{"held", "refused", "waiting", "drifted", "full", "orphaned"}, "type": "string"}, "storage-type": map[string]any{"description": "how the bus keeps it", "enum": []string{"stream", "key-value", "object-store", "ledger"}, "type": "string"}, "template": map[string]any{"description": "its template; empty for a configuration of its own", "enum": []string{"state", "events", "work-queue", "log", "objects", "ledger", ""}, "type": "string"}, "why": map[string]any{"description": "why it is in its state, in plain words; empty when held as asked", "type": "string"}}, "required": []string{"module", "local-name", "storage-type", "bus-name"}, "type": "object"}}, - {Name: "drop", Description: "Remove one orphan, after a stream snapshot.", Optional: true, - Input: map[string]any{"properties": map[string]any{"local-name": map[string]any{"description": "the entry's local name", "type": "string"}, "module": map[string]any{"description": "the module", "type": "string"}, "warrant": map[string]any{"description": "the operator's warrant, as the operator channel decided it (ADR 0259 §6)", "type": "string"}, "why": map[string]any{"description": "why, kept in the record", "type": "string"}}, "required": []string{"module", "local-name", "warrant", "why"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"dropped": map[string]any{"description": "it is gone", "type": "boolean"}, "snapshot": map[string]any{"description": "the snapshot taken first", "type": "string"}}, "required": []string{"dropped"}, "type": "object"}}, - }}, - {Name: MeshVault, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0161", Delivers: "secret", - Serves: []Verb{ - {Name: "get", Description: "One secret, sealed to the public key the asker sends (the sealed box of ADR 0206), with its generation; answered only to an asker the vault allows, as the bus proved it. Never the value in the clear, and never to a private key.", Optional: true, - Input: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the secret's name", "type": "string"}, "public_key": map[string]any{"description": "the asker's X25519 public key as SPKI PEM — never a private key", "type": "string"}}, "required": []string{"name", "public_key"}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "secret-ask", Description: "Ask the operator for a module's own secret at a desk, from the controller or a caller the vault's manifest names, named as the bus proved it (novox/hq ADR 0563 §8): the ask is recorded in the vault's store and bounded before the prompt opens, and what is typed is sealed to a key the vault made for the call and opens there alone.", Optional: true, - Input: map[string]any{"properties": map[string]any{"asked_by": map[string]any{"description": "who asked, as the bus named its caller", "type": "string"}, "call": map[string]any{"description": "the second step: the call the first step answered", "type": "string"}, "desk": map[string]any{"description": "the machine whose prompt asks", "type": "string"}, "module": map[string]any{"description": "the module", "type": "string"}, "name": map[string]any{"description": "the secret's name in the module", "type": "string"}, "node": map[string]any{"description": "the machine the secret is for", "type": "string"}, "outcome": map[string]any{"description": "instead of sealed: how the prompt ended", "type": "string"}, "sealed": map[string]any{"description": "the second step: the value sealed to the call's key — never the value", "type": "string"}}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "issued", Description: "Take the credential a provider's software issued for a provision, from the controller alone, which asks the provider's tool with a key the vault made for the call; kept as the provision's one credential, or, when the tool answers a value per holder named, each as that holder's own; the same value again writes nothing.", Optional: true, - Input: map[string]any{"properties": map[string]any{"call": map[string]any{"description": "the second step: the call the first step answered", "type": "string"}, "holders": map[string]any{"description": "the provision's holders from that provider, each {node, module}: its tool may answer a value for each, kept as that holder's own and handed to it alone", "items": map[string]any{"properties": map[string]any{"module": map[string]any{"description": "the consuming module", "type": "string"}, "node": map[string]any{"description": "the holder's machine", "type": "string"}}, "type": "object"}, "type": "array"}, "module": map[string]any{"description": "the provider's module", "type": "string"}, "provider": map[string]any{"description": "the provider's machine", "type": "string"}, "provision": map[string]any{"description": "the provision", "type": "string"}, "sealed": map[string]any{"description": "the second step: the value sealed to the call's key — never the value", "type": "string"}}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "rotate", Description: "Make a secret anew. Refused for a value a provider's software issued and for an accepted credential. Never answers the value.", Optional: true, - Input: map[string]any{"properties": map[string]any{"by": map[string]any{"description": "who asked, when the controller carries the act", "type": "string"}, "module": map[string]any{"description": "the module", "type": "string"}, "name": map[string]any{"description": "the secret's name in the module", "type": "string"}, "node": map[string]any{"description": "the machine, for a module's own secret", "type": "string"}, "provider": map[string]any{"description": "the provider's machine", "type": "string"}, "provision": map[string]any{"description": "the provision", "type": "string"}}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "move-in", Description: "Take one value of the controller's store in, as it is, from the node-engine of a machine it was sealed to and no other caller: name the row, its origin and generation and get a call and a key made for it, then send the call, the value sealed to that key and a key to read back to. Never replaces a value the vault holds; answers what it kept, read back sealed to that key.", Optional: true, - Input: map[string]any{"properties": map[string]any{"call": map[string]any{"description": "the second step: the call the first step answered", "type": "string"}, "consumer": map[string]any{"description": "the consumer's machine", "type": "string"}, "consumer_module": map[string]any{"description": "the consuming module", "type": "string"}, "generation": map[string]any{"description": "the controller's generation for the value", "type": "string"}, "local": map[string]any{"description": "the credential's local name, when it has one", "type": "string"}, "made_at": map[string]any{"description": "when it was made (RFC 3339)", "type": "string"}, "module": map[string]any{"description": "its module", "type": "string"}, "name": map[string]any{"description": "its name in the module", "type": "string"}, "node": map[string]any{"description": "a module's own secret: its machine", "type": "string"}, "origin": map[string]any{"description": "made, accepted or issued, as the controller's store says", "type": "string"}, "provider": map[string]any{"description": "a pair credential: the provider's machine", "type": "string"}, "provision": map[string]any{"description": "the provision", "type": "string"}, "read_back_to": map[string]any{"description": "the second step: a raw X25519 public key the read-back is sealed to", "type": "string"}, "sealed": map[string]any{"description": "the second step: the value sealed to the call's key — never the value", "type": "string"}}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "node-key", Description: "State this machine's sealing key to the vault, in its own name, from the node-engine of the machine alone: at its start and whenever the key changes. A first or changed key is recorded and the machine's core secrets are said changed; the same key again changes nothing.", Optional: true, - Input: map[string]any{"properties": map[string]any{"key": map[string]any{"description": "the machine's sealing key, a raw X25519 public key in base64", "type": "string"}}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "own", Description: "Answer the node-engine of one machine, about its own machine alone (as the bus proved the caller, never an argument), its modules' own secrets and the pair credentials it consumes or provides that it names, each sealed to the key it stated with node-key. A credential the vault does not hold is named missing; never a value in the clear.", Optional: true, - Input: map[string]any{"properties": map[string]any{"credentials": map[string]any{"description": "the pair credentials the machine's composition reads, each {role: consumer|provider, provision, consumer, consumer_module, provider, local}", "items": map[string]any{"type": "object"}, "type": "array"}}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "issue-account", Description: "Mint one bus account the grants part wants, now (novox/hq ADR 0561 §9): only while the controller's lease is handed away, never a node's account or the controller's, never anew for one the vault holds a value or an adopted hash for. Never the value.", Optional: true, - Input: map[string]any{"properties": map[string]any{"user": map[string]any{"description": "the bus user, e.g. ace.mesh-build-go", "type": "string"}}, "required": []string{"user"}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "sign", Description: "Sign with the vault's key or authority (novox/hq ADR 0550 §2): kind node-certificate signs the certificate of a machine's internal name, asked by that machine's node-engine for the serving key its record states, built from bus state alone, or by mesh-enrolment for a node it enrols, for the serving key the node presented at redeem (ADR 0587 §3); kind enrolment signs a redeem answer under its own context, for mesh-enrolment alone. Answers the certificate or the signature and the signer's fingerprint, and says signed; never a key.", Optional: true, - Input: map[string]any{"properties": map[string]any{"kind": map[string]any{"description": "node-certificate or enrolment", "type": "string"}, "node": map[string]any{"description": "the machine, for a node certificate: the caller's own, or the node mesh-enrolment enrols", "type": "string"}, "reply": map[string]any{"description": "the redeem answer's bytes in base64, for an enrolment", "type": "string"}, "serving_key": map[string]any{"description": "from mesh-enrolment: the serving key's public half the enrolling node presented", "type": "string"}}, "required": []string{"kind"}, "type": "object"}, - Output: map[string]any{"description": "for a node certificate the certificate as PEM and the authority that signed it, for an enrolment the signature in base64; always the signer's fingerprint and the digest; never a key's private half", "properties": map[string]any{"authority": map[string]any{"description": "the authority's certificate that signed it, as PEM", "type": "string"}, "certificate": map[string]any{"description": "the node certificate as PEM, for a node certificate", "type": "string"}, "digest": map[string]any{"description": "sha256: of the bytes signed", "type": "string"}, "kind": map[string]any{"description": "node-certificate or enrolment, as asked", "type": "string"}, "node": map[string]any{"description": "the machine, for a node certificate", "type": "string"}, "signature": map[string]any{"description": "the signature in base64, for an enrolment", "type": "string"}, "signer": map[string]any{"description": "the signer's fingerprint", "type": "string"}}, "required": []string{"kind", "signer", "digest"}, "type": "object"}}, - {Name: "enrolment-token", Description: "Make a one-time token for a node mesh-enrolment issues on the operator's warrant: its secret kept with its copy sealed to the operator key and its hash written as the bus user enrol.. Answers only what the joining machine must trust (the bus's address, the bus certificate's fingerprint, the vault's public signing key), never the token; with sealed, the token sealed to the operator key, for tokens to relay; with drop, the token and its hash dropped, at the enrolment's end. Asked by mesh-enrolment alone, only from the lease write. The token carries the first tunnel mesh-enrolment built at issue (ADR 0587 §3, insight of 2026-11-02).", Optional: true, - Input: map[string]any{"properties": map[string]any{"drop": map[string]any{"description": "\"true\": drop the node's token and its hash", "enum": []string{"true", "false"}, "type": "string"}, "expires": map[string]any{"description": "when the token expires (RFC 3339), as the enrolment's key says", "type": "string"}, "node": map[string]any{"description": "the node the token is for", "type": "string"}, "sealed": map[string]any{"description": "\"true\": answer the token sealed to the operator key", "enum": []string{"true", "false"}, "type": "string"}, "tunnel": map[string]any{"additionalProperties": false, "description": "the node's first tunnel, built by mesh-enrolment at issue and carried in the token: the hub's public key, the hub's endpoint, the node's tunnel address, its own tunnel key and the network's range", "properties": map[string]any{"address": map[string]any{"description": "the joining node's tunnel address", "type": "string"}, "hub_endpoint": map[string]any{"description": "the hub's endpoint, host:port", "type": "string"}, "hub_key": map[string]any{"description": "the hub's tunnel public key", "type": "string"}, "key": map[string]any{"description": "the joining machine's own tunnel public key, as given at issue", "type": "string"}, "range": map[string]any{"description": "the private network's range, a prefix", "type": "string"}}, "required": []string{"address", "hub_endpoint", "hub_key", "key", "range"}, "type": "object"}}, "required": []string{"node"}, "type": "object"}, - Output: map[string]any{"description": "what the joining machine must trust, never the token: the bus's address, its certificate's fingerprint and the vault's public signing key; the token sealed to the operator key when asked sealed; or that a drop left neither the token nor its hash", "properties": map[string]any{"broker": map[string]any{"description": "the bus's address the machine dials", "type": "string"}, "dropped": map[string]any{"description": "a drop left neither the token nor its hash", "type": "boolean"}, "fingerprint": map[string]any{"description": "the fingerprint of the bus's certificate", "type": "string"}, "node": map[string]any{"description": "the node the token is for", "type": "string"}, "sealed": map[string]any{"description": "the token sealed to the operator key, when asked sealed, relayed as it is", "type": "string"}, "signer": map[string]any{"description": "the vault's public signing key in base64", "type": "string"}}, "required": []string{"node"}, "type": "object"}}, - {Name: "node-account", Description: "Mint a joining node's bus account node., sealed to the sealing key it presented, and write its hash; asked again for the same node and keys, answers the account it already minted; with retire, the account it minted for a revoked enrolment retired. Writes the node's node-logins key naming node.. Asked by mesh-enrolment alone. Never the value in the clear.", Optional: true, - Input: map[string]any{"properties": map[string]any{"keys_digest": map[string]any{"description": "the digest of every public key the node presented (delivery.KeysDigest)", "type": "string"}, "node": map[string]any{"description": "the joining node", "type": "string"}, "retire": map[string]any{"description": "\"true\": retire the account minted for this node", "enum": []string{"true", "false"}, "type": "string"}, "sealing_key": map[string]any{"description": "the sealing key's public half the node presented, which the account is sealed to", "type": "string"}}, "required": []string{"node"}, "type": "object"}, - Output: map[string]any{"description": "the login minted and the account sealed to the sealing key the node presented, never in the clear; or that a retire left no account", "properties": map[string]any{"account": map[string]any{"description": "the account sealed to the node's sealing key, in base64", "type": "string"}, "login": map[string]any{"description": "the login minted", "type": "string"}, "node": map[string]any{"description": "the joining node", "type": "string"}, "retired": map[string]any{"description": "a retire left no account", "type": "boolean"}}, "required": []string{"node"}, "type": "object"}}, - {Name: "rotate-node", Description: "Begin a node's account rotation to its other login: answered only for a call carrying a hand act recorded through mesh-hand-acts, which the vault reads back by its id. Makes the other login's value sealed to that node, writes it rotating in node-logins and says rotation-begun; says node-account-ready once the bus node's have shows a list holding both logins. One node at a time across the mesh, the bus node last: a call while a rotation runs waits until it is done.", Optional: true, - Input: map[string]any{"properties": map[string]any{"act": map[string]any{"description": "the id of the hand act recorded for it in mesh-hand-acts", "type": "string"}, "node": map[string]any{"description": "the node whose account rotates", "type": "string"}, "why": map[string]any{"description": "why, in a sentence", "type": "string"}}, "required": []string{"node", "why", "act"}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "node-account-confirm", Description: "Confirm a node's switch to its new login: asked by the node-engine dialled as that login, the runtime's proof of the caller being the confirmation. The old login's hash is then removed, and rotation-done said once the bus node's have shows the list without it.", Optional: true, - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "abandon", Description: "Abandon a node's rotation before its old login is retired: the new login and its value dropped, the old one kept; a node that switched switches back first.", Optional: true, - Input: map[string]any{"properties": map[string]any{"node": map[string]any{"description": "the node whose rotation is abandoned", "type": "string"}}, "required": []string{"node"}, "type": "object"}, - Output: map[string]any{"description": "the holder's answer", "type": "object"}}, - {Name: "retire", Description: "Retire one module's own secret, by node, module and name, on the operator's warrant bound to exactly those three (design 32 *A value no longer read*). Emits own-retired.", Optional: true, - Input: map[string]any{"properties": map[string]any{"ask": map[string]any{"description": "the id of the operator's ask the warrant answers", "type": "string"}, "module": map[string]any{"description": "the module", "type": "string"}, "name": map[string]any{"description": "the own secret's name", "type": "string"}, "node": map[string]any{"description": "the node", "type": "string"}, "warrant": map[string]any{"description": "the operator's warrant for this node, module and name: the hand act's id", "type": "string"}}, "required": []string{"node", "module", "name", "warrant", "ask"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"retired": map[string]any{"description": "the secret is gone", "type": "boolean"}}, "required": []string{"retired"}, "type": "object"}}, - }}, - {Name: MeshConfig, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0348, ADR 0565", - Serves: []Verb{ - {Name: "config", Description: "Set, clear, list or read the history of config values: mesh config or node config for a predefined key, or a module's own value on one node, each set with why, validated, and naming each module on each node whose value moves.", Optional: true, - Input: map[string]any{"properties": map[string]any{"action": map[string]any{"description": "list (when not given), set, clear or history", "type": "string"}, "by": map[string]any{"description": "who gave the value at the controller's terminal", "type": "string"}, "key": map[string]any{"description": "the config key", "type": "string"}, "limit": map[string]any{"description": "for history: at most this many, newest", "type": "number"}, "module": map[string]any{"description": "a module: its own value, with node", "type": "string"}, "node": map[string]any{"description": "a node", "type": "string"}, "value": map[string]any{"description": "for set: the value, of the key's type: a number, a string, a boolean, or for a trusted key an object or a list"}, "why": map[string]any{"description": "for set and clear: why", "type": "string"}}, "type": "object"}, - Output: map[string]any{"description": "the values asked, or the one set or cleared, or its history", "type": "object"}}, - {Name: "renames", Description: "The open renames of config values (design 32 *A config value renamed*): per rename the module, the old and new name, when it moved, and the nodes still holding the old name open.", Optional: true, - Input: map[string]any{"properties": map[string]any{"module": map[string]any{"description": "one module (optional)", "type": "string"}}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"renames": map[string]any{"description": "every open rename", "items": map[string]any{"properties": map[string]any{"at": map[string]any{"description": "when it moved, RFC 3339", "type": "string"}, "from": map[string]any{"description": "the old name", "type": "string"}, "module": map[string]any{"description": "the module", "type": "string"}, "open": map[string]any{"description": "the nodes still holding the old name open", "items": map[string]any{"type": "string"}, "type": "array"}, "to": map[string]any{"description": "the new name", "type": "string"}}, "required": []string{"module", "from", "to"}, "type": "object"}, "type": "array"}}, "required": []string{"renames"}, "type": "object"}}, - }}, - {Name: MeshProvisioner, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0349", - Serves: []Verb{ - {Name: "provisioning", Description: "Read what the provisioner keeps: the registry (which domain answers which kind of placeholder), the needs each module on each node recorded, what is applied where, or the items of every collection, narrowed by node and module. Reads only.", Optional: true, - Input: map[string]any{"properties": map[string]any{"collection": map[string]any{"description": "for collect: a collection", "type": "string"}, "module": map[string]any{"description": "a module: for collect, a contributor", "type": "string"}, "node": map[string]any{"description": "a node", "type": "string"}, "what": map[string]any{"description": "registry (when not given), needs, applied or collect", "type": "string"}}, "type": "object"}}, - }}, - {Name: MeshArtifactStore, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0075"}, - {Name: MeshCatalog, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0604", - Serves: []Verb{ - {Name: "modules", Description: "Every module the trunk holds, each with its repository, its directory there, the merge commit it was last changed at and its fingerprint (or why it has none): the catalog of the mesh, read from its holder's state. Reads only.", Optional: true, - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "module", Description: "One module whole: its repository, directory, commit and fingerprint, and its manifest as the trunk holds it. Older versions are in its repository's history. Reads only.", Optional: true, - Input: map[string]any{"properties": map[string]any{"module": map[string]any{"description": "the module's name", "type": "string"}}, "required": []string{"module"}, "type": "object"}}, - {Name: "provides", Description: "Which modules provide a provision (a module provides its own name too) and which require it, from the module graph's provision edges. Reads only.", Optional: true, - Input: map[string]any{"properties": map[string]any{"provision": map[string]any{"description": "the provision, such as postgres-database", "type": "string"}}, "required": []string{"provision"}, "type": "object"}}, - {Name: "dependents", Description: "The modules whose build edges reach a module, directly or through others (a base, a mesh package it publishes, the build module that builds them): what a change of it builds again, from the module graph. Reads only.", Optional: true, - Input: map[string]any{"properties": map[string]any{"module": map[string]any{"description": "the module's name", "type": "string"}}, "required": []string{"module"}, "type": "object"}}, - }}, - {Name: MeshDelivery, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0239", - Serves: []Verb{ - {Name: "deliveries", Description: "Every delivery not final, and those that ended in the last day, one line each: its id (owner/repository@commit), its state, its group, what it waits for and since when. Narrowed by state, repository or group.", - Input: map[string]any{"properties": map[string]any{"all": map[string]any{"description": "\"true\": the final ones of the last thirty days too", "enum": []string{"true", "false"}, "type": "string"}, "group": map[string]any{"description": "a group's id (its branch name)", "type": "string"}, "repository": map[string]any{"description": "owner/repository", "type": "string"}, "state": map[string]any{"description": "one state, e.g. delivering or held", "type": "string"}}, "type": "object"}}, - {Name: "times", Description: "Removed in R6 (ADR 0539): refused by name, naming its replacement: the delivery view (nox-delivery): each machine's want and have, and since when", Optional: true, - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "show", Description: "One delivery or group whole: its delivery plan (what it builds, what each machine receives, what is not an ordinary send), every transition with when and why, the machine steps of its walk, its group and its order.", - Input: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "a delivery's id, or a group's", "type": "string"}}, "required": []string{"id"}, "type": "object"}}, - {Name: "groups", Description: "Every delivery group: its members in order, why each pair is ordered (declared, built by, version skew, engine before controller, by name), its composed check and its state, derived from its members.", - Input: map[string]any{"properties": map[string]any{"all": map[string]any{"description": "\"true\": the groups that ended too", "enum": []string{"true", "false"}, "type": "string"}}, "type": "object"}}, - {Name: "what-if", Description: "Removed in R6 (ADR 0539): refused by name, naming its replacement: the merge check's composition, which composes every machine a change touches", Optional: true, - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "checks", Description: "What the mesh's checks said of a pull request's head or of one commit: each of the commit's mesh statuses (mesh/check, the one a main requires from R4e, hq's mesh/repo-check, mesh/delivery, …) with its state, description and when it was set; the merge check's full verdict as the controller said it — each layer's summary, the machine that ran it, when, its build id and its report; and whether the branch's protection would let it merge, every required status being success.", Optional: true, - Input: map[string]any{"properties": map[string]any{"commit": map[string]any{"description": "a commit's sha, or the start of one, instead of a pull request", "type": "string"}, "number": map[string]any{"description": "a pull request's number: its head is read", "type": "string"}, "repository": map[string]any{"description": "owner/repository", "type": "string"}}, "required": []string{"repository"}, "type": "object"}}, - {Name: "table", Description: "The state table every delivery runs by: each transition with its guard, each state's bound and what healer H2 may do once it has passed; and the machine steps' table.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "stalled", Description: "Every delivery held past its state's bound, with the transition the table lets healer H2 take for it, or none.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "recheck", Description: "Check a rejected or ready delivery again: it goes back to proposed and the controller is asked for its check. With why.", - Input: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "the delivery's id", "type": "string"}, "why": map[string]any{"description": "why, kept with the transition", "type": "string"}}, "required": []string{"id", "why"}, "type": "object"}}, - {Name: "release", Description: "Removed in R6 (ADR 0539): refused by name, naming its replacement: the assigner's asks (hold, restore, backup, record), answered on the phone or in the delivery view", Optional: true, - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "stop", Description: "Removed in R6 (ADR 0539): refused by name, naming its replacement: a bad fingerprint is held and its canary set back by the assigner (ADR 0429 §6, ADR 0539 clause 18)", Optional: true, - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "close", Description: "Healer H2's verb: take the one transition the table names for a delivery held past its bound. Refused for any other.", - Input: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "the delivery's id", "type": "string"}, "why": map[string]any{"description": "the condition it answers", "type": "string"}}, "required": []string{"id", "why"}, "type": "object"}}, - {Name: "retire-history", Description: "Removed in R6 (ADR 0539): refused by name, naming its replacement: nothing to retire: the gate keeps no delivery after its merge", Optional: true, - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "settle", Description: "Ask the operator to settle deliveries held because their merge reached the trunk without a passing check: one ask on the operator's channel for all of them, as an Approve is asked. The verb moves nothing; on the operator's answer each goes to merged, and whether it is on the machines is derived as for any merge (novox/hq ADR 0556). Refused for a delivery not held, or held for another reason, naming why.", Optional: true, - Input: map[string]any{"additionalProperties": false, "properties": map[string]any{"ids": map[string]any{"description": "the held deliveries' ids (owner/repository@commit)", "items": map[string]any{"type": "string"}, "minItems": float64(1), "type": "array"}, "why": map[string]any{"description": "why they may be settled, shown to the operator", "type": "string"}}, "required": []string{"ids", "why"}, "type": "object"}}, - }}, - {Name: MeshPlacement, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0567", - Serves: []Verb{ - {Name: "placement", Description: "Read placement: one node's key, the nodes one module is placed on, or every key, each with its revision, and how full the state is against its cap. With history, one node's or one module's changes in order: a revision the state keeps whole, an older one as the proposal's changes from its placement-changed event.", Optional: true, - Input: map[string]any{"properties": map[string]any{"history": map[string]any{"description": "\"true\": the changes in order, from the stream and the state's revisions", "enum": []string{"true", "false"}, "type": "string"}, "module": map[string]any{"description": "one module: the nodes it is placed on", "type": "string"}, "node": map[string]any{"description": "one node", "type": "string"}, "seats": map[string]any{"description": "\"true\": who holds each seat, and on which node (the state seat-holders); with node, that node's", "enum": []string{"true", "false"}, "type": "string"}}, "type": "object"}}, - {Name: "proposals", Description: "Every open proposal of placement: its changes, the revision of each key it was laid over, its candidates, every node's answer, the differences computed and answered, its verdict and its ask. Narrowed to one by id.", Optional: true, - Input: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "one proposal's id", "type": "string"}}, "type": "object"}}, - {Name: "propose", Description: "Propose a change of placement, with why: laid over the current keys and refused at once, naming every finding, when the result cannot be placed (two modules on one host port, a provider not placed, a seat held once held twice, two nodes with one address, two hubs, a module the indexer does not hold, a node with no record). Otherwise the nodes it touches compose it in shadow and answer, and on a passing verdict the operator is asked to approve it. Who proposes is the caller, as the runtime proves it. An add-node from mesh-enrolment, whose warrant names its node and address, is written with no ask of its own: its Approve is the enrolment's (ADR 0587 §7).", Optional: true, - Input: map[string]any{"additionalProperties": false, "properties": map[string]any{"changes": map[string]any{"description": "the changes, each an object with `op`: place (node, module, ports, bindings), take-off (node, module), pin (node, provision, provider {node, module}), unpin (node, provision), hold (node, module, claim, scope, kind, site), unhold (node, module, claim), decide (node, field, value: a decided field of the node, its address among them), config (node, module, mesh-wide, layer, mesh-wide-values, values), add-node (node, decided, keys: a joining node, mesh-enrolment's alone, the proposal's one change, with the enrolment's warrant, its tunnel key alone at the operator's yes; ADR 0587 §7), remove-node (node: a joining node never confirmed, taken off when its enrolment ends; mesh-enrolment's alone, on the same warrant)", "items": map[string]any{"required": []string{"op", "node"}, "type": "object"}, "minItems": float64(1), "type": "array"}, "warrant": map[string]any{"description": "for an add-node alone: the enrolment's warrant {node, address, ask, warrant}, naming the change's node and address", "required": []string{"node", "address", "ask", "warrant"}, "type": "object"}, "why": map[string]any{"description": "why, shown to the operator with the proposal", "type": "string"}}, "required": []string{"why", "changes"}, "type": "object"}}, - {Name: "withdraw", Description: "Withdraw an open proposal: its key and its candidates are deleted and placement-withdrawn carries it whole. With why.", Optional: true, - Input: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "the proposal's id", "type": "string"}, "why": map[string]any{"description": "why, kept in the event", "type": "string"}}, "required": []string{"id", "why"}, "type": "object"}}, - {Name: "again", Description: "Judge an open proposal now with the answers that stand, and raise its ask again when its verdict passes and the operator's channel does not hold the ask. Nothing more: the recovery of a proposal whose judgement or ask was lost.", Optional: true, - Input: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "the proposal's id", "type": "string"}}, "required": []string{"id"}, "type": "object"}}, - }}, - {Name: MeshDeliveryChecker, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0547", - Serves: []Verb{ - {Name: "try", Description: "Try a pushed head before its pull request is opened: the checker judges it as it judges a pull request's head, merged onto its trunk's head, its unit and integration levels through the build modules, and owes the forge nothing. Answers the try's id at once; try-read reads where it stands. A try of the same head that waits or runs is the answer to a second ask; at most 4 wait or run at once, mesh-wide.", Optional: true, - Input: map[string]any{"properties": map[string]any{"head": map[string]any{"description": "the full commit to try, 40 or 64 hex digits", "type": "string"}, "level": map[string]any{"description": "unit or integration: only this level; both when absent", "type": "string"}, "repository": map[string]any{"description": "/", "type": "string"}, "tests": map[string]any{"description": "a go test -run pattern: only those tests run, and nothing is kept", "type": "string"}, "trunk": map[string]any{"description": "the trunk commit to merge it onto, or the trunk's branch for its head now; the trunk's head when absent", "type": "string"}}, "required": []string{"repository", "head"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "the try's id", "type": "string"}, "state": map[string]any{"description": "its state", "type": "string"}}, "required": []string{"id"}, "type": "object"}, Replaces: []string{"sh merge-check.sh", "docker run postgres"}}, - {Name: "try-read", Description: "Where a try stands, as stored: queued, running, or its end (pass, fail, or error when it could not run), with what was said of it, the head and the trunk commit it was merged onto, the time it took and what failed first. Read-only.", Optional: true, - Input: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "the try's id, try-", "type": "string"}}, "required": []string{"id"}, "type": "object"}, - Output: map[string]any{"description": "the try: its state, its steps and its verdict when it has one", "type": "object"}}, - {Name: "try-end", Description: "End a try that waits or runs, as error, by hand: say why (kept in what it says). Its place among the tries is free at once and the next try of its head asks anew with a new id. A try that ended already is refused.", Optional: true, - Input: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "the try's id, try-", "type": "string"}, "why": map[string]any{"description": "why it is ended, in words", "type": "string"}}, "required": []string{"id", "why"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"ended": map[string]any{"description": "it is ended", "type": "boolean"}, "id": map[string]any{"description": "the try", "type": "string"}}, "required": []string{"id", "ended"}, "type": "object"}}, - }}, - {Name: MeshConditions, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0564", Emits: []string{"condition-raised", "condition-changed", "condition-cleared"}, - Serves: []Verb{ - {Name: "conditions", Description: "What is wrong with the mesh now: every open condition, urgent first, then oldest, raised from the observers' findings (the watchman, the self-check, the healers, the assigner) and cleared when the finding goes, never by hand. Given a key, that one whole with its evidence; with history, every transition lately.", - Input: map[string]any{"properties": map[string]any{"days": map[string]any{"description": "with history: how many days back (default 7, at most 90)", "type": "string"}, "history": map[string]any{"description": "\"true\": every raising, change, silence and clearing lately, oldest first", "enum": []string{"true", "false"}, "type": "string"}, "key": map[string]any{"description": "a condition's key: that one whole; with history, only its transitions", "type": "string"}, "machine": map[string]any{"description": "only those about this machine", "type": "string"}, "scope": map[string]any{"description": "only this scope: machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module", "type": "string"}, "severity": map[string]any{"description": "only urgent, or only warning", "type": "string"}}, "type": "object"}}, - {Name: "silence", Description: "Stop one condition's messages for a while, a hand act with why: it stays open and in status, and the silence is kept as its transition in history.", - Input: map[string]any{"properties": map[string]any{"cause": map[string]any{"description": "the cause in a word (the condition's kind when absent)", "type": "string"}, "for": map[string]any{"description": "how long: 30m, 4h, 2d; at most 7d", "type": "string"}, "key": map[string]any{"description": "the condition's key", "type": "string"}, "why": map[string]any{"description": "why, required and recorded", "type": "string"}}, "required": []string{"key", "for", "why"}, "type": "object"}}, - {Name: "resync", Description: "Read every observer's findings again and raise and clear what differs from the open conditions: the recovery of a keeper that missed a change.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - }}, - {Name: MeshWatchdogs, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0564", - Serves: []Verb{ - {Name: "signals", Description: "Every watchdog of the signals table the watchman keeps, each a key that expires unless its signal renews it: its row, what renews it, its bound, the age of its newest signal, and the finding its expiry wrote.", - Input: map[string]any{"properties": map[string]any{"row": map[string]any{"description": "one row, e.g. S1", "type": "string"}}, "type": "object"}}, - {Name: "stuck", Description: "Every want, build or step the watchman says stopped moving past its bound, with since when and the words of its finding.", - Input: map[string]any{"properties": map[string]any{"machine": map[string]any{"description": "only those on this machine", "type": "string"}}, "type": "object"}}, - {Name: "presence", Description: "Every machine and tool runner the watchman sees connected, with its last word, and those it says silent; a machine that said it sleeps is shown as sleeping, not silent.", - Input: map[string]any{"properties": map[string]any{"machine": map[string]any{"description": "one machine", "type": "string"}}, "type": "object"}}, - {Name: "dead-letters", Description: "Every message a consumer on the bus gave up on, kept in DEAD_LETTERS: whose consumer, the subject, how often it was handed over and when it was given up, newest first. With id: that one whole. With deliver or drop, a hand act with why.", - Input: map[string]any{"properties": map[string]any{"cause": map[string]any{"description": "with deliver or drop: the cause in a word (dead-letter when absent)", "type": "string"}, "consumer": map[string]any{"description": "a consumer's name, or .: only what that one gave up on", "type": "string"}, "deliver": map[string]any{"description": "a dead letter's id: deliver it again to the consumer that gave it up (needs why)", "type": "string"}, "drop": map[string]any{"description": "a dead letter's id: drop it for good (needs why)", "type": "string"}, "id": map[string]any{"description": "a dead letter's id, as the list gives it: that one whole, with its message", "type": "string"}, "limit": map[string]any{"description": "how many to list, newest first (default 50); only when listing", "type": "string"}, "why": map[string]any{"description": "with deliver or drop: why, required and recorded", "type": "string"}}, "type": "object"}}, - {Name: "resync", Description: "Rebuild presence from the bus's connections, and the pulses and findings from the keys the watchman watches, and take every dead-letter notice waiting: the recovery of a watchman that missed a change.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - }}, - {Name: MeshSelfCheck, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0564", Emits: []string{"checked"}, - Serves: []Verb{ - {Name: "doctor", Description: "The self-check: the last run's verdict at once, each probe passed, failed, deferred (its facts not on the bus yet, named) or could not run, and how long ago. With run, a run now; with probe, one probe's verdict whole. Runs every five minutes on its own; each failure is a finding.", - Input: map[string]any{"properties": map[string]any{"probe": map[string]any{"description": "one probe's id (D6): its verdict alone, with every finding", "type": "string"}, "run": map[string]any{"description": "\"true\": run every probe now and answer the verdict", "enum": []string{"true", "false"}, "type": "string"}}, "type": "object"}}, - {Name: "probes", Description: "The registry: what each probe asserts, where it reads its facts, and the finding it writes.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - }}, - {Name: MeshHealers, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0564", Emits: []string{"healer-acted"}, - Serves: []Verb{ - {Name: "healers", Description: "The healers: each registered response to one kind of condition, its repair, its budget and what happens when it is spent; every act taken lately with its outcome, and the mesh-wide brake.", - Input: map[string]any{"properties": map[string]any{"days": map[string]any{"description": "how many days of acts back (default 7)", "type": "string"}}, "type": "object"}}, - {Name: "heal", Description: "Take one healer's act for one open condition now, within its budget and the brake.", - Input: map[string]any{"properties": map[string]any{"key": map[string]any{"description": "the condition's key", "type": "string"}, "why": map[string]any{"description": "why now, kept with the act", "type": "string"}}, "required": []string{"key", "why"}, "type": "object"}}, - {Name: "drill", Description: "Record a drill: something broken on purpose to see the mesh raise and clear it, with what it tests. A drill is never a repair: no healer is wanted for it however often it is run.", - Input: map[string]any{"properties": map[string]any{"condition": map[string]any{"description": "the key of the condition the drill is meant to raise, if any (optional)", "type": "string"}, "what": map[string]any{"description": "what was done on purpose, in a line", "type": "string"}, "why": map[string]any{"description": "what the drill tests", "type": "string"}}, "required": []string{"what", "why"}, "type": "object"}}, - }}, - {Name: MeshWants, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0565", Emits: []string{"policy-changed"}, - Serves: []Verb{ - {Name: "upgrade", Description: "Whether each module's new builds wait for a person: its policy (roll, together or record) and where it comes from (a person, the module, the bus, its irreplaceable data, the default); the bus is never rolled. With module, that one; with module, policy and why, a person's choice for it, recorded as a hand act; with policy default, the choice taken back. With compare, each node's own composition and the assigner's findings.", - Input: map[string]any{"properties": map[string]any{"compare": map[string]any{"description": "\"true\": each node's own composition, by its digest, and the assigner's findings", "enum": []string{"true", "false"}, "type": "string"}, "module": map[string]any{"description": "one module", "type": "string"}, "policy": map[string]any{"description": "with module: roll, together, record, or default to take a person's choice back", "type": "string"}, "why": map[string]any{"description": "with policy: why, required and kept", "type": "string"}}, "type": "object"}}, - {Name: "bus", Description: "What a bus upgrade would do: the fingerprint of the bus module each node runs against the indexer's newest, and the last bus steps from the hand-act log and the assigner's puts of the bus. Read-only: the step itself is the put on the operator's warrant.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - }}, - {Name: MeshHandActs, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0565", Emits: []string{"act-recorded"}, - Serves: []Verb{ - {Name: "hand-act", Description: "Record one act done by hand: what, why, its cause in a word or a condition's kind (the word a second act for the same reason uses) and the condition it addresses. Who did it is the caller the runtime proves, never said by the caller. Said as act-recorded.", - Input: map[string]any{"properties": map[string]any{"cause": map[string]any{"description": "the cause in a word, or a condition's kind", "type": "string"}, "condition": map[string]any{"description": "the key of the condition it addressed, if any (optional)", "type": "string"}, "verb": map[string]any{"description": "the act's verb in the log: a repair when absent, `hand-act drill` for a drill, or a recorder's own", "type": "string"}, "what": map[string]any{"description": "what was done, in a line", "type": "string"}, "why": map[string]any{"description": "why it had to be done by hand", "type": "string"}}, "required": []string{"what", "why"}, "type": "object"}}, - {Name: "hand-acts", Description: "What was done by hand lately, over this log and the controller's: who, why and the cause of each, and the causes recorded twice within a fortnight (a healer is wanted).", - Input: map[string]any{"properties": map[string]any{"cause": map[string]any{"description": "only this cause", "type": "string"}, "days": map[string]any{"description": "how many days back (default 14)", "type": "string"}, "warrants": map[string]any{"description": "\"true\": every warrant recorded", "enum": []string{"true", "false"}, "type": "string"}, "who": map[string]any{"description": "only acts whose who contains this", "type": "string"}}, "type": "object"}}, - {Name: "warranted", Description: "Record what a module did on the operator's warrant: who chose, through which channel, with which proofs and which answer read from the router's own record of that module's ask, never from the caller; once per ask. Refused for an ask still open, ended without a choice, or not that module's.", - Input: map[string]any{"properties": map[string]any{"ask": map[string]any{"description": "its ask's id", "type": "string"}, "asker": map[string]any{"description": "the module that asked", "type": "string"}}, "required": []string{"asker", "ask"}, "type": "object"}}, - }}, - {Name: MeshEnrolment, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0587", Emits: []string{"enrolment-issued", "node-enrolled", "enrolment-refused", "enrolment-revoked", "enrolment-expired"}, - Serves: []Verb{ - {Name: "issue", Description: "Issue a one-time token for a new machine, on the operator's Approve: refused at once, naming every finding, for a name that is not a node name or that a node record, a placement key or an open enrolment holds, and for every call before the controller's lease is handed away. Otherwise reserves the lowest free private-network address, asks the operator under the seat's own name and, on the warrant, adds the node to placement with its tunnel key, asks the vault for the token and says enrolment-issued. The token itself is read only through tokens.", - Input: map[string]any{"properties": map[string]any{"expires": map[string]any{"description": "how long the token lasts, a duration such as 1h or 24h: one hour when not given, at most 24 hours", "type": "string"}, "node": map[string]any{"description": "the new machine's name in the mesh", "type": "string"}, "overlay_key": map[string]any{"description": "the new machine's private network (tunnel) public key, as `nox-mesh-host key` prints it", "type": "string"}, "why": map[string]any{"description": "why it joins, shown to the operator with the ask", "type": "string"}}, "required": []string{"node", "why", "overlay_key"}, "type": "object"}}, - {Name: "redeem", Description: "Spend a token: asked by a joining node-engine dialled as enrol. alone, with the public halves of its keys and its identity key's proof over them. Spends the token, asks the vault for the node's account and certificate, writes the node into placement, and answers the account sealed to the node, its certificate and the vault's public half, signed by the vault. Asked again with the same keys it finishes what is not done and answers the same; with other keys it is refused.", - Input: map[string]any{"additionalProperties": false, "properties": map[string]any{"node": map[string]any{"description": "the node the token was issued for", "type": "string"}, "overlay_key": map[string]any{"description": "the private network key's public half", "type": "string"}, "profile": map[string]any{"description": "what the machine is, as its record says it", "type": "object"}, "proof": map[string]any{"description": "the identity key's signature over the redeem's proof bytes (delivery.RedeemProof), base64", "type": "string"}, "public_key": map[string]any{"description": "the identity key's public half, base64", "type": "string"}, "sealing_key": map[string]any{"description": "the sealing key's public half, which the account is sealed to", "type": "string"}, "serving_key": map[string]any{"description": "the serving key's public half, which the certificate binds", "type": "string"}, "tunnel": map[string]any{"description": "a tunnel found on the machine: everything but its private key", "type": "object"}}, "required": []string{"node", "public_key", "sealing_key", "proof"}, "type": "object"}}, - {Name: "confirm", Description: "Confirm an enrolment: asked by the node-engine dialled as its own node login alone, the runtime's proof of that caller being the confirmation. Says node-enrolled and ends the enrolment.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "revoke", Description: "End an enrolment at any stage, with why, without an Approve: withdraws an open ask, deletes the node's key and its address, has the vault retire an account it already minted for it, records the act as a hand act and says enrolment-revoked.", - Input: map[string]any{"properties": map[string]any{"node": map[string]any{"description": "the node", "type": "string"}, "why": map[string]any{"description": "why, recorded with the hand act", "type": "string"}}, "required": []string{"node", "why"}, "type": "object"}}, - {Name: "tokens", Description: "Read every open enrolment, or one: its stage, expiry, ask and revision, the bucket's fill against its cap. For a node at live, its token sealed to the operator key, asked of the vault at that moment and relayed as the vault sealed it. With history, the ended enrolments from the seat's events, as far back as the events are kept.", - Input: map[string]any{"properties": map[string]any{"history": map[string]any{"description": "\"true\": the ended enrolments too", "enum": []string{"true", "false"}, "type": "string"}, "node": map[string]any{"description": "one node", "type": "string"}}, "type": "object"}}, - {Name: "again", Description: "Raise an issue's ask again when the operator's channel does not hold it, and nothing more.", - Input: map[string]any{"properties": map[string]any{"node": map[string]any{"description": "the node whose ask is raised again", "type": "string"}}, "required": []string{"node"}, "type": "object"}}, - }}, - {Name: MeshArtifactSweep, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0571", Emits: []string{"artifacts-gone"}, - Serves: []Verb{ - {Name: "artifacts", Description: "Every artifact the mesh made that lives in its artifact store, each with its repository, digest, kind (image or archive) and state: kept, and why (a want names it, or one of the five newest builds of its module in the builder's log), eligible (made, kept for no reason, not yet let go), or unrecorded (in the store and named by nothing the mesh made: never removed). Reads only.", - Input: map[string]any{"properties": map[string]any{"collected": map[string]any{"description": "\"true\": list the collected references too (counted always)", "enum": []string{"true", "false"}, "type": "string"}, "repository": map[string]any{"description": "only this repository's references, as / (the counts stay whole)", "type": "string"}}, "type": "object"}}, - {Name: "held", Description: "Whether the artifact store holds each reference given, asked with one HEAD each: held, gone (the store answered 404) or unknown with why. The builder asks it before it reuses a done key. Reads only.", - Input: map[string]any{"properties": map[string]any{"references": map[string]any{"description": "the artifacts' references, artifact-store:////blobs/sha256:: a list, or one text separated by commas", "items": map[string]any{"type": "string"}, "type": "array"}}, "required": []string{"references"}, "type": "object"}}, - {Name: "collect", Description: "The artifact store's sweep, now: what is eligible is let go of. Without confirm a dry run: what would be let go, nothing held or deleted. With confirm and why: every kept archive held first, then each eligible artifact let go of, oldest first, an index with the platform manifests no kept index names, each deletion recorded in the hand-act log. Bounded by most (500 by default, at most 5000) and 45 seconds; what is left is said. Never touches a digest the mesh did not make.", - Input: map[string]any{"properties": map[string]any{"confirm": map[string]any{"description": "\"true\": let go of what is eligible (needs why); without it nothing is done", "enum": []string{"true", "false"}, "type": "string"}, "most": map[string]any{"description": "let go of at most this many (500 by default, at most 5000)", "type": "string"}, "why": map[string]any{"description": "with confirm: why, required and recorded", "type": "string"}}, "type": "object"}}, - {Name: "mirrors", Description: "The bases the mesh copied into its artifact store, each kept (a kept build stood on it), eligible (only builds no longer kept stood on it) or unrecorded (no build the builder's log holds stood on it: never removed), by what the builder's log says each build stood on. Records nothing by hand.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "images", Description: "Every container image one machine's want and have name, each with the resources naming it: what the machine keeps when it prunes its images. Reads only.", - Input: map[string]any{"properties": map[string]any{"node": map[string]any{"description": "the machine's name", "type": "string"}}, "required": []string{"node"}, "type": "object"}}, - }}, - {Name: NpmPackageRegistry, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0109", Delivers: "npm-package-registry", Emits: []string{"published", "refused", "removed", "kept"}, - Serves: []Verb{ - {Name: "publish", Description: "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).", Optional: true, - Input: map[string]any{"properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "parts": map[string]any{"description": "every part's digest and size; none when the digest is already held", "items": map[string]any{"description": "one part of a transfer", "properties": map[string]any{"digest": map[string]any{"description": "the part's digest, sha256:: an image's layer or config, a package's file, a bundle's blob", "type": "string"}, "size": map[string]any{"description": "its size in bytes", "type": "integer"}}, "required": []string{"digest", "size"}, "type": "object"}, "type": "array"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "transfer": map[string]any{"description": "the transfer's name, the caller's own; the same name opens the same transfer again", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"transfer", "name", "kind", "digest"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"held": map[string]any{"description": "the digest is held already: nothing to send; the outcome follows as an event", "type": "boolean"}, "lacks": map[string]any{"description": "the digests of the parts the holder lacks, to send by publish-part", "items": map[string]any{"type": "string"}, "type": "array"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer"}, "type": "object"}}, - {Name: "publish-part", Description: "Send one chunk of one part of an open transfer; the last chunk of the last part ends the transfer, and the holder hashes what it received, asks builder_versions, stores it and says published or refused. A chunk's message stays under the bus's largest message (1 MiB), so at most 760 KiB of data, base64.", Optional: true, - Input: map[string]any{"properties": map[string]any{"data": map[string]any{"description": "the chunk's bytes, base64 (at most 760 KiB before encoding)", "type": "string"}, "last": map[string]any{"description": "this chunk is the part's last", "type": "boolean"}, "part": map[string]any{"description": "the part's digest", "type": "string"}, "sequence": map[string]any{"description": "the chunk's place in its part, from 0", "type": "integer"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer", "part", "sequence", "last", "data"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"complete": map[string]any{"description": "the part is whole and its bytes match its digest", "type": "boolean"}, "part": map[string]any{"description": "the part", "type": "string"}, "received": map[string]any{"description": "the chunks of the part held so far", "type": "integer"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer", "part"}, "type": "object"}}, - {Name: "versions", Description: "The versions the seat holds of one name, newest first: each one's version, tag, digest, kind and when it was published.", Optional: true, - Input: map[string]any{"properties": map[string]any{"kind": map[string]any{"description": "narrowed to one kind (optional)", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}}, "required": []string{"name"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the name", "type": "string"}, "versions": map[string]any{"description": "newest first", "items": map[string]any{"description": "one published thing, as every registry event and answer names it", "properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"name", "kind", "digest"}, "type": "object"}, "type": "array"}}, "required": []string{"name", "versions"}, "type": "object"}}, - {Name: "remove", Description: "Remove one tagged version; the holder refuses an untagged one but for the retention removal of a bundle, archive or copied base by the artifact sweep's holder. Every removal emits removed with its reason. A tagged version the store cannot remove alone, because another tag names its digest, stays whole, its tag and bytes kept: the answer says kept with the reason shared-digest and the tags still naming the digest, removed false, and kept is emitted, never removed; the caller ends there without a retry.", Optional: true, - Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest (for a bundle, archive or copied base)", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "why it goes", "enum": []string{"tag-gone", "retention"}, "type": "string"}, "tag": map[string]any{"description": "the tag (for a tagged version)", "type": "string"}, "version": map[string]any{"description": "the version", "type": "string"}}, "required": []string{"name", "reason"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"artifact": map[string]any{"description": "one published thing, as every registry event and answer names it", "properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"name", "kind", "digest"}, "type": "object"}, "kept": map[string]any{"description": "it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)", "type": "boolean"}, "reason": map[string]any{"description": "why it was kept: shared-digest, another tag names its digest", "enum": []string{"shared-digest"}, "type": "string"}, "removed": map[string]any{"description": "it is gone", "type": "boolean"}, "tags": map[string]any{"description": "the other tags still naming its digest, when kept", "items": map[string]any{"type": "string"}, "type": "array"}}, "required": []string{"removed"}, "type": "object"}}, - }}, - {Name: GoPackageRegistry, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0605", Delivers: "go-package-registry", Emits: []string{"published", "refused", "removed", "kept"}, - Serves: []Verb{ - {Name: "publish", Description: "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).", Optional: true, - Input: map[string]any{"properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "parts": map[string]any{"description": "every part's digest and size; none when the digest is already held", "items": map[string]any{"description": "one part of a transfer", "properties": map[string]any{"digest": map[string]any{"description": "the part's digest, sha256:: an image's layer or config, a package's file, a bundle's blob", "type": "string"}, "size": map[string]any{"description": "its size in bytes", "type": "integer"}}, "required": []string{"digest", "size"}, "type": "object"}, "type": "array"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "transfer": map[string]any{"description": "the transfer's name, the caller's own; the same name opens the same transfer again", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"transfer", "name", "kind", "digest"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"held": map[string]any{"description": "the digest is held already: nothing to send; the outcome follows as an event", "type": "boolean"}, "lacks": map[string]any{"description": "the digests of the parts the holder lacks, to send by publish-part", "items": map[string]any{"type": "string"}, "type": "array"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer"}, "type": "object"}}, - {Name: "publish-part", Description: "Send one chunk of one part of an open transfer; the last chunk of the last part ends the transfer, and the holder hashes what it received, asks builder_versions, stores it and says published or refused. A chunk's message stays under the bus's largest message (1 MiB), so at most 760 KiB of data, base64.", Optional: true, - Input: map[string]any{"properties": map[string]any{"data": map[string]any{"description": "the chunk's bytes, base64 (at most 760 KiB before encoding)", "type": "string"}, "last": map[string]any{"description": "this chunk is the part's last", "type": "boolean"}, "part": map[string]any{"description": "the part's digest", "type": "string"}, "sequence": map[string]any{"description": "the chunk's place in its part, from 0", "type": "integer"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer", "part", "sequence", "last", "data"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"complete": map[string]any{"description": "the part is whole and its bytes match its digest", "type": "boolean"}, "part": map[string]any{"description": "the part", "type": "string"}, "received": map[string]any{"description": "the chunks of the part held so far", "type": "integer"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer", "part"}, "type": "object"}}, - {Name: "versions", Description: "The versions the seat holds of one name, newest first: each one's version, tag, digest, kind and when it was published.", Optional: true, - Input: map[string]any{"properties": map[string]any{"kind": map[string]any{"description": "narrowed to one kind (optional)", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}}, "required": []string{"name"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the name", "type": "string"}, "versions": map[string]any{"description": "newest first", "items": map[string]any{"description": "one published thing, as every registry event and answer names it", "properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"name", "kind", "digest"}, "type": "object"}, "type": "array"}}, "required": []string{"name", "versions"}, "type": "object"}}, - {Name: "remove", Description: "Remove one tagged version; the holder refuses an untagged one but for the retention removal of a bundle, archive or copied base by the artifact sweep's holder. Every removal emits removed with its reason. A tagged version the store cannot remove alone, because another tag names its digest, stays whole, its tag and bytes kept: the answer says kept with the reason shared-digest and the tags still naming the digest, removed false, and kept is emitted, never removed; the caller ends there without a retry.", Optional: true, - Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest (for a bundle, archive or copied base)", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "why it goes", "enum": []string{"tag-gone", "retention"}, "type": "string"}, "tag": map[string]any{"description": "the tag (for a tagged version)", "type": "string"}, "version": map[string]any{"description": "the version", "type": "string"}}, "required": []string{"name", "reason"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"artifact": map[string]any{"description": "one published thing, as every registry event and answer names it", "properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"name", "kind", "digest"}, "type": "object"}, "kept": map[string]any{"description": "it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)", "type": "boolean"}, "reason": map[string]any{"description": "why it was kept: shared-digest, another tag names its digest", "enum": []string{"shared-digest"}, "type": "string"}, "removed": map[string]any{"description": "it is gone", "type": "boolean"}, "tags": map[string]any{"description": "the other tags still naming its digest, when kept", "items": map[string]any{"type": "string"}, "type": "array"}}, "required": []string{"removed"}, "type": "object"}}, - }}, - {Name: ContainerImageRegistry, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0605", Emits: []string{"published", "refused", "removed", "kept", "held", "released"}, - Serves: []Verb{ - {Name: "publish", Description: "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).", Optional: true, - Input: map[string]any{"properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "parts": map[string]any{"description": "every part's digest and size; none when the digest is already held", "items": map[string]any{"description": "one part of a transfer", "properties": map[string]any{"digest": map[string]any{"description": "the part's digest, sha256:: an image's layer or config, a package's file, a bundle's blob", "type": "string"}, "size": map[string]any{"description": "its size in bytes", "type": "integer"}}, "required": []string{"digest", "size"}, "type": "object"}, "type": "array"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "transfer": map[string]any{"description": "the transfer's name, the caller's own; the same name opens the same transfer again", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"transfer", "name", "kind", "digest"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"held": map[string]any{"description": "the digest is held already: nothing to send; the outcome follows as an event", "type": "boolean"}, "lacks": map[string]any{"description": "the digests of the parts the holder lacks, to send by publish-part", "items": map[string]any{"type": "string"}, "type": "array"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer"}, "type": "object"}}, - {Name: "publish-part", Description: "Send one chunk of one part of an open transfer; the last chunk of the last part ends the transfer, and the holder hashes what it received, asks builder_versions, stores it and says published or refused. A chunk's message stays under the bus's largest message (1 MiB), so at most 760 KiB of data, base64.", Optional: true, - Input: map[string]any{"properties": map[string]any{"data": map[string]any{"description": "the chunk's bytes, base64 (at most 760 KiB before encoding)", "type": "string"}, "last": map[string]any{"description": "this chunk is the part's last", "type": "boolean"}, "part": map[string]any{"description": "the part's digest", "type": "string"}, "sequence": map[string]any{"description": "the chunk's place in its part, from 0", "type": "integer"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer", "part", "sequence", "last", "data"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"complete": map[string]any{"description": "the part is whole and its bytes match its digest", "type": "boolean"}, "part": map[string]any{"description": "the part", "type": "string"}, "received": map[string]any{"description": "the chunks of the part held so far", "type": "integer"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer", "part"}, "type": "object"}}, - {Name: "versions", Description: "The versions the seat holds of one name, newest first: each one's version, tag, digest, kind and when it was published.", Optional: true, - Input: map[string]any{"properties": map[string]any{"kind": map[string]any{"description": "narrowed to one kind (optional)", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}}, "required": []string{"name"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the name", "type": "string"}, "versions": map[string]any{"description": "newest first", "items": map[string]any{"description": "one published thing, as every registry event and answer names it", "properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"name", "kind", "digest"}, "type": "object"}, "type": "array"}}, "required": []string{"name", "versions"}, "type": "object"}}, - {Name: "remove", Description: "Remove one tagged version; the holder refuses an untagged one but for the retention removal of a bundle, archive or copied base by the artifact sweep's holder. Every removal emits removed with its reason. A tagged version the store cannot remove alone, because another tag names its digest, stays whole, its tag and bytes kept: the answer says kept with the reason shared-digest and the tags still naming the digest, removed false, and kept is emitted, never removed; the caller ends there without a retry.", Optional: true, - Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest (for a bundle, archive or copied base)", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "why it goes", "enum": []string{"tag-gone", "retention"}, "type": "string"}, "tag": map[string]any{"description": "the tag (for a tagged version)", "type": "string"}, "version": map[string]any{"description": "the version", "type": "string"}}, "required": []string{"name", "reason"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"artifact": map[string]any{"description": "one published thing, as every registry event and answer names it", "properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"name", "kind", "digest"}, "type": "object"}, "kept": map[string]any{"description": "it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)", "type": "boolean"}, "reason": map[string]any{"description": "why it was kept: shared-digest, another tag names its digest", "enum": []string{"shared-digest"}, "type": "string"}, "removed": map[string]any{"description": "it is gone", "type": "boolean"}, "tags": map[string]any{"description": "the other tags still naming its digest, when kept", "items": map[string]any{"type": "string"}, "type": "array"}}, "required": []string{"removed"}, "type": "object"}}, - {Name: "mirror", Description: "Copy an outside image into the store by its pinned digest; emits published or refused.", Optional: true, - Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "its pinned digest, sha256:", "type": "string"}, "name": map[string]any{"description": "the name it is kept under", "type": "string"}, "source": map[string]any{"description": "the outside image's reference", "type": "string"}}, "required": []string{"source", "digest", "name"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest stored", "type": "string"}, "reference": map[string]any{"description": "the reference the seat answers", "type": "string"}}, "required": []string{"reference", "digest"}, "type": "object"}}, - {Name: "tags", Description: "The tags of one image and the digest each names.", Optional: true, - Input: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the image", "type": "string"}}, "required": []string{"name"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the image", "type": "string"}, "tags": map[string]any{"description": "every tag", "items": map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "its digest", "type": "string"}, "tag": map[string]any{"description": "the tag", "type": "string"}}, "required": []string{"tag", "digest"}, "type": "object"}, "type": "array"}}, "required": []string{"name", "tags"}, "type": "object"}}, - {Name: "hold", Description: "Keep one untagged bundle, archive or copied base from removal by retention, as the artifact sweep's rules keep it (ADR 0617 §2); never an image of the holder's own. Emits held.", Optional: true, - Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "which of the sweep's rules keeps it", "type": "string"}}, "required": []string{"name", "digest", "reason"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"held": map[string]any{"description": "it is held", "type": "boolean"}, "since": map[string]any{"description": "when the hold began, RFC 3339", "type": "string"}}, "required": []string{"held"}, "type": "object"}}, - {Name: "release", Description: "Let a hold go; emits released. A release of what is not held answers released false and emits nothing, never an error: the outcome asked holds already. By hand only through the holder's own tool, the operator's session as the proved caller, recorded as a hand act.", Optional: true, - Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "why it is let go", "type": "string"}}, "required": []string{"name", "digest", "reason"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"released": map[string]any{"description": "a hold was let go; false when there was none", "type": "boolean"}}, "required": []string{"released"}, "type": "object"}}, - {Name: "list", Description: "What the store holds of one repository, or of every repository the seat holds when none is named: every image manifest and index, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).", Optional: true, - Input: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the repository, /; every repository of the seat when not given", "type": "string"}}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"repositories": map[string]any{"description": "each repository named, in order of its name", "items": map[string]any{"properties": map[string]any{"manifests": map[string]any{"description": "each manifest it holds, by digest", "items": map[string]any{"properties": map[string]any{"artifact": map[string]any{"description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by", "type": "string"}, "digest": map[string]any{"description": "the manifest's digest, sha256:", "type": "string"}, "held": map[string]any{"description": "a hold keeps it from retention", "type": "boolean"}, "index": map[string]any{"description": "it is an index, naming a manifest per platform", "type": "boolean"}, "platforms": map[string]any{"description": "for an index, the digests of the manifests it names", "items": map[string]any{"type": "string"}, "type": "array"}, "size": map[string]any{"description": "its bytes: its own content and every blob it names", "type": "integer"}, "tags": map[string]any{"description": "the tags naming it, none for an untagged one", "items": map[string]any{"type": "string"}, "type": "array"}}, "required": []string{"digest", "index", "size", "held"}, "type": "object"}, "type": "array"}, "name": map[string]any{"description": "the repository", "type": "string"}}, "required": []string{"name", "manifests"}, "type": "object"}, "type": "array"}}, "required": []string{"repositories"}, "type": "object"}}, - }}, - {Name: OciArtifactRegistry, Scope: "mesh", Grain: "whole", Decision: "novox/hq ADR 0605", Emits: []string{"published", "refused", "removed", "kept", "held", "released"}, - Serves: []Verb{ - {Name: "publish", Description: "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).", Optional: true, - Input: map[string]any{"properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "parts": map[string]any{"description": "every part's digest and size; none when the digest is already held", "items": map[string]any{"description": "one part of a transfer", "properties": map[string]any{"digest": map[string]any{"description": "the part's digest, sha256:: an image's layer or config, a package's file, a bundle's blob", "type": "string"}, "size": map[string]any{"description": "its size in bytes", "type": "integer"}}, "required": []string{"digest", "size"}, "type": "object"}, "type": "array"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "transfer": map[string]any{"description": "the transfer's name, the caller's own; the same name opens the same transfer again", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"transfer", "name", "kind", "digest"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"held": map[string]any{"description": "the digest is held already: nothing to send; the outcome follows as an event", "type": "boolean"}, "lacks": map[string]any{"description": "the digests of the parts the holder lacks, to send by publish-part", "items": map[string]any{"type": "string"}, "type": "array"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer"}, "type": "object"}}, - {Name: "publish-part", Description: "Send one chunk of one part of an open transfer; the last chunk of the last part ends the transfer, and the holder hashes what it received, asks builder_versions, stores it and says published or refused. A chunk's message stays under the bus's largest message (1 MiB), so at most 760 KiB of data, base64.", Optional: true, - Input: map[string]any{"properties": map[string]any{"data": map[string]any{"description": "the chunk's bytes, base64 (at most 760 KiB before encoding)", "type": "string"}, "last": map[string]any{"description": "this chunk is the part's last", "type": "boolean"}, "part": map[string]any{"description": "the part's digest", "type": "string"}, "sequence": map[string]any{"description": "the chunk's place in its part, from 0", "type": "integer"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer", "part", "sequence", "last", "data"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"complete": map[string]any{"description": "the part is whole and its bytes match its digest", "type": "boolean"}, "part": map[string]any{"description": "the part", "type": "string"}, "received": map[string]any{"description": "the chunks of the part held so far", "type": "integer"}, "transfer": map[string]any{"description": "the transfer", "type": "string"}}, "required": []string{"transfer", "part"}, "type": "object"}}, - {Name: "versions", Description: "The versions the seat holds of one name, newest first: each one's version, tag, digest, kind and when it was published.", Optional: true, - Input: map[string]any{"properties": map[string]any{"kind": map[string]any{"description": "narrowed to one kind (optional)", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}}, "required": []string{"name"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the name", "type": "string"}, "versions": map[string]any{"description": "newest first", "items": map[string]any{"description": "one published thing, as every registry event and answer names it", "properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"name", "kind", "digest"}, "type": "object"}, "type": "array"}}, "required": []string{"name", "versions"}, "type": "object"}}, - {Name: "remove", Description: "Remove one tagged version; the holder refuses an untagged one but for the retention removal of a bundle, archive or copied base by the artifact sweep's holder. Every removal emits removed with its reason. A tagged version the store cannot remove alone, because another tag names its digest, stays whole, its tag and bytes kept: the answer says kept with the reason shared-digest and the tags still naming the digest, removed false, and kept is emitted, never removed; the caller ends there without a retry.", Optional: true, - Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest (for a bundle, archive or copied base)", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "why it goes", "enum": []string{"tag-gone", "retention"}, "type": "string"}, "tag": map[string]any{"description": "the tag (for a tagged version)", "type": "string"}, "version": map[string]any{"description": "the version", "type": "string"}}, "required": []string{"name", "reason"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"artifact": map[string]any{"description": "one published thing, as every registry event and answer names it", "properties": map[string]any{"commit": map[string]any{"description": "the source commit it was built from", "type": "string"}, "digest": map[string]any{"description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", "type": "string"}, "fingerprint": map[string]any{"description": "the build's fingerprint", "type": "string"}, "kind": map[string]any{"description": "what it is", "enum": []string{"package", "image", "bundle", "archive", "upstream"}, "type": "string"}, "name": map[string]any{"description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", "type": "string"}, "tag": map[string]any{"description": "the tag it carries, for a tagged version", "type": "string"}, "version": map[string]any{"description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", "type": "string"}}, "required": []string{"name", "kind", "digest"}, "type": "object"}, "kept": map[string]any{"description": "it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)", "type": "boolean"}, "reason": map[string]any{"description": "why it was kept: shared-digest, another tag names its digest", "enum": []string{"shared-digest"}, "type": "string"}, "removed": map[string]any{"description": "it is gone", "type": "boolean"}, "tags": map[string]any{"description": "the other tags still naming its digest, when kept", "items": map[string]any{"type": "string"}, "type": "array"}}, "required": []string{"removed"}, "type": "object"}}, - {Name: "hold", Description: "Keep one untagged bundle, archive or copied base from removal by retention, as the artifact sweep's rules keep it (ADR 0617 §2); never an image of the holder's own. Emits held.", Optional: true, - Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "which of the sweep's rules keeps it", "type": "string"}}, "required": []string{"name", "digest", "reason"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"held": map[string]any{"description": "it is held", "type": "boolean"}, "since": map[string]any{"description": "when the hold began, RFC 3339", "type": "string"}}, "required": []string{"held"}, "type": "object"}}, - {Name: "release", Description: "Let a hold go; emits released. A release of what is not held answers released false and emits nothing, never an error: the outcome asked holds already. By hand only through the holder's own tool, the operator's session as the proved caller, recorded as a hand act.", Optional: true, - Input: map[string]any{"properties": map[string]any{"digest": map[string]any{"description": "the digest", "type": "string"}, "name": map[string]any{"description": "the name", "type": "string"}, "reason": map[string]any{"description": "why it is let go", "type": "string"}}, "required": []string{"name", "digest", "reason"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"released": map[string]any{"description": "a hold was let go; false when there was none", "type": "boolean"}}, "required": []string{"released"}, "type": "object"}}, - {Name: "list", Description: "What the store holds of one repository, or of every repository the seat holds when none is named: every manifest keeping one bundle or archive, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).", Optional: true, - Input: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the repository, /; every repository of the seat when not given", "type": "string"}}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"repositories": map[string]any{"description": "each repository named, in order of its name", "items": map[string]any{"properties": map[string]any{"manifests": map[string]any{"description": "each manifest it holds, by digest", "items": map[string]any{"properties": map[string]any{"artifact": map[string]any{"description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by", "type": "string"}, "digest": map[string]any{"description": "the manifest's digest, sha256:", "type": "string"}, "held": map[string]any{"description": "a hold keeps it from retention", "type": "boolean"}, "index": map[string]any{"description": "it is an index, naming a manifest per platform", "type": "boolean"}, "platforms": map[string]any{"description": "for an index, the digests of the manifests it names", "items": map[string]any{"type": "string"}, "type": "array"}, "size": map[string]any{"description": "its bytes: its own content and every blob it names", "type": "integer"}, "tags": map[string]any{"description": "the tags naming it, none for an untagged one", "items": map[string]any{"type": "string"}, "type": "array"}}, "required": []string{"digest", "index", "size", "held"}, "type": "object"}, "type": "array"}, "name": map[string]any{"description": "the repository", "type": "string"}}, "required": []string{"name", "manifests"}, "type": "object"}, "type": "array"}}, "required": []string{"repositories"}, "type": "object"}}, - }}, - {Name: Git, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0111", Delivers: "git"}, - {Name: NodeBuildGo, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0525, ADR 0536, ADR 0553", Accepts: []string{"delivery", "check", "recheck"}, PulledByKind: true, Yields: true, - Serves: []Verb{ - {Name: "current", Description: "The Go asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "pause", Description: "Take no new Go ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "resume", Description: "Take Go asks again on this node.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - }}, - {Name: NodeBuildNode, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0525, ADR 0536, ADR 0553", Accepts: []string{"delivery", "check", "recheck"}, PulledByKind: true, Yields: true, - Serves: []Verb{ - {Name: "current", Description: "The Node asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "pause", Description: "Take no new Node ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "resume", Description: "Take Node asks again on this node.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - }}, - {Name: NodeBuildImage, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0525, ADR 0536, ADR 0553", Accepts: []string{"delivery", "check", "recheck"}, PulledByKind: true, Yields: true, - Serves: []Verb{ - {Name: "current", Description: "The image asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "pause", Description: "Take no new image ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "resume", Description: "Take image asks again on this node.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - }}, - {Name: MeshDnsResolver, Scope: "mesh", Grain: "seat-list", Decision: "novox/hq ADR 0194, ADR 0223", Replicated: true, Delivers: "wildcard-resolution"}, - {Name: NodeHostname, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0199, ADR 0223", - Serves: []Verb{ - {Name: "entries", Description: "Every line of this machine's /etc/hosts, each marked whose it is: the operator's, or the block of the module or tool that writes it.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, Replaces: []string{"cat /etc/hosts", "getent hosts"}}, - {Name: "add", Description: "Add one address and its names to the operator's lines of this machine's /etc/hosts — a name for this machine's own programs, not the mesh's.", - Input: map[string]any{"properties": map[string]any{"address": map[string]any{"description": "the IPv4 or IPv6 address", "type": "string"}, "names": map[string]any{"description": "the names for it, separated by spaces", "type": "string"}}, "required": []string{"address", "names"}, "type": "object"}, Replaces: []string{"edit /etc/hosts", "HOSTALIASES"}}, - {Name: "remove", Description: "Remove one name, or every line of one address, from the operator's lines of this machine's /etc/hosts. A line a module writes is refused, naming the module.", - Input: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "a host name, or an address to remove every line of", "type": "string"}}, "required": []string{"name"}, "type": "object"}, Replaces: []string{"sed -i /etc/hosts"}}, - }, Paths: []Path{{Path: "/etc/hosts", StepsBackFor: ""}, {Path: "/etc/hostname", StepsBackFor: ""}}}, - {Name: NodeIntrusionPrevention, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0121", - Serves: []Verb{ - {Name: "status", Description: "Every jail on this machine with how many it is watching and holding now, and the totals since the jail started; one jail's detail when named.", - Input: map[string]any{"properties": map[string]any{"jail": map[string]any{"description": "one jail (optional)", "type": "string"}}, "type": "object"}, Replaces: []string{"fail2ban-client status"}}, - {Name: "banned", Description: "Every address banned on this machine right now, with the jail that holds it and when the ban ends.", - Input: map[string]any{"properties": map[string]any{"jail": map[string]any{"description": "one jail (optional)", "type": "string"}}, "type": "object"}, Replaces: []string{"fail2ban-client banned"}}, - {Name: "ban", Description: "Ban one address in one jail now, for the jail's ban time — an operator's act on the live ban list, which the mesh never writes itself.", - Input: map[string]any{"properties": map[string]any{"ip": map[string]any{"description": "the address", "type": "string"}, "jail": map[string]any{"description": "the jail to hold it", "type": "string"}}, "required": []string{"ip", "jail"}, "type": "object"}, Replaces: []string{"fail2ban-client set banip"}}, - {Name: "unban", Description: "Let one address go, from one jail or from every jail when none is named.", - Input: map[string]any{"properties": map[string]any{"ip": map[string]any{"description": "the address", "type": "string"}, "jail": map[string]any{"description": "one jail (optional)", "type": "string"}}, "required": []string{"ip"}, "type": "object"}, Replaces: []string{"fail2ban-client unban", "fail2ban-client set unbanip"}}, - }}, - {Name: NodePacketFilter, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0121", - Serves: []Verb{ - {Name: "rules", Description: "The packet filter as this machine enforces it now: the nftables ruleset and, where the tool exists, the legacy filter's listings. Narrowed to one table or chain when asked.", - Input: map[string]any{"properties": map[string]any{"chain": map[string]any{"description": "one chain of that table (optional)", "type": "string"}, "table": map[string]any{"description": "one nftables table, as `family name` (optional)", "type": "string"}}, "type": "object"}, Replaces: []string{"nft list ruleset", "iptables -L", "iptables-save"}}, - {Name: "reload", Description: "Load the mesh's own filter again from the file the mesh writes, and answer with the mesh's table as loaded.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, Replaces: []string{"nft -f"}}, - {Name: "remove", Description: "Remove one rule set the mesh did not write, named exactly as the host reports it (novox/hq ADR 0168) — `chain X (iptables-legacy)` or `table ip6 filter, chain DOCKER-USER`. Refuses the mesh's tables, the runtime's own chains, a built-in chain and an active found firewall's chains. An operator's act, by name, never a flush.", - Input: map[string]any{"properties": map[string]any{"where": map[string]any{"description": "the rule set, as `node show` lists it", "type": "string"}}, "required": []string{"where"}, "type": "object"}, Replaces: []string{"nft delete", "iptables -X"}}, - }}, - {Name: NodeServiceManager, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0177", - Serves: []Verb{ - {Name: "units", Description: "The units the service manager knows in a scope, each with its load, active and sub state; narrowed to a pattern when asked.", - Input: map[string]any{"properties": map[string]any{"pattern": map[string]any{"description": "a glob the unit's name must match (optional)", "type": "string"}, "scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}}, "type": "object"}, Replaces: []string{"systemctl list-units"}}, - {Name: "status", Description: "One unit as the service manager sees it now: its states, whether it starts at boot, its main process, and whether the mesh declares it.", - Input: map[string]any{"properties": map[string]any{"scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}, "unit": map[string]any{"description": "the unit's name, as the service manager knows it", "type": "string"}}, "required": []string{"unit"}, "type": "object"}, Replaces: []string{"systemctl status", "systemctl is-active"}}, - {Name: "start", Description: "Start one unit. For a unit the mesh declares, the answer says the host will restore what its declaration says at the next apply.", - Input: map[string]any{"properties": map[string]any{"scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}, "unit": map[string]any{"description": "the unit's name, as the service manager knows it", "type": "string"}}, "required": []string{"unit"}, "type": "object"}, Replaces: []string{"systemctl start"}}, - {Name: "stop", Description: "Stop one unit; for a mesh-declared unit the answer says the host will restore its declared state.", - Input: map[string]any{"properties": map[string]any{"scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}, "unit": map[string]any{"description": "the unit's name, as the service manager knows it", "type": "string"}}, "required": []string{"unit"}, "type": "object"}, Replaces: []string{"systemctl stop"}}, - {Name: "restart", Description: "Restart one unit.", - Input: map[string]any{"properties": map[string]any{"scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}, "unit": map[string]any{"description": "the unit's name, as the service manager knows it", "type": "string"}}, "required": []string{"unit"}, "type": "object"}, Replaces: []string{"systemctl restart"}}, - {Name: "enable", Description: "Make one unit start at boot (or at the account's login, in user scope).", - Input: map[string]any{"properties": map[string]any{"scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}, "unit": map[string]any{"description": "the unit's name, as the service manager knows it", "type": "string"}}, "required": []string{"unit"}, "type": "object"}, Replaces: []string{"systemctl enable"}}, - {Name: "disable", Description: "Stop one unit starting at boot (or at login, in user scope).", - Input: map[string]any{"properties": map[string]any{"scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}, "unit": map[string]any{"description": "the unit's name, as the service manager knows it", "type": "string"}}, "required": []string{"unit"}, "type": "object"}, Replaces: []string{"systemctl disable"}}, - {Name: "journal", Description: "The last lines of one unit's journal (at most 2000), in a time window and narrowed to a priority and to lines holding a text when asked. A secret the unit printed is shown as [redacted: ].", - Input: map[string]any{"properties": map[string]any{"lines": map[string]any{"description": "how many lines from the end of what matches (default 100, at most 2000)", "type": "string"}, "match": map[string]any{"description": "only the lines holding this text, as written — a fixed string, not a pattern (optional)", "type": "string"}, "priority": map[string]any{"description": "only entries this severe or more: 0-7 or emerg, alert, crit, err, warning, notice, info, debug (optional)", "type": "string"}, "scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}, "since": map[string]any{"description": "the window's start: an RFC 3339 time (2026-10-07T09:30:00Z) or relative to now (-30min, -2h, yesterday) (optional)", "type": "string"}, "unit": map[string]any{"description": "the unit's name, as the service manager knows it", "type": "string"}, "until": map[string]any{"description": "the window's end, in the same forms (optional; now when absent)", "type": "string"}}, "required": []string{"unit"}, "type": "object"}, Replaces: []string{"journalctl"}}, - {Name: "failed", Description: "Every failed unit on this machine, in the system manager and in the operator account's; a manager that does not answer is reported with its error, never as nothing failed.", Optional: true, - Input: map[string]any{"properties": map[string]any{"scope": map[string]any{"description": "\"system\" or \"user\": only that manager (both when absent)", "type": "string"}}, "type": "object"}, Replaces: []string{"systemctl --failed"}}, - {Name: "reset-failed", Description: "Clear one unit's failed record in its manager — its failed state and its start-limit count — and answer its state after. It removes no file and starts nothing.", - Input: map[string]any{"properties": map[string]any{"scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}, "unit": map[string]any{"description": "the unit's name, as the service manager knows it", "type": "string"}}, "required": []string{"unit"}, "type": "object"}, Replaces: []string{"systemctl reset-failed"}}, - {Name: "wanted-by", Description: "What wants, requires or triggers one unit, read-only: the manager's reverse dependencies (WantedBy, RequiredBy, UpheldBy, BoundBy, TriggeredBy and the rest) and every enable link naming it in the scope's configuration directories (*.wants, *.requires, *.upholds), each with where it points and whether it dangles — the account's own directory included in user scope.", - Input: map[string]any{"properties": map[string]any{"scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}, "unit": map[string]any{"description": "the unit's name, as the service manager knows it", "type": "string"}}, "required": []string{"unit"}, "type": "object"}, Replaces: []string{"systemctl list-dependencies --reverse", "systemctl show -p WantedBy", "ls ~/.config/systemd/user/*.wants"}}, - {Name: "unlink-dangling", Description: "Remove the dangling enable links named for one unit — links in a *.wants, *.requires or *.upholds directory of the account's or the machine's own configuration whose target does not exist, which `disable` leaves once the unit's file is gone — on the person's word: `why` is required. Each is recorded (its path, its target, the reason) before it is removed, and the manager reloaded; a link whose target exists, or one a package, a generator or the runtime placed, is left and said.", - Input: map[string]any{"properties": map[string]any{"scope": map[string]any{"description": "\"system\" (the default) or \"user\": the operator account's own manager", "type": "string"}, "unit": map[string]any{"description": "the unit's name, as the service manager knows it", "type": "string"}, "why": map[string]any{"description": "the person's reason for removing the links, kept in the record", "type": "string"}}, "required": []string{"unit", "why"}, "type": "object"}, Replaces: []string{"rm ~/.config/systemd/user/*.wants/", "unlink /etc/systemd/system/*.wants/"}}, - }}, - {Name: NodePackageManager, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0207"}, - {Name: NodeContainerRuntime, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0166, ADR 0207"}, - {Name: NodeEnvironment, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0203"}, - {Name: NodeProvisioner, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0349"}, - {Name: NodeLoginShell, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0204", - Serves: []Verb{ - {Name: "execute", Description: "Run one command on this machine as the operator account, in a non-interactive login shell in its home; answers with what it printed and how it exited.", Optional: true, - Input: map[string]any{"properties": map[string]any{"command": map[string]any{"description": "the command line, as you would type it", "type": "string"}, "timeout_seconds": map[string]any{"description": "give up after this long, at most 25 (default 20)", "type": "string"}}, "required": []string{"command"}, "type": "object"}}, - }}, - {Name: NodePower, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0211"}, - {Name: NodeHotkeys, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0212", - Receives: []Receivable{{Kind: "trigger", Comment: "#"}}}, - {Name: NodeMessageBus, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0215"}, - {Name: NodeBackup, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0214", - Serves: []Verb{ - {Name: "backed-up", Description: "What this machine backs up: each module, what it declared, its last good night, how many restore points are kept and the repository's size.", - Input: map[string]any{"properties": map[string]any{"module": map[string]any{"description": "one module (optional)", "type": "string"}}, "type": "object"}, Replaces: []string{"restic snapshots"}}, - {Name: "now", Description: "Take a backup now, of one module or of every module on this machine — before a migration, a retirement or anything else that could go wrong.", - Input: map[string]any{"properties": map[string]any{"module": map[string]any{"description": "one module (optional)", "type": "string"}}, "type": "object"}, Replaces: []string{"restic backup"}}, - {Name: "restore", Description: "Restore one module's data from a restore point BESIDE the live data, never over it: each directory as .restored-. Swapping it in is a person's act. Lists the restore points when none is named.", - Input: map[string]any{"properties": map[string]any{"module": map[string]any{"description": "the module", "type": "string"}, "path": map[string]any{"description": "one of the module's directories (all of them when omitted)", "type": "string"}, "snapshot": map[string]any{"description": "the restore point (from `backed-up`; the newest when omitted)", "type": "string"}}, "required": []string{"module"}, "type": "object"}, Replaces: []string{"restic restore"}}, - }, - Receives: []Receivable{{Kind: "backup", Comment: "#", Dirs: true}, {Kind: "data", Comment: "#", Dirs: true}}}, - {Name: ThePrivateNetwork, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0110"}, - {Name: NodeUplink, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0117", - Serves: []Verb{ - {Name: "resolvers", Description: "The machine's resolver file as it is now: the resolvers, search domains and options it lists, whether it is the file the mesh declares, and when it is not, who wrote it as far as the machine shows — its header, a backup a VPN client left beside it, a link in its place, a known writer running.", Optional: true, - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "links", Description: "Every network link on the machine: its state, its addresses, whether the default route leaves through it, and the resolvers and search domains the network manager knows for it — a VPN's tunnel included.", Optional: true, - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - }, Paths: []Path{{Path: "/etc/resolv.conf", StepsBackFor: "node-resolver"}}}, - {Name: NodeResolver, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0247", - Serves: []Verb{ - {Name: "routes", Description: "What this machine's own resolver sends where: the mesh's resolvers, which answer every name not routed elsewhere, and each link given servers of its own with the domains routed to them. Also the resolver file's outside writes it kept, newest first: when, who wrote it as far as the file says, whether a module took it, and when the resolver's own file was put back.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, Replaces: []string{"resolvectl status", "resolvectl dns", "resolvectl domain"}}, - {Name: "route", Description: "Send these domains, and every name under them, to these servers over this link — and only them: the link is never the machine's default route for names, and the mesh's own domain is refused. Replaces whatever the link was given before. A link that goes away takes its route with it.", - Input: map[string]any{"properties": map[string]any{"domains": map[string]any{"description": "the domains to route there, separated by spaces or commas", "type": "string"}, "link": map[string]any{"description": "the network link the servers are reached over, by name (a VPN's tunnel interface)", "type": "string"}, "servers": map[string]any{"description": "the servers' addresses, separated by spaces or commas", "type": "string"}}, "required": []string{"link", "domains", "servers"}, "type": "object"}, Replaces: []string{"resolvectl dns", "resolvectl domain", "resolvectl default-route"}}, - {Name: "unroute", Description: "Take one link's route away: its domains go back to the mesh's resolvers. Nothing changes when the link has none.", - Input: map[string]any{"properties": map[string]any{"link": map[string]any{"description": "the network link, by name", "type": "string"}}, "required": []string{"link"}, "type": "object"}, Replaces: []string{"resolvectl revert"}}, - }, Paths: []Path{{Path: "/etc/resolv.conf", StepsBackFor: ""}}}, - {Name: NodeNfsServer, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0263", - Serves: []Verb{ - {Name: "exports", Description: "Every share this machine exports: its name, its path, read-write or read-only, the owner every client is mapped to (uid and gid), each node it is exported to with that node's private address and access (only the nodes the server grants it to and that ask for it), what is granted and not asked for or asked for and not granted, and whether the kernel holds it now. Also the exports found that are not the mesh's: a dataset's sharenfs property, a line in /etc/exports.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, Replaces: []string{"exportfs -v", "cat /etc/exports", "zfs get sharenfs"}}, - {Name: "clients", Description: "Which machines have mounted which share now, as the NFS server knows its clients.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, Replaces: []string{"ss -tn sport = :2049", "cat /proc/fs/nfsd/clients/*/info"}}, - {Name: "test", Description: "Whether this machine exports one share for the mesh now, and whether its NFS service is up; with an address, also whether the share is exported to that address: a node's own private address, when the server grants it the share and the node asks for it. What a machine mounting the share asks before it mounts.", - Input: map[string]any{"properties": map[string]any{"address": map[string]any{"description": "a node's private address, to ask whether the share is exported to it (optional)", "type": "string"}, "share": map[string]any{"description": "the share, by its name", "type": "string"}}, "required": []string{"share"}, "type": "object"}, Replaces: []string{"showmount -e"}}, - {Name: "reload", Description: "Have the kernel read this machine's export files again now (exportfs -ra) and answer the shares as it then holds them. The module's own process writes its export file; this is for after a change made outside it. Changes no shared path.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, Replaces: []string{"exportfs -ra"}}, - {Name: "adopt", Description: "Take over an export a person made by hand: clear a dataset's sharenfs property for a path the mesh's own export now serves — only when that export is live. Without confirm, says what it would do and changes nothing.", - Input: map[string]any{"properties": map[string]any{"confirm": map[string]any{"description": "\"true\": change it (needs why); anything else is a dry run", "enum": []string{"true", "false"}, "type": "string"}, "path": map[string]any{"description": "the shared path whose hand-made export is taken over", "type": "string"}, "why": map[string]any{"description": "why, for the record", "type": "string"}}, "required": []string{"path"}, "type": "object"}, Replaces: []string{"zfs set sharenfs=off"}}, - }}, - {Name: NodeMounts, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0263", - Serves: []Verb{ - {Name: "list", Description: "Every mount point on this machine: its fstab line, the mesh's mount and automount units for it, its state (armed, mounted, absent, unreachable, failed) and since when, and which settings asked for it. A password in a mount's options is never shown.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, Replaces: []string{"cat /etc/fstab", "findmnt", "systemctl list-units --type=mount"}}, - {Name: "test", Description: "Whether one share's or occasional source's server answers from this machine now, without touching its mount point.", - Input: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the share or source, by its name", "type": "string"}}, "required": []string{"name"}, "type": "object"}, Replaces: []string{"showmount -e", "ping"}}, - {Name: "mount", Description: "Mount one share or occasional source now, rather than at its first access.", - Input: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the share or source, by its name", "type": "string"}}, "required": []string{"name"}, "type": "object"}, Replaces: []string{"mount"}}, - {Name: "unmount", Description: "Release one share or occasional source now; its automount stays armed, so the next access mounts it again.", - Input: map[string]any{"properties": map[string]any{"name": map[string]any{"description": "the share or source, by its name", "type": "string"}}, "required": []string{"name"}, "type": "object"}, Replaces: []string{"umount"}}, - {Name: "adopt", Description: "Take over a mount a person wrote by hand: comment out the /etc/fstab line for a mount point the mesh's own units now serve, keeping a copy of the file — only when the mesh's automount for it is armed. Without confirm, says what it would do and changes nothing.", - Input: map[string]any{"properties": map[string]any{"confirm": map[string]any{"description": "\"true\": change it (needs why); anything else is a dry run", "enum": []string{"true", "false"}, "type": "string"}, "mountpoint": map[string]any{"description": "the mount point whose fstab line is taken over", "type": "string"}, "why": map[string]any{"description": "why, for the record", "type": "string"}}, "required": []string{"mountpoint"}, "type": "object"}, Replaces: []string{"sed -i /etc/fstab"}}, - }}, - {Name: NodeReads, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0310", - Serves: []Verb{ - {Name: "stat", Description: "Each component of one path or several, from / down: its type, owner and group (name and number), mode, whether it is a link and to what, and whether it is a mount point and of which file system. A link is not followed unless follow is \"true\". Read as the holder's own account, which holds no secret: a component it may not reach says so. At most 200 paths in one call.", - Input: map[string]any{"properties": map[string]any{"follow": map[string]any{"description": "\"true\": also say where the path leads once every link is followed", "enum": []string{"true", "false"}, "type": "string"}, "paths": map[string]any{"description": "one absolute path, or several separated by line breaks", "type": "string"}}, "required": []string{"paths"}, "type": "object"}, Replaces: []string{"stat", "ls -ld", "namei -l", "findmnt -T"}}, - {Name: "readlink", Description: "What one link points to, as written, and where the path leads once every link is followed, and whether that exists.", - Input: map[string]any{"properties": map[string]any{"path": map[string]any{"description": "an absolute path", "type": "string"}}, "required": []string{"path"}, "type": "object"}, Replaces: []string{"readlink", "readlink -f", "realpath"}}, - {Name: "list", Description: "Every entry of one directory with its type, owner, group, mode, size and, for a link, its target. At most 2000 entries; a longer directory is cut and says so. A place a secret is kept is refused by name.", - Input: map[string]any{"properties": map[string]any{"path": map[string]any{"description": "an absolute path to a directory", "type": "string"}}, "required": []string{"path"}, "type": "object"}, Replaces: []string{"ls", "ls -la"}}, - {Name: "read", Description: "One text file, or a part of it by line: at most 256 KiB in one answer (64 KiB unless more is asked), cut at a line and said so. A binary file is refused; a place a secret is kept, or a known credential file, is refused by name; a line shaped like a secret is withheld and its number said. /proc and /sys are read as far as the holder's account may.", - Input: map[string]any{"properties": map[string]any{"bytes": map[string]any{"description": "the most bytes to answer, up to 262144 (optional, 65536 unless given)", "type": "string"}, "from": map[string]any{"description": "the first line to answer, counted from 1 (optional)", "type": "string"}, "lines": map[string]any{"description": "how many lines to answer (optional)", "type": "string"}, "path": map[string]any{"description": "an absolute path to a file", "type": "string"}}, "required": []string{"path"}, "type": "object"}, Replaces: []string{"cat", "head", "tail", "less"}}, - {Name: "search", Description: "Find a fixed string (never a pattern) in one file or under one directory: every matching line with its file and number. Bounded: at most 500 matches (100 unless more is asked), 8 directories deep (4 unless more is asked), 5000 files and 4 MiB per file; links are not followed and binary files are skipped. A place a secret is kept is skipped and named; a matching line shaped like a secret is withheld. The answer says which bound it met.", - Input: map[string]any{"properties": map[string]any{"case": map[string]any{"description": "\"false\": match whatever the case (optional)", "enum": []string{"true", "false"}, "type": "string"}, "depth": map[string]any{"description": "how many directories deep, up to 8 (optional)", "type": "string"}, "matches": map[string]any{"description": "the most matches to answer, up to 500 (optional)", "type": "string"}, "path": map[string]any{"description": "an absolute path to a file or a directory", "type": "string"}, "text": map[string]any{"description": "the fixed string to find", "type": "string"}}, "required": []string{"text", "path"}, "type": "object"}, Replaces: []string{"grep", "grep -r", "grep -F"}}, - {Name: "account", Description: "One account or group by name or number, as this node's account files say: an account's number, group, home, shell and every group it is in; a group's number and members. With neither, every account and group. Never a password field.", - Input: map[string]any{"properties": map[string]any{"group": map[string]any{"description": "a group's name or number (optional)", "type": "string"}, "name": map[string]any{"description": "an account's name or number (optional)", "type": "string"}}, "type": "object"}, Replaces: []string{"getent passwd", "getent group", "id", "groups"}}, - }}, - {Name: NodeFiles, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0394", - Serves: []Verb{ - {Name: "stat", Description: "Each component of one path or several, from / down: its type, owner and group (name and number), mode, whether it is a link and to what, and whether it is a mount point and of which file system. A link is not followed unless follow is \"true\". At most 200 paths in one call. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - Input: map[string]any{"properties": map[string]any{"follow": map[string]any{"description": "\"true\": also say where the path leads once every link is followed", "enum": []string{"true", "false"}, "type": "string"}, "paths": map[string]any{"description": "one absolute path, or several separated by line breaks", "type": "string"}}, "required": []string{"paths"}, "type": "object"}, Replaces: []string{"stat", "ls -ld", "namei -l", "findmnt -T"}}, - {Name: "readlink", Description: "What one link points to, as written, and where the path leads once every link is followed, and whether that exists. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - Input: map[string]any{"properties": map[string]any{"path": map[string]any{"description": "an absolute path", "type": "string"}}, "required": []string{"path"}, "type": "object"}, Replaces: []string{"readlink", "readlink -f", "realpath"}}, - {Name: "list", Description: "Every entry of one directory with its type, owner, group, mode, size and, for a link, its target. At most 2000 entries; a longer directory is cut and says so. A place a secret is kept is refused by name. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - Input: map[string]any{"properties": map[string]any{"path": map[string]any{"description": "an absolute path to a directory", "type": "string"}}, "required": []string{"path"}, "type": "object"}, Replaces: []string{"ls", "ls -la"}}, - {Name: "read", Description: "One text file, or a part of it by line: at most 256 KiB in one answer (64 KiB unless more is asked), cut at a line and said so. A binary file is refused; a place a secret is kept, or a known credential file, is refused by name; a line shaped like a secret is withheld and its number said. /proc and /sys are read as far as the holder's account may. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - Input: map[string]any{"properties": map[string]any{"bytes": map[string]any{"description": "the most bytes to answer, up to 262144 (optional, 65536 unless given)", "type": "string"}, "from": map[string]any{"description": "the first line to answer, counted from 1 (optional)", "type": "string"}, "lines": map[string]any{"description": "how many lines to answer (optional)", "type": "string"}, "path": map[string]any{"description": "an absolute path to a file", "type": "string"}}, "required": []string{"path"}, "type": "object"}, Replaces: []string{"cat", "head", "tail", "less"}}, - {Name: "search", Description: "Find a fixed string (never a pattern) in one file or under one directory: every matching line with its file and number. Bounded: at most 500 matches (100 unless more is asked), 8 directories deep (4 unless more is asked), 5000 files and 4 MiB per file; links are not followed and binary files are skipped. A place a secret is kept is skipped and named; a matching line shaped like a secret is withheld. The answer says which bound it met. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - Input: map[string]any{"properties": map[string]any{"case": map[string]any{"description": "\"false\": match whatever the case (optional)", "enum": []string{"true", "false"}, "type": "string"}, "depth": map[string]any{"description": "how many directories deep, up to 8 (optional)", "type": "string"}, "matches": map[string]any{"description": "the most matches to answer, up to 500 (optional)", "type": "string"}, "path": map[string]any{"description": "an absolute path to a file or a directory", "type": "string"}, "text": map[string]any{"description": "the fixed string to find", "type": "string"}}, "required": []string{"text", "path"}, "type": "object"}, Replaces: []string{"grep", "grep -r", "grep -F"}}, - {Name: "account", Description: "One account or group by name or number, as this node's account files say: an account's number, group, home, shell and every group it is in; a group's number and members. With neither, every account and group. Never a password field. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - Input: map[string]any{"properties": map[string]any{"group": map[string]any{"description": "a group's name or number (optional)", "type": "string"}, "name": map[string]any{"description": "an account's name or number (optional)", "type": "string"}}, "type": "object"}, Replaces: []string{"getent passwd", "getent group", "id", "groups"}}, - {Name: "write", Description: "Write one text file whole, replacing what it holds; the old content goes to the node's trash. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - Input: map[string]any{"properties": map[string]any{"content": map[string]any{"description": "the text to write", "type": "string"}, "mode": map[string]any{"description": "the file's mode, such as 0644 (optional)", "enum": []string{"true", "false"}, "type": "string"}, "override": map[string]any{"description": "a reason, to change a path a module owns anyway (optional)", "enum": []string{"true", "false"}, "type": "string"}, "path": map[string]any{"description": "an absolute path", "type": "string"}, "reason": map[string]any{"description": "why the file is changed, for the audit trail", "type": "string"}}, "required": []string{"path", "content", "reason"}, "type": "object"}, Replaces: []string{"tee", ">"}}, - {Name: "copy", Description: "Copy a file or directory to a path on this node. A target on another node (:/path) is refused: copying between nodes comes in step 2 (issue 903). An existing target is overwritten, the old one kept in the node's trash for 7 days. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - Input: map[string]any{"properties": map[string]any{"from": map[string]any{"description": "an absolute path", "type": "string"}, "override": map[string]any{"description": "a reason, to change a path a module owns anyway (optional)", "enum": []string{"true", "false"}, "type": "string"}, "reason": map[string]any{"description": "why the file is changed, for the audit trail", "type": "string"}, "to": map[string]any{"description": "an absolute path on this node", "type": "string"}}, "required": []string{"from", "to", "reason"}, "type": "object"}, Replaces: []string{"cp", "cp -a", "ssh cp"}}, - {Name: "move", Description: "Move or rename a file or directory. A target on another node is refused: copying between nodes comes in step 2 (issue 903). An existing target is overwritten, the old one kept in the node's trash for 7 days. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - Input: map[string]any{"properties": map[string]any{"from": map[string]any{"description": "an absolute path", "type": "string"}, "override": map[string]any{"description": "a reason, to change a path a module owns anyway (optional)", "enum": []string{"true", "false"}, "type": "string"}, "reason": map[string]any{"description": "why the file is changed, for the audit trail", "type": "string"}, "to": map[string]any{"description": "an absolute path", "type": "string"}}, "required": []string{"from", "to", "reason"}, "type": "object"}, Replaces: []string{"mv"}}, - {Name: "delete", Description: "Delete a file or directory into the node's trash, where restore can bring it back. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - Input: map[string]any{"properties": map[string]any{"override": map[string]any{"description": "a reason, to change a path a module owns anyway (optional)", "enum": []string{"true", "false"}, "type": "string"}, "path": map[string]any{"description": "an absolute path", "type": "string"}, "reason": map[string]any{"description": "why the file is changed, for the audit trail", "type": "string"}, "recursive": map[string]any{"description": "\"true\": delete a directory with what it holds", "enum": []string{"true", "false"}, "type": "string"}}, "required": []string{"path", "reason"}, "type": "object"}, Replaces: []string{"rm", "rm -r"}}, - {Name: "mkdir", Description: "Make a directory. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - Input: map[string]any{"properties": map[string]any{"override": map[string]any{"description": "a reason, to change a path a module owns anyway (optional)", "enum": []string{"true", "false"}, "type": "string"}, "parents": map[string]any{"description": "\"true\": make the missing parents too", "enum": []string{"true", "false"}, "type": "string"}, "path": map[string]any{"description": "an absolute path", "type": "string"}, "reason": map[string]any{"description": "why the file is changed, for the audit trail", "type": "string"}}, "required": []string{"path", "reason"}, "type": "object"}, Replaces: []string{"mkdir", "mkdir -p"}}, - {Name: "chmod", Description: "Change the mode of a file or directory. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - Input: map[string]any{"properties": map[string]any{"mode": map[string]any{"description": "the mode, such as 0644 or u+x", "type": "string"}, "override": map[string]any{"description": "a reason, to change a path a module owns anyway (optional)", "enum": []string{"true", "false"}, "type": "string"}, "path": map[string]any{"description": "an absolute path", "type": "string"}, "reason": map[string]any{"description": "why the file is changed, for the audit trail", "type": "string"}, "recursive": map[string]any{"description": "\"true\": also everything under a directory", "enum": []string{"true", "false"}, "type": "string"}}, "required": []string{"path", "mode", "reason"}, "type": "object"}, Replaces: []string{"chmod"}}, - {Name: "chown", Description: "Change the owner and group of a file or directory. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - Input: map[string]any{"properties": map[string]any{"group": map[string]any{"description": "a group's name or number (optional)", "enum": []string{"true", "false"}, "type": "string"}, "override": map[string]any{"description": "a reason, to change a path a module owns anyway (optional)", "enum": []string{"true", "false"}, "type": "string"}, "owner": map[string]any{"description": "an account's name or number", "type": "string"}, "path": map[string]any{"description": "an absolute path", "type": "string"}, "reason": map[string]any{"description": "why the file is changed, for the audit trail", "type": "string"}, "recursive": map[string]any{"description": "\"true\": also everything under a directory", "enum": []string{"true", "false"}, "type": "string"}}, "required": []string{"path", "owner", "reason"}, "type": "object"}, Replaces: []string{"chown", "chgrp"}}, - {Name: "upload", Description: "Send one file of this node into a bucket of the object store, over a short signed link and never over the bus; answers the key (the file's SHA-256 when none is given), the size and the SHA-256. At most 100 MiB. A place a secret is kept, a known credential file, or a link to one is refused by name, before anything is sent. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - Input: map[string]any{"properties": map[string]any{"bucket": map[string]any{"description": "the bucket the file goes into", "type": "string"}, "key": map[string]any{"description": "the object's key (optional; the file's SHA-256 when not given)", "type": "string"}, "path": map[string]any{"description": "an absolute path to a file", "type": "string"}}, "required": []string{"path", "bucket"}, "type": "object"}}, - {Name: "download", Description: "Write one object of a bucket to a file of this node, over a short signed link and never over the bus; the old file goes to the node's trash. At most 100 MiB. The bytes must hash to sha256, or to the key when the key is a SHA-256; otherwise no file is written. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - Input: map[string]any{"properties": map[string]any{"bucket": map[string]any{"description": "the bucket", "type": "string"}, "key": map[string]any{"description": "the object's key", "type": "string"}, "override": map[string]any{"description": "a reason, to change a path a module owns anyway (optional)", "type": "string"}, "path": map[string]any{"description": "an absolute path", "type": "string"}, "reason": map[string]any{"description": "why the file is changed, for the audit trail", "type": "string"}, "sha256": map[string]any{"description": "the SHA-256 the bytes must have (optional; the key when that is a SHA-256)", "type": "string"}}, "required": []string{"bucket", "key", "path", "reason"}, "type": "object"}}, - {Name: "trash", Description: "What the node's trash holds: each deleted or replaced item with its id, original path and when it went.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, Replaces: []string{"ls ~/.local/share/Trash"}}, - {Name: "restore", Description: "Put an item of the node's trash back where it was; refuses when something is there now. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - Input: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "an id from trash", "type": "string"}, "override": map[string]any{"description": "a reason, to change a path a module owns anyway (optional)", "enum": []string{"true", "false"}, "type": "string"}, "reason": map[string]any{"description": "why the file is changed, for the audit trail", "type": "string"}}, "required": []string{"id", "reason"}, "type": "object"}, Replaces: []string{"trash-restore"}}, - }}, - {Name: NodeEngine, Scope: "node", Grain: "whole", Decision: "novox/hq ADR 0563", Emits: []string{"asked", "done", "refused", "manifest-reads-changed"}, HeldByTheEngine: true, - Serves: []Verb{ - {Name: "hand-over", Description: "Hand a directory this machine uses as found to the mesh (novox/hq ADR 0563 §3, §5): the engine judges the path as it does every hand-over (absolute, as it states it, used as found, reached through no link) and refuses in words, or asks the operator on their channel at the level approve, binding the owner, group and mode it will set. Nothing changes on the call: only the operator's yes hands it over. One ask is open per path; acts shows where it stands.", - Input: map[string]any{"properties": map[string]any{"path": map[string]any{"description": "the directory's absolute path, as the engine states it", "type": "string"}, "why": map[string]any{"description": "why it is asked, in words the operator reads", "type": "string"}}, "required": []string{"path"}, "type": "object"}, - Output: map[string]any{"description": "the answer of an act the operator is asked", "properties": map[string]any{"asked": map[string]any{"description": "the ask's id: acts shows where it stands", "type": "string"}, "node": map[string]any{"description": "the node", "type": "string"}, "says": map[string]any{"description": "what happens next, in words", "type": "string"}}, "required": []string{"asked", "node", "says"}, "type": "object"}}, - {Name: "access-act", Description: "Ask the operator for the take act on a library whose root holds data outside its group (novox/hq ADR 0328 §7, ADR 0563 §3, §4), on the library's home engine: the access as declared and its root as last declared, judged and refused in words or asked at the level approve. Only the operator's yes sets the library's group, group write and setgid under its root. One ask is open per access.", - Input: map[string]any{"properties": map[string]any{"access": map[string]any{"description": "the access, library:", "type": "string"}, "act": map[string]any{"description": "the act: take", "type": "string"}, "why": map[string]any{"description": "why it is asked, in words the operator reads", "type": "string"}}, "required": []string{"act", "access"}, "type": "object"}, - Output: map[string]any{"description": "the answer of an act the operator is asked", "properties": map[string]any{"asked": map[string]any{"description": "the ask's id: acts shows where it stands", "type": "string"}, "node": map[string]any{"description": "the node", "type": "string"}, "says": map[string]any{"description": "what happens next, in words", "type": "string"}}, "required": []string{"asked", "node", "says"}, "type": "object"}}, - {Name: "judge-directories", Description: "Which of this machine's declared directories the engine would refuse to make or keep, one line each (novox/hq ADR 0563 §2). Read-only: changes no owner or mode.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, - Output: map[string]any{"description": "the answer of a read-only judgement", "properties": map[string]any{"node": map[string]any{"description": "the node", "type": "string"}, "said": map[string]any{"description": "the engine's judgement or search, one line per directory or program", "type": "string"}}, "required": []string{"node", "said"}, "type": "object"}}, - {Name: "setuid-search", Description: "Throw away the engine's last search for setuid programs and start a full one (novox/hq ADR 0563 §2); until it completes root-free answers that the machine is not judged yet. Refused while one runs, and when one was started within the hour, naming when the next may start.", - Input: map[string]any{"properties": map[string]any{"why": map[string]any{"description": "why it is asked, in words", "type": "string"}}, "type": "object"}, - Output: map[string]any{"description": "the answer of a read-only judgement", "properties": map[string]any{"node": map[string]any{"description": "the node", "type": "string"}, "said": map[string]any{"description": "the engine's judgement or search, one line per directory or program", "type": "string"}}, "required": []string{"node", "said"}, "type": "object"}}, - {Name: "root-free", Description: "Whether this machine is root-free (novox/hq ADR 0259 §8, ADR 0563 §6): free only on the engine's own verdict, at most fifteen minutes old, that the account agents run as cannot become root without a person, and the login shell's execute not served here. Anything else is no pass, and says why. Answered by this machine's engine alone.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"machines": map[string]any{"description": "this machine's verdict", "items": map[string]any{"description": "the engine's root-free verdict (ADR 0563 §6)", "properties": map[string]any{"free": map[string]any{"description": "the machine is root-free", "type": "boolean"}, "judged": map[string]any{"description": "when the engine judged it, RFC 3339", "type": "string"}, "machine": map[string]any{"description": "the machine", "type": "string"}, "why": map[string]any{"description": "why, in words", "type": "string"}}, "required": []string{"machine", "free", "why", "judged"}, "type": "object"}, "type": "array"}}, "required": []string{"machines"}, "type": "object"}}, - {Name: "acts", Description: "Every act this machine's engine was asked through this seat (novox/hq ADR 0563 §9): each hand-over, access act and setuid search, open, done or refused, with who asked, the ask and the operator's warrant. Read-only.", - Input: map[string]any{"properties": map[string]any{"id": map[string]any{"description": "one act, by its id", "type": "string"}, "limit": map[string]any{"description": "at most this many, newest", "type": "string"}}, "type": "object"}, - Output: map[string]any{"description": "the acts kept", "properties": map[string]any{"acts": map[string]any{"description": "the acts, newest first", "items": map[string]any{"description": "one act asked through the seat, as acts reads it (ADR 0563 §9)", "properties": map[string]any{"act": map[string]any{"description": "the act: hand-over, access-act, judge-directories or setuid-search", "type": "string"}, "ask": map[string]any{"description": "the operator's ask of a hand-over or an access act", "type": "string"}, "at": map[string]any{"description": "when it was asked, RFC 3339", "type": "string"}, "auto": map[string]any{"description": "the engine asked it itself, on finding a library's root holding data outside its group (ADR 0563 §4)", "type": "boolean"}, "by": map[string]any{"description": "who answered, as the operator's warrant names them", "type": "string"}, "caller": map[string]any{"description": "the caller as the runtime proved it", "type": "string"}, "disk": map[string]any{"description": "what the engine read on disk when it judged", "type": "string"}, "ended": map[string]any{"description": "when it ended, RFC 3339", "type": "string"}, "expires": map[string]any{"description": "when the ask ends unanswered, RFC 3339", "type": "string"}, "id": map[string]any{"description": "the act's id", "type": "string"}, "path": map[string]any{"description": "the directory the act changes", "type": "string"}, "proofs": map[string]any{"description": "the warrant's proofs", "items": map[string]any{"type": "string"}, "type": "array"}, "said": map[string]any{"description": "what the engine said of it: its outcome or why it was refused", "type": "string"}, "sets": map[string]any{"description": "the owner, group and mode it sets, in words", "type": "string"}, "state": map[string]any{"description": "where it stands", "enum": []string{"asked", "done", "refused", "answered"}, "type": "string"}, "subject": map[string]any{"description": "the path of a hand-over, or the access of an access act", "type": "string"}, "via": map[string]any{"description": "the channel the warrant came on", "type": "string"}, "why": map[string]any{"description": "why it was asked, in words", "type": "string"}}, "required": []string{"id", "act", "caller", "at", "state"}, "type": "object"}, "type": "array"}, "node": map[string]any{"description": "the node", "type": "string"}}, "required": []string{"node", "acts"}, "type": "object"}}, - {Name: "packages", Description: "The packages this machine's compositions declare, present or absent, each with the modules that declare it, as the engine keeps them in its key of `packages`; until the controller hands its lease away, beside the controller's mirror for this machine and their difference. With write, the key written again from what the engine composed.", Optional: true, - Input: map[string]any{"properties": map[string]any{"write": map[string]any{"description": "\"true\": write this machine's key again from what the engine composed", "enum": []string{"true", "false"}, "type": "string"}}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"key": map[string]any{"description": "the node's key as the bus holds it; absent when the engine wrote none", "properties": map[string]any{"node": map[string]any{"description": "the node", "type": "string"}, "packages": map[string]any{"description": "its packages sorted by name, absent after present", "items": map[string]any{"properties": map[string]any{"absent": map[string]any{"description": "declared absent", "type": "boolean"}, "modules": map[string]any{"description": "the modules that declare it", "items": map[string]any{"type": "string"}, "type": "array"}, "package": map[string]any{"description": "the package", "type": "string"}}, "required": []string{"package", "modules", "absent"}, "type": "object"}, "type": "array"}}, "required": []string{"node", "packages"}, "type": "object"}, "key_error": map[string]any{"description": "why the key could not be read", "type": "string"}, "node": map[string]any{"description": "the node", "type": "string"}, "note": map[string]any{"description": "a note, such as that no key is written yet", "type": "string"}, "source": map[string]any{"description": "where the key is read: the engine's state packages", "type": "string"}, "wrote": map[string]any{"description": "with write: the key was written again (false: it already held what the engine composed)", "type": "boolean"}}, "required": []string{"node", "source"}, "type": "object"}}, - {Name: "composition", Description: "One module's composition on this machine beside what runs of it, read-only: the composition its want's build has in the bucket composed- (the latest revision, or the one whose digest is given, from the revisions the bucket keeps), the digest of each kept revision, what runs (applied, its have, or the kept declaration), its want and have (phase running, failed, held or waiting-yes, and the digest that waits for a yes), and the move's judgement with its words: why a record module waits for a yes. With diff, every resource added, removed or changed, a provider's received file naming the consumers that join or leave.", Optional: true, - Input: map[string]any{"properties": map[string]any{"diff": map[string]any{"description": "\"true\": list the resources that differ from what runs", "type": "string"}, "digest": map[string]any{"description": "a composition's digest (sha256:…), to read that revision rather than the latest", "type": "string"}, "module": map[string]any{"description": "the module, as placed on this machine; node for the node's own", "type": "string"}}, "required": []string{"module"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"attempt": map[string]any{"description": "the module's last apply that did not end running; absent when none", "properties": map[string]any{"answered_revision": map[string]any{"description": "the want's revision the hold took", "type": "integer"}, "composed": map[string]any{"description": "the digest tried", "type": "string"}, "composition_revision": map[string]any{"description": "the composition's revision it was tried at", "type": "integer"}, "fp": map[string]any{"description": "the fingerprint tried", "type": "string"}, "held": map[string]any{"description": "held for a failed backup", "type": "boolean"}, "transient": map[string]any{"description": "it failed because the bus did not answer for a moment", "type": "boolean"}, "want_revision": map[string]any{"description": "the want's revision it was tried at", "type": "integer"}}, "required": []string{"fp", "want_revision", "composition_revision"}, "type": "object"}, "composition": map[string]any{"description": "one revision of a composition key", "properties": map[string]any{"digest": map[string]any{"description": "its digest", "type": "string"}, "key": map[string]any{"description": "the composition's key", "type": "string"}, "latest": map[string]any{"description": "the latest revision", "type": "boolean"}, "refused": map[string]any{"description": "why it cannot be read or trusted", "type": "string"}, "resources": map[string]any{"description": "how many resources it names", "type": "integer"}, "revision": map[string]any{"description": "its revision", "type": "integer"}}, "required": []string{"key"}, "type": "object"}, "diff": map[string]any{"description": "every resource that differs from what runs, when asked", "items": map[string]any{"description": "one resource added, removed or changed, as the engine's judge says it", "type": "object"}, "type": "array"}, "have": map[string]any{"description": "what the module's have key says now; absent when it says nothing", "properties": map[string]any{"composed": map[string]any{"description": "digest", "type": "string"}, "detail": map[string]any{"description": "detail", "type": "string"}, "fp": map[string]any{"description": "fingerprint", "type": "string"}, "judgement": map[string]any{"description": "judgement", "type": "string"}, "phase": map[string]any{"description": "running, failed, held or waiting-yes", "type": "string"}, "waits": map[string]any{"description": "the digest that waits for a yes", "type": "string"}}, "required": []string{"phase"}, "type": "object"}, "judged": map[string]any{"description": "the move from what runs to the composition read", "properties": map[string]any{"judgement": map[string]any{"description": "the judgement", "type": "string"}, "waits_for_yes": map[string]any{"description": "the agent would wait for a person's yes before applying it", "type": "boolean"}, "words": map[string]any{"description": "its words", "type": "string"}}, "required": []string{"judgement", "words", "waits_for_yes"}, "type": "object"}, "module": map[string]any{"description": "the module", "type": "string"}, "node": map[string]any{"description": "the node", "type": "string"}, "own": map[string]any{"description": "of the node's own composition, its own fields beside what runs names", "properties": map[string]any{"adoption": map[string]any{"description": "the revision's adoption; absent for a converged node", "type": "object"}, "groups": map[string]any{"description": "groups", "type": "integer"}, "held": map[string]any{"additionalProperties": map[string]any{"type": "string"}, "description": "each module the node's composer held in its last pass, with why", "type": "object"}, "identities": map[string]any{"description": "identities the revision names", "type": "integer"}, "libraries": map[string]any{"description": "libraries", "type": "integer"}, "provisioner_account": map[string]any{"description": "the account the provisioner's client runs as, as the revision names it", "type": "string"}, "runs_provisioner_account": map[string]any{"description": "as what runs names it", "type": "string"}}, "required": []string{"provisioner_account"}, "type": "object"}, "revisions": map[string]any{"description": "the revisions of its key the bucket keeps, oldest first", "items": map[string]any{"description": "one revision of a composition key", "properties": map[string]any{"digest": map[string]any{"description": "its digest", "type": "string"}, "key": map[string]any{"description": "the composition's key", "type": "string"}, "latest": map[string]any{"description": "the latest revision", "type": "boolean"}, "refused": map[string]any{"description": "why it cannot be read or trusted", "type": "string"}, "resources": map[string]any{"description": "how many resources it names", "type": "integer"}, "revision": map[string]any{"description": "its revision", "type": "integer"}}, "required": []string{"key"}, "type": "object"}, "type": "array"}, "runs": map[string]any{"description": "what runs of the module; absent when nothing runs", "properties": map[string]any{"composed": map[string]any{"description": "digest", "type": "string"}, "fp": map[string]any{"description": "fingerprint", "type": "string"}, "from": map[string]any{"description": "applied, have or kept", "type": "string"}, "phase": map[string]any{"description": "phase", "type": "string"}, "resources": map[string]any{"description": "how many resources run", "type": "integer"}}, "required": []string{"from", "resources"}, "type": "object"}, "started": map[string]any{"description": "the agent has looked at the module once since it started", "type": "boolean"}, "want": map[string]any{"description": "the module's want as the agent holds it; absent when it holds none", "properties": map[string]any{"backup": map[string]any{"description": "the operator's answer to a failed backup, again or skip", "type": "string"}, "composed": map[string]any{"description": "the composition's digest it names", "type": "string"}, "fp": map[string]any{"description": "its build's fingerprint", "type": "string"}, "removed": map[string]any{"description": "the module is removed", "type": "boolean"}, "revision": map[string]any{"description": "the revision of the want's key read last", "type": "integer"}, "self": map[string]any{"description": "the node composes the module itself (ADR 0547 §2)", "type": "boolean"}, "words": map[string]any{"description": "its words", "type": "string"}, "yes": map[string]any{"description": "the digest a person said yes to", "type": "string"}}, "required": []string{"fp", "revision"}, "type": "object"}}, "required": []string{"node", "module", "started", "composition", "judged"}, "type": "object"}}, - {Name: "compose", Description: "Compose this machine's modules now and say how it went, so a composition that failed, or a module held, recovers without a restart: what the pass refused or held, each with why, and when the engine composes again on its own after a seat that did not answer (a deadline, no responders: retried with a growing wait; a refusal never). With module, that module's apply is also tried again. Changes only what a pass the engine makes on its own would change.", Optional: true, - Input: map[string]any{"properties": map[string]any{"module": map[string]any{"description": "a module placed on this machine whose apply is also tried again (optional)", "type": "string"}}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"pass": map[string]any{"description": "how the pass went", "properties": map[string]any{"again": map[string]any{"description": "when the node composes again on its own, RFC 3339", "type": "string"}, "at": map[string]any{"description": "when the pass ended, RFC 3339", "type": "string"}, "earlier": map[string]any{"description": "the answer is the last pass, made moments ago", "type": "boolean"}, "handed": map[string]any{"description": "how many compositions the pass handed the agent that differ from before", "type": "integer"}, "held": map[string]any{"additionalProperties": map[string]any{"type": "string"}, "description": "each module refused alone, with why", "type": "object"}, "refused": map[string]any{"description": "why the node composed none of its modules", "type": "string"}}, "required": []string{"at", "handed"}, "type": "object"}, "retried": map[string]any{"description": "the module whose apply was tried again", "type": "string"}}, "required": []string{"pass"}, "type": "object"}}, - {Name: "compose-candidate", Description: "Compose a check's candidate on this machine in shadow, with its own sealed values and the indexer's manifest verdicts, and say per module placed here whether it composes (with its digest and whether the candidate changes it), is refused by the candidate or is held without it, each with why, and the verdict of the machine's own composition (novox/hq ADR 0593 §7). Read-only: applies nothing and writes nothing. Answered by this machine's engine alone, within the asker's bound.", Optional: true, - Input: map[string]any{"properties": map[string]any{"candidate": map[string]any{"description": "the key of the indexer's candidate, .", "type": "string"}, "input": map[string]any{"description": "also answer the composition input the candidate was composed from, for the sandbox path", "type": "boolean"}}, "required": []string{"candidate"}, "type": "object"}, - Output: map[string]any{"properties": map[string]any{"candidate": map[string]any{"description": "the candidate's key", "type": "string"}, "input": map[string]any{"description": "when asked: the composition input it composed from", "type": "object"}, "modules": map[string]any{"description": "every module placed on the node, sorted by name", "items": map[string]any{"description": "one module's composition with the candidate", "properties": map[string]any{"changed": map[string]any{"description": "the candidate changes it from the one composed without it", "type": "boolean"}, "digest": map[string]any{"description": "the composition's digest when composed", "type": "string"}, "fp": map[string]any{"description": "the fingerprint it was composed at", "type": "string"}, "module": map[string]any{"description": "the module, node for the node's own composition", "type": "string"}, "state": map[string]any{"description": "composed (digest set), refused by the candidate, or held without it too (words set)", "enum": []string{"composed", "refused", "held"}, "type": "string"}, "words": map[string]any{"description": "why, for a refused or held one", "type": "string"}}, "required": []string{"module", "state"}, "type": "object"}, "type": "array"}, "node": map[string]any{"description": "the node", "type": "string"}, "own": map[string]any{"description": "one module's composition with the candidate", "properties": map[string]any{"changed": map[string]any{"description": "the candidate changes it from the one composed without it", "type": "boolean"}, "digest": map[string]any{"description": "the composition's digest when composed", "type": "string"}, "fp": map[string]any{"description": "the fingerprint it was composed at", "type": "string"}, "module": map[string]any{"description": "the module, node for the node's own composition", "type": "string"}, "state": map[string]any{"description": "composed (digest set), refused by the candidate, or held without it too (words set)", "enum": []string{"composed", "refused", "held"}, "type": "string"}, "words": map[string]any{"description": "why, for a refused or held one", "type": "string"}}, "required": []string{"module", "state"}, "type": "object"}, "refused": map[string]any{"description": "why the node could not compose with the candidate at all", "type": "string"}, "took": map[string]any{"description": "how long the engine took, in milliseconds", "type": "integer"}, "v": map[string]any{"description": "the layout's version", "type": "integer"}, "verdict": map[string]any{"description": "the node's verdict (ADR 0593 §7, §9)", "enum": []string{"composed", "refused", "unready", "not-judged"}, "type": "string"}}, "required": []string{"v", "node", "candidate", "verdict", "modules", "own", "took"}, "type": "object"}}, - {Name: "manifest-reads", Description: "The reader capabilities this machine's engine states (manifest words, forms and seat names it reads, novox/hq ADR 0618 §3) and when it last stated them. An engine built before it said them states none, which a reader takes as the baseline. Read-only. Answered by this machine's engine alone.", Optional: true, - Input: map[string]any{"properties": map[string]any{}, "type": "object"}, - Output: map[string]any{"description": "what this machine's engine reads of a manifest (ADR 0618 §3)", "properties": map[string]any{"manifest-reads": map[string]any{"description": "the reader capabilities its engine states: the manifest words, forms and seat names it reads; empty for an engine built before it said them, read as the baseline", "items": map[string]any{"minLength": float64(1), "type": "string"}, "type": "array"}, "node": map[string]any{"description": "the node", "type": "string"}, "stated-at": map[string]any{"description": "when the engine last stated them, RFC 3339", "type": "string"}, "v": map[string]any{"description": "the layout's version", "type": "integer"}}, "required": []string{"v", "node", "stated-at"}, "type": "object"}}, - }}, - {Name: NodeLoginManager, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208", - Serves: []Verb{ - {Name: "sessions", Description: "The sessions the login manager offers on this machine, and which one the operator account starts by default.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - }}, - {Name: NodeDisplayServer, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208", - Serves: []Verb{ - {Name: "displays", Description: "The monitors connected now, each with its identity, its modes and where it is placed; and the layout profile in force, if one matches.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "layout", Description: "The monitor layout profiles, keyed by the connected monitors' identities: list them, save the current arrangement under a name, or apply one.", - Input: map[string]any{"properties": map[string]any{"action": map[string]any{"description": "list, save or apply", "enum": []string{"list", "save", "apply"}, "type": "string"}, "name": map[string]any{"description": "the profile to save or apply (save and apply only)", "type": "string"}}, "required": []string{"action"}, "type": "object"}}, - }}, - {Name: NodeDisplaySession, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208", - Serves: []Verb{ - {Name: "reload", Description: "Reload the session's configuration in place, keeping its windows.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "workspaces", Description: "The session's workspaces: each one's name, output, and whether it is visible or focused.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - {Name: "windows", Description: "The session's windows: each one's title, class, workspace and whether it has focus; narrowed to one workspace when named.", - Input: map[string]any{"properties": map[string]any{"workspace": map[string]any{"description": "one workspace (optional)", "type": "string"}}, "type": "object"}}, - }, - Receives: []Receivable{{Kind: "config", Comment: "#"}}}, - {Name: NodeTerminalEmulator, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208", - Serves: []Verb{ - {Name: "open", Description: "Open a terminal window in the operator's session, running a command or the login shell, in a directory or the account's home.", - Input: map[string]any{"properties": map[string]any{"command": map[string]any{"description": "what to run in it (optional; the login shell when absent)", "type": "string"}, "directory": map[string]any{"description": "where it starts (optional; the account's home when absent)", "type": "string"}}, "type": "object"}}, - }}, - {Name: NodeLauncher, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208", - Serves: []Verb{ - {Name: "menu", Description: "Put a menu of choices in front of the operator and answer with the one chosen, or nothing when the menu was dismissed — the dmenu-compatible contract.", - Input: map[string]any{"properties": map[string]any{"choices": map[string]any{"description": "the lines to choose between, in order", "items": map[string]any{"type": "string"}, "type": "array"}, "prompt": map[string]any{"description": "what the menu asks (optional)", "type": "string"}}, "required": []string{"choices"}, "type": "object"}}, - {Name: "secret", Description: "Ask the operator for a value in a prompt that does not show what is typed, and answer it sealed to the key the asker gives — never in the clear — or cancelled when the prompt was dismissed or not answered in time.", Optional: true, - Input: map[string]any{"properties": map[string]any{"module": map[string]any{"description": "the module whose own secret is asked for", "type": "string"}, "node": map[string]any{"description": "the machine the module runs on", "type": "string"}, "seal_to": map[string]any{"description": "the asker's public sealing key: the answer is sealed to it", "type": "string"}, "secret": map[string]any{"description": "the own secret's name", "type": "string"}, "timeout_seconds": map[string]any{"description": "give up after this long (optional)", "type": "string"}}, "required": []string{"module", "secret", "node", "seal_to"}, "type": "object"}}, - }}, - {Name: NodeNotifier, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208", - Serves: []Verb{ - {Name: "send", Description: "Show the operator a notification.", - Input: map[string]any{"properties": map[string]any{"body": map[string]any{"description": "its text (optional)", "type": "string"}, "title": map[string]any{"description": "the notification's summary", "type": "string"}, "urgency": map[string]any{"description": "low, normal (the default) or critical", "enum": []string{"low", "normal", "critical"}, "type": "string"}}, "required": []string{"title"}, "type": "object"}}, - {Name: "history", Description: "The notifications shown lately, newest first.", - Input: map[string]any{"properties": map[string]any{"limit": map[string]any{"description": "how many (optional, default 20)", "type": "string"}}, "type": "object"}}, - }}, - {Name: NodeLockScreen, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208", - Serves: []Verb{ - {Name: "lock", Description: "Lock the operator's session now.", - Input: map[string]any{"properties": map[string]any{}, "type": "object"}}, - }}, - {Name: NodeClipboard, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208", - Serves: []Verb{ - {Name: "history", Description: "What the clipboard held lately, newest first.", - Input: map[string]any{"properties": map[string]any{"limit": map[string]any{"description": "how many (optional, default 20)", "type": "string"}}, "type": "object"}}, - {Name: "copy", Description: "Put text on the operator's clipboard.", - Input: map[string]any{"properties": map[string]any{"text": map[string]any{"description": "the text", "type": "string"}}, "required": []string{"text"}, "type": "object"}}, - }}, - {Name: NodeBar, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208", - Receives: []Receivable{{Kind: "block", Comment: "#", Offered: true, Shape: &Shape{Places: []PlaceField{{Field: "bar", Values: []string{"bottom", "top"}}, {Field: "place", Values: []string{"resources", "status"}}}, Order: "order", Examples: []map[string]any{map[string]any{"bar": "bottom", "place": "status", "shows": "battery"}, map[string]any{"bar": "bottom", "options": map[string]any{"device": "BAT0"}, "place": "status", "shows": "battery"}, map[string]any{"bar": "bottom", "options": map[string]any{"command": "echo \"a line\"", "interval": float64(5)}, "place": "status", "shows": "command"}, map[string]any{"bar": "bottom", "options": map[string]any{"state": "power.draw"}, "place": "status", "shows": "state"}, map[string]any{"bar": "bottom", "options": map[string]any{"key": "a-machine", "state": "power.draw"}, "place": "status", "shows": "state"}}, Text: true}}}}, - {Name: NodeCompositor, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208"}, - {Name: NodeSecretService, Scope: "node", Grain: "seat-list", Decision: "novox/hq ADR 0208"}, - } -} diff --git a/health-and-repair/go/clients/meshconditionsclient/client.go b/health-and-repair/go/clients/meshconditionsclient/client.go new file mode 100644 index 0000000..5a28c7b --- /dev/null +++ b/health-and-repair/go/clients/meshconditionsclient/client.go @@ -0,0 +1,172 @@ +// Package meshconditionsclient is the logic every observer, healer and reader of conditions shares over the seat +// mesh-conditions (novox/hq ADR 0564, ADR 0634 §4): how a finding's key is made and checked, the scopes and the bounds, +// the order a person reads conditions in, and the answers a notification offers. The layouts are the seat's contract's. +package meshconditionsclient + +import ( + "fmt" + "regexp" + "sort" + "strings" + "time" + + seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" +) + +// The two severities, how soon the operator is needed. +const ( + Urgent = "urgent" + Warning = "warning" +) + +// The scopes a condition's key starts with: what kind of thing is wrong. +const ( + ScopeMachine = "machine" + ScopePlan = "plan" + ScopeCall = "call" + ScopeBuild = "build" + ScopeMerge = "merge" + ScopeProvider = "provider" + ScopeSeat = "seat" + ScopeBus = "bus" + ScopeCore = "core" + ScopeProbe = "probe" + ScopeMesh = "mesh" + // ScopeDelivery is a delivery by its id (novox/hq ADR 0239); ScopeModule a module on a machine, `.`. + ScopeDelivery = "delivery" + ScopeModule = "module" +) + +// Scopes is every scope, in the order a person reads them. +var Scopes = []string{ScopeMachine, ScopePlan, ScopeCall, ScopeBuild, ScopeMerge, ScopeProvider, + ScopeSeat, ScopeBus, ScopeCore, ScopeProbe, ScopeMesh, ScopeDelivery, ScopeModule} + +// Who resolves a condition. +const ( + ResolverSelf = "self" + ResolverOperator = "operator" + ResolverAgent = "agent" +) + +// ResolverHealer is the resolver of a condition a registered healer works on. +func ResolverHealer(name string) string { return "healer:" + name } + +// The bounds a condition keeps. +const ( + KeptEvidence = 10 + KeptTried = 10 + KeptGaps = 8 + MaxSilence = 7 * 24 * time.Hour + ReopenWithin = 10 * time.Minute +) + +// CauseOperatorAnswer is the cause of an act the operator chose. +const CauseOperatorAnswer = "operator-answer" + +// The changes a condition event says. +const ( + ChangeRaised = "raised" + ChangeReopened = "reopened" + ChangeSeverity = "severity" + ChangeResolver = "resolver" + ChangeSilenced = "silenced" + ChangeUnsilenced = "silence-ended" + ChangeCleared = "cleared" + ChangeWords = "words" +) + +var unsafeKey = regexp.MustCompile(`[^A-Za-z0-9_=/-]`) + +// Key is a condition's key: its scope, the parts of its id and a token, each made safe for a bus key. +func Key(scope, id, token string) string { + var parts []string + for _, p := range append(append([]string{scope}, strings.Split(id, ".")...), token) { + p = unsafeKey.ReplaceAllString(strings.TrimSpace(p), "_") + if p == "" { + p = "_" + } + parts = append(parts, p) + } + return strings.Join(parts, ".") +} + +// EventFor is the seat's event that says a change. +func EventFor(change string) string { + switch change { + case ChangeRaised, ChangeReopened: + return seat.EventConditionRaised + case ChangeCleared: + return seat.EventConditionCleared + } + return seat.EventConditionChanged +} + +// ShowKeeper is the call that shows one condition whole. +func ShowKeeper(key string) string { return seat.Seat + "." + seat.VerbConditions + " key=" + key } + +// SilenceAction is the answer a notification offers to stop a condition's messages for a week. +func SilenceAction(key string) seat.Action { + return seat.Action{Label: "Silence for a week", Verb: seat.Seat + "." + seat.VerbSilence, Level: seat.ActionLevelAcknowledge, + Arguments: map[string]string{"key": key, "for": "7d", "why": "", "cause": CauseOperatorAnswer}} +} + +// CheckFinding refuses a finding the keeper could not make a condition of, naming why. +func CheckFinding(f seat.FindingsByKeyValue) error { + known := false + for _, s := range Scopes { + known = known || s == f.Subject.Scope + } + token := f.Ref + if token == "" { + token = f.Kind + } + switch { + case !known: + return fmt.Errorf("health: a finding's scope is one of %s, not %q", strings.Join(Scopes, ", "), f.Subject.Scope) + case strings.TrimSpace(f.Subject.ID) == "": + return fmt.Errorf("health: a %s finding names what it is about", f.Subject.Scope) + case strings.TrimSpace(f.Kind) == "": + return fmt.Errorf("health: the finding %s has no kind", f.Key) + case f.Key != Key(f.Subject.Scope, f.Subject.ID, token): + return fmt.Errorf("health: the finding's key %q is not %q, its scope, id and kind", f.Key, Key(f.Subject.Scope, f.Subject.ID, token)) + case f.Severity != Urgent && f.Severity != Warning: + return fmt.Errorf("health: the finding %s is urgent or a warning, not %q", f.Key, f.Severity) + case strings.TrimSpace(f.Summary) == "": + return fmt.Errorf("health: the finding %s says nothing", f.Key) + case strings.TrimSpace(f.Source) == "": + return fmt.Errorf("health: the finding %s does not say what saw it", f.Key) + case strings.TrimSpace(f.Observer) == "": + return fmt.Errorf("health: the finding %s does not name its observer", f.Key) + case len(f.Evidence) > KeptEvidence: + return fmt.Errorf("health: the finding %s keeps %d observations, at most %d", f.Key, len(f.Evidence), KeptEvidence) + } + return nil +} + +// Order sorts conditions as a person reads them: urgent first, then oldest raised, then by key. +func Order(list []seat.ConditionsByKeyValue) { + raised := func(c seat.ConditionsByKeyValue) time.Time { t, _ := time.Parse(time.RFC3339Nano, c.Raised); return t } + sort.SliceStable(list, func(i, j int) bool { + if list[i].Severity != list[j].Severity { + return list[i].Severity == Urgent + } + if ri, rj := raised(list[i]), raised(list[j]); !ri.Equal(rj) { + return ri.Before(rj) + } + return list[i].Key < list[j].Key + }) +} + +// SilencedAt says whether the condition's messages are silenced at now. +func SilencedAt(c seat.ConditionsByKeyValue, now time.Time) bool { + if c.Silenced == nil { + return false + } + until, err := time.Parse(time.RFC3339Nano, c.Silenced.Until) + return err == nil && now.Before(until) +} + +// Escalated says the condition waits on the operator after healers tried. +func Escalated(c seat.ConditionsByKeyValue) bool { + return c.Resolver == ResolverOperator && len(c.Tried) > 0 +} diff --git a/health-and-repair/go/clients/meshconditionsclient/client_test.go b/health-and-repair/go/clients/meshconditionsclient/client_test.go new file mode 100644 index 0000000..7baef69 --- /dev/null +++ b/health-and-repair/go/clients/meshconditionsclient/client_test.go @@ -0,0 +1,62 @@ +package meshconditionsclient + +import ( + "strings" + "testing" + "time" + + seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions" +) + +// ADR 0564 clause 3: the key rule, the checks of a finding, the order and the silence a person reads conditions by. +func TestAFindingsKeyAndTheOrderOfConditions(t *testing.T) { + if k := Key("machine", "g14.local", "silent"); k != "machine.g14.local.silent" { + t.Errorf("key %s", k) + } + if k := Key("call", "a b", ""); k != "call.a_b._" { + t.Errorf("an unsafe part and an empty token: %s", k) + } + good := seat.FindingsByKeyValue{Key: "machine.g14.silent", Kind: "silent", Severity: Warning, Summary: "x", Source: "S1", + Observer: "mesh-delivery-watchman", Subject: seat.Subject{Scope: ScopeMachine, ID: "g14"}} + for _, c := range []struct { + name string + change func(*seat.FindingsByKeyValue) + refuse string + }{ + {"a good finding", func(*seat.FindingsByKeyValue) {}, ""}, + {"an unknown scope", func(f *seat.FindingsByKeyValue) { f.Subject.Scope = "moon" }, "scope is one of"}, + {"a key that is not its subject's", func(f *seat.FindingsByKeyValue) { f.Key = "machine.g14.other" }, "is not"}, + {"a reference as the key's last part", func(f *seat.FindingsByKeyValue) { f.Ref = "gone"; f.Key = "machine.g14.gone" }, ""}, + {"no summary", func(f *seat.FindingsByKeyValue) { f.Summary = " " }, "says nothing"}, + {"no observer", func(f *seat.FindingsByKeyValue) { f.Observer = "" }, "observer"}, + {"a severity of neither kind", func(f *seat.FindingsByKeyValue) { f.Severity = "info" }, "urgent or a warning"}, + {"too much evidence", func(f *seat.FindingsByKeyValue) { f.Evidence = make([]seat.Evidence, KeptEvidence+1) }, "keeps 11"}, + } { + f := good + c.change(&f) + err := CheckFinding(f) + if (c.refuse == "") != (err == nil) || (err != nil && !strings.Contains(err.Error(), c.refuse)) { + t.Errorf("%s: %v", c.name, err) + } + } + list := []seat.ConditionsByKeyValue{ + {Key: "b", Severity: Warning, Raised: "2026-11-01T10:00:00Z"}, + {Key: "c", Severity: Urgent, Raised: "2026-11-02T10:00:00Z"}, + {Key: "a", Severity: Urgent, Raised: "2026-11-01T10:00:00.5Z", Silenced: &seat.ConditionsByKeyValueSilenced{Until: "2026-11-03T00:00:00Z"}}, + } + Order(list) + if got := list[0].Key + list[1].Key + list[2].Key; got != "acb" { + t.Errorf("order %s, want urgent oldest first", got) + } + now := time.Date(2026, 11, 2, 0, 0, 0, 0, time.UTC) + if !SilencedAt(list[0], now) || SilencedAt(list[0], now.Add(48*time.Hour)) || SilencedAt(list[1], now) { + t.Error("silence is until its end") + } + if EventFor(ChangeReopened) != seat.EventConditionRaised || EventFor(ChangeSilenced) != seat.EventConditionChanged || + EventFor(ChangeCleared) != seat.EventConditionCleared { + t.Error("a change is said as its event") + } + if a := SilenceAction("k"); a.Verb != "mesh-conditions.silence" || ShowKeeper("k") != "mesh-conditions.conditions key=k" { + t.Errorf("the seat's addresses: %+v", a) + } +} diff --git a/health-and-repair/go/clients/meshconditionsclient/go.mod b/health-and-repair/go/clients/meshconditionsclient/go.mod new file mode 100644 index 0000000..8ee6c8e --- /dev/null +++ b/health-and-repair/go/clients/meshconditionsclient/go.mod @@ -0,0 +1,5 @@ +module git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient + +go 1.26.0 + +require git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions v0.0.0 diff --git a/health-and-repair/go/clients/meshhandactsclient/client.go b/health-and-repair/go/clients/meshhandactsclient/client.go new file mode 100644 index 0000000..e9fbaa7 --- /dev/null +++ b/health-and-repair/go/clients/meshhandactsclient/client.go @@ -0,0 +1,88 @@ +// Package meshhandactsclient is the logic every module that records an act by hand shares over the seat mesh-hand-acts +// (novox/hq ADR 0565 clauses 10 and 11, ADR 0634 §4): the recorder, which asks the seat alone, and what makes an act a +// repair. The act's layout is the seat's contract's. +package meshhandactsclient + +import ( + "encoding/json" + "errors" + "fmt" + "strings" + + seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts" +) + +// The verbs an act is recorded under in the log, and the causes and kinds that are no repair. +const ( + VerbRecord = "hand-act record" + VerbDrill = "hand-act drill" + VerbWarrant = "warrant" + // CauseDrill is a drill's cause, never counted as a repair; CauseOperatorAnswer an act the operator chose. + CauseDrill = "drill" + CauseOperatorAnswer = "operator-answer" + // KindRecordedBuilds is a push that only moved recorded builds: no repair (ADR 0242). + KindRecordedBuilds = "recorded-builds" +) + +// Repair says the act counts toward a cause repeated: not a drill, not an operator's answer, not recorded builds. +func Repair(a seat.Act) bool { + return a.Cause != CauseDrill && a.Verb != VerbDrill && a.Cause != CauseOperatorAnswer && a.Kind != KindRecordedBuilds +} + +// Request is one act a recorder records: what, why, its cause, the condition it addresses, and its verb in the log (a +// repair when empty). +type Request struct { + What string `json:"what"` + Why string `json:"why"` + Cause string `json:"cause,omitempty"` + Condition string `json:"condition,omitempty"` + Verb string `json:"verb,omitempty"` +} + +// Taken is which log took an act, and what its holder answered. +type Taken struct { + Log string `json:"log"` + Answer json.RawMessage `json:"answer,omitempty"` +} + +// Recorder records acts for a module through the seat mesh-hand-acts alone. An act the seat does not take is an error +// that says nothing was recorded, never a quiet success. +type Recorder struct { + // Ask calls a seat verb by its address (`seat:.`): the runtime's ask. + Ask func(key string, body any) (json.RawMessage, error) +} + +// NoResponder says err is the bus finding nobody to answer: the seat has no holder now. +func NoResponder(err error) bool { + return err != nil && strings.Contains(err.Error(), "no responders") +} + +// Record records one act. +func (r Recorder) Record(q Request) (Taken, error) { + if strings.TrimSpace(q.What) == "" || strings.TrimSpace(q.Why) == "" { + return Taken{}, errors.New("a hand act says what was done and why: nothing was recorded") + } + body := map[string]any{"what": q.What, "why": q.Why} + for k, v := range map[string]string{"cause": q.Cause, "condition": q.Condition, "verb": q.Verb} { + if v != "" { + body[k] = v + } + } + return r.record(seat.VerbHandAct, body) +} + +// RecordWarrant records what the asking module did on the operator's warrant for its ask. +func (r Recorder) RecordWarrant(asker, ask string) (Taken, error) { + return r.record(seat.VerbWarranted, map[string]any{"asker": asker, "ask": ask}) +} + +func (r Recorder) record(verb string, body any) (Taken, error) { + answer, err := r.Ask("seat:"+seat.Seat+"."+verb, body) + if err == nil { + return Taken{Log: seat.Seat, Answer: answer}, nil + } + if NoResponder(err) { + return Taken{}, fmt.Errorf("%s has no holder that answers: nothing was recorded (%w)", seat.Seat, err) + } + return Taken{}, fmt.Errorf("%s.%s refused, nothing was recorded: %w", seat.Seat, verb, err) +} diff --git a/health-and-repair/go/clients/meshhandactsclient/client_test.go b/health-and-repair/go/clients/meshhandactsclient/client_test.go new file mode 100644 index 0000000..a85870e --- /dev/null +++ b/health-and-repair/go/clients/meshhandactsclient/client_test.go @@ -0,0 +1,73 @@ +package meshhandactsclient + +import ( + "encoding/json" + "errors" + "reflect" + "strings" + "testing" + + seat "git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts" +) + +// ADR 0565 clauses 10 and 11 (issue 1953 L5-S2, S4, L5b): an act reads the controller's entries unchanged, and a +// recorder records through the seat alone, the controller's verbs being gone, and says loudly when nothing was recorded. +func TestAnActIsRecordedInTheLogThatAnswers(t *testing.T) { + // The shape of mesh-controller internal/link HandAct, as its bucket keeps an entry (handacts.go at 0fbb5ef3). + raw := `{"id":"act-1","at":"2026-10-28T09:00:00Z","by":"person.jochen","verb":"hand-act record","why":"a stuck consumer",` + + `"cause":"consumer-lost","condition":"bus.EVENTS.x.consumer-lost","via":"telegram","ask":"a-1","proofs":["P1"],` + + `"requested-by":"bus.EVENTS.x.consumer-lost","outcome":"done"}` + var a seat.Act + if err := json.Unmarshal([]byte(raw), &a); err != nil { + t.Fatal(err) + } + again, _ := json.Marshal(a) + var want, got map[string]any + _ = json.Unmarshal([]byte(raw), &want) + _ = json.Unmarshal(again, &got) + if !reflect.DeepEqual(want, got) || !Repair(a) || Repair(seat.Act{Cause: CauseDrill}) || Repair(seat.Act{Verb: VerbDrill}) { + t.Errorf("the controller's entry reads back as %s", again) + } + noResponders := errors.New("nats: no responders available for request") + for _, c := range []struct { + name string + seat error + ctrl error + log string + refused string + asked string + warrants bool + }{ + {name: "the seat answers", log: seat.Seat, asked: "seat:mesh-hand-acts.hand-act"}, + {name: "no holder: nothing recorded, the controller never asked", seat: noResponders, refused: "nothing was recorded", + asked: "seat:mesh-hand-acts.hand-act"}, + {name: "the seat refuses", seat: errors.New("why is required"), refused: "refused, nothing was recorded", + asked: "seat:mesh-hand-acts.hand-act"}, + {name: "a warrant without a holder", seat: noResponders, warrants: true, refused: "nothing was recorded", + asked: "seat:mesh-hand-acts.warranted"}, + {name: "a warrant recorded", warrants: true, log: seat.Seat, asked: "seat:mesh-hand-acts.warranted"}, + } { + var asked []string + r := Recorder{Ask: func(key string, body any) (json.RawMessage, error) { + asked = append(asked, key) + if strings.HasPrefix(key, "seat:mesh-hand-acts.") { + return json.RawMessage(`{}`), c.seat + } + return json.RawMessage(`{}`), c.ctrl + }} + var taken Taken + var err error + if c.warrants { + taken, err = r.RecordWarrant("mesh-config", "ask-1") + } else { + taken, err = r.Record(Request{What: "silenced", Why: "known", Cause: "silent", Verb: "conditions silence"}) + } + if (c.refused != "") != (err != nil) || (err != nil && !strings.Contains(err.Error(), c.refused)) || taken.Log != c.log || + strings.Join(asked, ",") != c.asked { + t.Errorf("%s: took %q, %v, asked %v", c.name, taken.Log, err, asked) + } + } + if _, err := (Recorder{}).Record(Request{What: "x"}); err == nil { + t.Error("an act with no why was recorded") + } +} diff --git a/health-and-repair/go/clients/meshhandactsclient/go.mod b/health-and-repair/go/clients/meshhandactsclient/go.mod new file mode 100644 index 0000000..575a706 --- /dev/null +++ b/health-and-repair/go/clients/meshhandactsclient/go.mod @@ -0,0 +1,5 @@ +module git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshhandactsclient + +go 1.26.0 + +require git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts v0.0.0 diff --git a/health-and-repair/go/seats/meshconditions/contract.go b/health-and-repair/go/seats/meshconditions/contract.go new file mode 100644 index 0000000..eddd19d --- /dev/null +++ b/health-and-repair/go/seats/meshconditions/contract.go @@ -0,0 +1,384 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// Package meshconditions is the contract of the seat "mesh-conditions", defined whole (seats/mesh-conditions/seat.json): What is wrong with the mesh now (ADR 0564): the open conditions raised from the observers' findings and cleared when a finding goes. +package meshconditions + +import ( + "bytes" + "encoding/json" + "errors" +) + +// Seat is the seat's name. +const Seat = "mesh-conditions" + +const ( + VerbConditions = "conditions" + VerbSilence = "silence" + VerbResync = "resync" +) + +const ( + EventConditionRaised = "condition-raised" + EventConditionChanged = "condition-changed" + EventConditionCleared = "condition-cleared" +) + +// Verbs is every verb the seat serves, in its definition's order: a new copy each call. +func Verbs() []string { + return []string{VerbConditions, VerbSilence, VerbResync} +} + +// Events is every event published on the seat's subject: a new copy each call. +func Events() []string { + return []string{EventConditionRaised, EventConditionChanged, EventConditionCleared} +} + +// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects. +const ( + Family = "mesh-conditions.events" + FamilyMaxAgeDays = 7 +) + +// SubjectConditionRaised is where condition-raised is published. +const SubjectConditionRaised = "mesh.seat.mesh-conditions.event.condition-raised" + +// SubjectConditionChanged is where condition-changed is published. +const SubjectConditionChanged = "mesh.seat.mesh-conditions.event.condition-changed" + +// SubjectConditionCleared is where condition-cleared is published. +const SubjectConditionCleared = "mesh.seat.mesh-conditions.event.condition-cleared" + +const ( + StateConditions = "conditions" + StateFindings = "findings" +) + +// ConditionsByKeyKey is the key of one value of the state conditions under the form "". +func ConditionsByKeyKey(key string) string { return key } + +// ParseConditionsByKeyKey reads the parts of a key of the form "", in the order of the form; the last result is false for a key of another form. +func ParseConditionsByKeyKey(key string) (string, bool) { + rest := key + if rest == "" { + return "", false + } + v0 := rest + rest = "" + if rest != "" { + return "", false + } + return v0, true +} + +// DecodeConditionsByKeyValue reads one value of the state conditions under the form "", refusing a field ConditionsByKeyValue does not know and any text after the value. +func DecodeConditionsByKeyValue(raw []byte) (ConditionsByKeyValue, error) { + var v ConditionsByKeyValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// FindingsByKeyKey is the key of one value of the state findings under the form "". +func FindingsByKeyKey(key string) string { return key } + +// ParseFindingsByKeyKey reads the parts of a key of the form "", in the order of the form; the last result is false for a key of another form. +func ParseFindingsByKeyKey(key string) (string, bool) { + rest := key + if rest == "" { + return "", false + } + v0 := rest + rest = "" + if rest != "" { + return "", false + } + return v0, true +} + +// DecodeFindingsByKeyValue reads one value of the state findings under the form "", refusing a field FindingsByKeyValue does not know and any text after the value. +func DecodeFindingsByKeyValue(raw []byte) (FindingsByKeyValue, error) { + var v FindingsByKeyValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// Action is one answer the operator may give from a notification. +type Action struct { + Arguments map[string]string `json:"arguments,omitempty"` + Label string `json:"label"` + Level string `json:"level,omitempty"` + Machine string `json:"machine,omitempty"` + // the seat verb the operator channel calls + Verb string `json:"verb"` +} + +// The values of Action's level. +const ( + ActionLevelAcknowledge = "acknowledge" + ActionLevelApprove = "approve" +) + +// ConditionEventGapsItem is a type of the contract. +type ConditionEventGapsItem struct { + Cleared string `json:"cleared,omitempty"` + Reopened string `json:"reopened,omitempty"` +} + +// ConditionEventSilenced is a type of the contract. +type ConditionEventSilenced struct { + By string `json:"by,omitempty"` + Since string `json:"since,omitempty"` + Until string `json:"until,omitempty"` + Why string `json:"why,omitempty"` +} + +// ConditionEventTriedItem is a type of the contract. +type ConditionEventTriedItem struct { + At string `json:"at,omitempty"` + By string `json:"by,omitempty"` + Outcome string `json:"outcome,omitempty"` + What string `json:"what,omitempty"` +} + +// ConditionEvent is a condition and what changed. +type ConditionEvent struct { + Actions []Action `json:"actions,omitempty"` + At string `json:"at"` + Change string `json:"change"` + Cleared string `json:"cleared,omitempty"` + Count int64 `json:"count,omitempty"` + Epoch int64 `json:"epoch,omitempty"` + Event string `json:"event"` + // newest first, at most 10 + Evidence []Evidence `json:"evidence,omitempty"` + Explanation string `json:"explanation,omitempty"` + First string `json:"first,omitempty"` + Gaps []ConditionEventGapsItem `json:"gaps,omitempty"` + Headline string `json:"headline,omitempty"` + // scope.id.token + Key string `json:"key"` + Kind string `json:"kind,omitempty"` + LastObserved string `json:"last-observed,omitempty"` + LooksUnread string `json:"looks-unread,omitempty"` + Needs string `json:"needs,omitempty"` + Observations int64 `json:"observations,omitempty"` + Raised string `json:"raised,omitempty"` + Resolved string `json:"resolved,omitempty"` + // self, operator, agent or healer: + Resolver string `json:"resolver,omitempty"` + Severity string `json:"severity,omitempty"` + // the tool call that shows it + Show string `json:"show,omitempty"` + Silenced *ConditionEventSilenced `json:"silenced,omitempty"` + // the row, probe or event that saw it + Source string `json:"source,omitempty"` + Subject *Subject `json:"subject,omitempty"` + // one line + Summary string `json:"summary,omitempty"` + Tried []ConditionEventTriedItem `json:"tried,omitempty"` + Was string `json:"was,omitempty"` + Why string `json:"why,omitempty"` +} + +// The values of ConditionEvent's change. +const ( + ConditionEventChangeRaised = "raised" + ConditionEventChangeReopened = "reopened" + ConditionEventChangeSeverity = "severity" + ConditionEventChangeResolver = "resolver" + ConditionEventChangeSilenced = "silenced" + ConditionEventChangeSilenceEnded = "silence-ended" + ConditionEventChangeCleared = "cleared" + ConditionEventChangeWords = "words" +) + +// The values of ConditionEvent's severity. +const ( + ConditionEventSeverityUrgent = "urgent" + ConditionEventSeverityWarning = "warning" +) + +// Evidence is one observation, as it was said. +type Evidence struct { + At string `json:"at"` + Said string `json:"said"` +} + +// Subject is what the condition is about. +type Subject struct { + // the other machines it concerns + Also []string `json:"also,omitempty"` + // its id within the scope + ID string `json:"id"` + // the machine it concerns, when there is one + Machine string `json:"machine,omitempty"` + // machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module + Scope string `json:"scope"` +} + +// ConditionsInput is what conditions takes. +type ConditionsInput struct { + // with history: how many days back (default 7, at most 90) + Days string `json:"days,omitempty"` + // "true": every raising, change, silence and clearing lately, oldest first + History string `json:"history,omitempty"` + // a condition's key: that one whole; with history, only its transitions + Key string `json:"key,omitempty"` + // only those about this machine + Machine string `json:"machine,omitempty"` + // only this scope: machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module + Scope string `json:"scope,omitempty"` + // only urgent, or only warning + Severity string `json:"severity,omitempty"` +} + +// The values of ConditionsInput's history. +const ( + ConditionsInputHistoryTrue = "true" + ConditionsInputHistoryFalse = "false" +) + +// ConditionsAnswer is what conditions answers. +type ConditionsAnswer = map[string]any + +// SilenceInput is what silence takes. +type SilenceInput struct { + // the cause in a word (the condition's kind when absent) + Cause string `json:"cause,omitempty"` + // how long: 30m, 4h, 2d; at most 7d + For string `json:"for"` + // the condition's key + Key string `json:"key"` + // why, required and recorded + Why string `json:"why"` +} + +// SilenceAnswer is what silence answers. +type SilenceAnswer = map[string]any + +// ResyncInput is what resync takes. +type ResyncInput struct { +} + +// ResyncAnswer is what resync answers. +type ResyncAnswer = map[string]any + +// ConditionRaisedEvent is the body of the event condition-raised. +type ConditionRaisedEvent = ConditionEvent + +// ConditionChangedEvent is the body of the event condition-changed. +type ConditionChangedEvent = ConditionEvent + +// ConditionClearedEvent is the body of the event condition-cleared. +type ConditionClearedEvent = ConditionEvent + +// ConditionsByKeyValueGapsItem is a type of the contract. +type ConditionsByKeyValueGapsItem struct { + Cleared string `json:"cleared,omitempty"` + Reopened string `json:"reopened,omitempty"` +} + +// ConditionsByKeyValueSilenced is a type of the contract. +type ConditionsByKeyValueSilenced struct { + By string `json:"by,omitempty"` + Since string `json:"since,omitempty"` + Until string `json:"until,omitempty"` + Why string `json:"why,omitempty"` +} + +// ConditionsByKeyValueTriedItem is a type of the contract. +type ConditionsByKeyValueTriedItem struct { + At string `json:"at,omitempty"` + By string `json:"by,omitempty"` + Outcome string `json:"outcome,omitempty"` + What string `json:"what,omitempty"` +} + +// ConditionsByKeyValue is one value of the state conditions under the key . +type ConditionsByKeyValue struct { + Actions []Action `json:"actions,omitempty"` + Count int64 `json:"count,omitempty"` + Epoch int64 `json:"epoch,omitempty"` + // newest first, at most 10 + Evidence []Evidence `json:"evidence,omitempty"` + Explanation string `json:"explanation,omitempty"` + First string `json:"first,omitempty"` + Gaps []ConditionsByKeyValueGapsItem `json:"gaps,omitempty"` + Headline string `json:"headline,omitempty"` + // scope.id.token + Key string `json:"key"` + Kind string `json:"kind"` + LastObserved string `json:"last-observed"` + LooksUnread string `json:"looks-unread,omitempty"` + Needs string `json:"needs,omitempty"` + Observations int64 `json:"observations,omitempty"` + Raised string `json:"raised"` + Resolved string `json:"resolved,omitempty"` + // self, operator, agent or healer: + Resolver string `json:"resolver,omitempty"` + Severity string `json:"severity"` + Silenced *ConditionsByKeyValueSilenced `json:"silenced,omitempty"` + // the row, probe or event that saw it + Source string `json:"source,omitempty"` + Subject Subject `json:"subject"` + // one line + Summary string `json:"summary,omitempty"` + Tried []ConditionsByKeyValueTriedItem `json:"tried,omitempty"` +} + +// The values of ConditionsByKeyValue's severity. +const ( + ConditionsByKeyValueSeverityUrgent = "urgent" + ConditionsByKeyValueSeverityWarning = "warning" +) + +// FindingsByKeyValue is one value of the state findings under the key . +type FindingsByKeyValue struct { + Actions []Action `json:"actions,omitempty"` + At string `json:"at"` + // newest first, at most 10 + Evidence []Evidence `json:"evidence,omitempty"` + Explanation string `json:"explanation,omitempty"` + Headline string `json:"headline,omitempty"` + // scope.id.token + Key string `json:"key"` + Kind string `json:"kind"` + Needs string `json:"needs,omitempty"` + // the module that wrote it + Observer string `json:"observer"` + // the key's last part when it is not the kind + Ref string `json:"ref,omitempty"` + Resolved string `json:"resolved,omitempty"` + // self, operator, agent or healer: + Resolver string `json:"resolver,omitempty"` + // what of its own saw it last + Run string `json:"run,omitempty"` + Severity string `json:"severity"` + // the row, probe or event that saw it + Source string `json:"source"` + Subject Subject `json:"subject"` + // one line + Summary string `json:"summary"` + // layout version, 1 + V int64 `json:"v"` +} + +// The values of FindingsByKeyValue's severity. +const ( + FindingsByKeyValueSeverityUrgent = "urgent" + FindingsByKeyValueSeverityWarning = "warning" +) diff --git a/health-and-repair/go/seats/meshconditions/go.mod b/health-and-repair/go/seats/meshconditions/go.mod new file mode 100644 index 0000000..4662aa6 --- /dev/null +++ b/health-and-repair/go/seats/meshconditions/go.mod @@ -0,0 +1,3 @@ +module git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshconditions + +go 1.26.0 diff --git a/health-and-repair/go/seats/meshhandacts/contract.go b/health-and-repair/go/seats/meshhandacts/contract.go new file mode 100644 index 0000000..dfe00ce --- /dev/null +++ b/health-and-repair/go/seats/meshhandacts/contract.go @@ -0,0 +1,222 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// Package meshhandacts is the contract of the seat "mesh-hand-acts", defined whole (seats/mesh-hand-acts/seat.json): Acts done by hand, recorded with who, why and cause (ADR 0565): the log every module records through. +package meshhandacts + +import ( + "bytes" + "encoding/json" + "errors" + "strings" +) + +// Seat is the seat's name. +const Seat = "mesh-hand-acts" + +const ( + VerbHandAct = "hand-act" + VerbHandActs = "hand-acts" + VerbWarranted = "warranted" +) + +const ( + EventActRecorded = "act-recorded" +) + +// Verbs is every verb the seat serves, in its definition's order: a new copy each call. +func Verbs() []string { + return []string{VerbHandAct, VerbHandActs, VerbWarranted} +} + +// Events is every event published on the seat's subject: a new copy each call. +func Events() []string { + return []string{EventActRecorded} +} + +// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects. +const ( + Family = "mesh-hand-acts.events" + FamilyMaxAgeDays = 7 +) + +// SubjectActRecorded is where act-recorded is published. +const SubjectActRecorded = "mesh.seat.mesh-hand-acts.event.act-recorded" + +const ( + StateActs = "acts" + StateWarrants = "warrants" +) + +// ActsByIDKey is the key of one value of the state acts under the form "". +func ActsByIDKey(id string) string { return id } + +// ParseActsByIDKey reads the parts of a key of the form "", in the order of the form; the last result is false for a key of another form. +func ParseActsByIDKey(key string) (string, bool) { + rest := key + if rest == "" { + return "", false + } + v0 := rest + rest = "" + if rest != "" { + return "", false + } + return v0, true +} + +// DecodeActsByIDValue reads one value of the state acts under the form "", refusing a field ActsByIDValue does not know and any text after the value. +func DecodeActsByIDValue(raw []byte) (ActsByIDValue, error) { + var v ActsByIDValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// WarrantsByAskerAskKey is the key of one value of the state warrants under the form ".". +func WarrantsByAskerAskKey(asker, ask string) string { return asker + "." + ask } + +// ParseWarrantsByAskerAskKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. +func ParseWarrantsByAskerAskKey(key string) (string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", false + } + rest = after0 + if rest == "" { + return "", "", false + } + v2 := rest + rest = "" + if rest != "" { + return "", "", false + } + return v0, v2, true +} + +// DecodeWarrantsByAskerAskValue reads one value of the state warrants under the form ".", refusing a field WarrantsByAskerAskValue does not know and any text after the value. +func DecodeWarrantsByAskerAskValue(raw []byte) (WarrantsByAskerAskValue, error) { + var v WarrantsByAskerAskValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// Act is one act done by hand. +type Act struct { + Arguments []string `json:"arguments,omitempty"` + Ask string `json:"ask,omitempty"` + At string `json:"at"` + // the caller the runtime proved + By string `json:"by"` + Carried []string `json:"carried,omitempty"` + Cause string `json:"cause"` + Condition string `json:"condition,omitempty"` + ID string `json:"id"` + // what the act was read to be from what it did + Kind string `json:"kind,omitempty"` + // which log holds it, set when it is read + Log string `json:"log,omitempty"` + Outcome string `json:"outcome,omitempty"` + Proofs []string `json:"proofs,omitempty"` + RequestedBy string `json:"requested-by,omitempty"` + Verb string `json:"verb"` + Via string `json:"via,omitempty"` + Why string `json:"why"` +} + +// HandActInput is what hand-act takes. +type HandActInput struct { + // the cause in a word, or a condition's kind + Cause string `json:"cause,omitempty"` + // the key of the condition it addressed, if any (optional) + Condition string `json:"condition,omitempty"` + // the act's verb in the log: a repair when absent, `hand-act drill` for a drill, or a recorder's own + Verb string `json:"verb,omitempty"` + // what was done, in a line + What string `json:"what"` + // why it had to be done by hand + Why string `json:"why"` +} + +// HandActAnswer is what hand-act answers. +type HandActAnswer = map[string]any + +// HandActsInput is what hand-acts takes. +type HandActsInput struct { + // only this cause + Cause string `json:"cause,omitempty"` + // how many days back (default 14) + Days string `json:"days,omitempty"` + // "true": every warrant recorded + Warrants string `json:"warrants,omitempty"` + // only acts whose who contains this + Who string `json:"who,omitempty"` +} + +// The values of HandActsInput's warrants. +const ( + HandActsInputWarrantsTrue = "true" + HandActsInputWarrantsFalse = "false" +) + +// HandActsAnswer is what hand-acts answers. +type HandActsAnswer = map[string]any + +// WarrantedInput is what warranted takes. +type WarrantedInput struct { + // its ask's id + Ask string `json:"ask"` + // the module that asked + Asker string `json:"asker"` +} + +// WarrantedAnswer is what warranted answers. +type WarrantedAnswer = map[string]any + +// ActRecordedEvent is the body of the event act-recorded. +type ActRecordedEvent = Act + +// ActsByIDValue is one value of the state acts under the key . +type ActsByIDValue struct { + Arguments []string `json:"arguments,omitempty"` + Ask string `json:"ask,omitempty"` + At string `json:"at"` + // the caller the runtime proved + By string `json:"by"` + Carried []string `json:"carried,omitempty"` + Cause string `json:"cause"` + Condition string `json:"condition,omitempty"` + ID string `json:"id"` + // what the act was read to be from what it did + Kind string `json:"kind,omitempty"` + // which log holds it, set when it is read + Log string `json:"log,omitempty"` + Outcome string `json:"outcome,omitempty"` + Proofs []string `json:"proofs,omitempty"` + RequestedBy string `json:"requested-by,omitempty"` + Verb string `json:"verb"` + Via string `json:"via,omitempty"` + Why string `json:"why"` +} + +// WarrantsByAskerAskValue is one value of the state warrants under the key .. +type WarrantsByAskerAskValue struct { + Ask string `json:"ask"` + Asker string `json:"asker"` + At string `json:"at,omitempty"` + By string `json:"by,omitempty"` + Option string `json:"option,omitempty"` +} diff --git a/health-and-repair/go/seats/meshhandacts/go.mod b/health-and-repair/go/seats/meshhandacts/go.mod new file mode 100644 index 0000000..101f623 --- /dev/null +++ b/health-and-repair/go/seats/meshhandacts/go.mod @@ -0,0 +1,3 @@ +module git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhandacts + +go 1.26.0 diff --git a/health-and-repair/go/seats/meshhealers/contract.go b/health-and-repair/go/seats/meshhealers/contract.go new file mode 100644 index 0000000..b26eaaf --- /dev/null +++ b/health-and-repair/go/seats/meshhealers/contract.go @@ -0,0 +1,197 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// Package meshhealers is the contract of the seat "mesh-healers", defined whole (seats/mesh-healers/seat.json): The healers (ADR 0564): each a registered response to one kind of condition, within a budget and the mesh-wide brake. +package meshhealers + +import ( + "bytes" + "encoding/json" + "errors" +) + +// Seat is the seat's name. +const Seat = "mesh-healers" + +const ( + VerbHealers = "healers" + VerbHeal = "heal" + VerbDrill = "drill" +) + +const ( + EventHealerActed = "healer-acted" +) + +// Verbs is every verb the seat serves, in its definition's order: a new copy each call. +func Verbs() []string { + return []string{VerbHealers, VerbHeal, VerbDrill} +} + +// Events is every event published on the seat's subject: a new copy each call. +func Events() []string { + return []string{EventHealerActed} +} + +// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects. +const ( + Family = "mesh-healers.events" + FamilyMaxAgeDays = 7 +) + +// SubjectHealerActed is where healer-acted is published. +const SubjectHealerActed = "mesh.seat.mesh-healers.event.healer-acted" + +const ( + StateAttempts = "attempts" + StateWould = "would" +) + +// AttemptsByAttemptKey is the key of one value of the state attempts under the form "". +func AttemptsByAttemptKey(attempt string) string { return attempt } + +// ParseAttemptsByAttemptKey reads the parts of a key of the form "", in the order of the form; the last result is false for a key of another form. +func ParseAttemptsByAttemptKey(key string) (string, bool) { + rest := key + if rest == "" { + return "", false + } + v0 := rest + rest = "" + if rest != "" { + return "", false + } + return v0, true +} + +// DecodeAttemptsByAttemptValue reads one value of the state attempts under the form "", refusing a field AttemptsByAttemptValue does not know and any text after the value. +func DecodeAttemptsByAttemptValue(raw []byte) (AttemptsByAttemptValue, error) { + var v AttemptsByAttemptValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// WouldByWouldKey is the key of one value of the state would under the form "". +func WouldByWouldKey(would string) string { return would } + +// ParseWouldByWouldKey reads the parts of a key of the form "", in the order of the form; the last result is false for a key of another form. +func ParseWouldByWouldKey(key string) (string, bool) { + rest := key + if rest == "" { + return "", false + } + v0 := rest + rest = "" + if rest != "" { + return "", false + } + return v0, true +} + +// DecodeWouldByWouldValue reads one value of the state would under the form "", refusing a field WouldByWouldValue does not know and any text after the value. +func DecodeWouldByWouldValue(raw []byte) (WouldByWouldValue, error) { + var v WouldByWouldValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// HealersInput is what healers takes. +type HealersInput struct { + // how many days of acts back (default 7) + Days string `json:"days,omitempty"` +} + +// HealersAnswer is what healers answers. +type HealersAnswer = map[string]any + +// HealInput is what heal takes. +type HealInput struct { + // the condition's key + Key string `json:"key"` + // why now, kept with the act + Why string `json:"why"` +} + +// HealAnswer is what heal answers. +type HealAnswer = map[string]any + +// DrillInput is what drill takes. +type DrillInput struct { + // the key of the condition the drill is meant to raise, if any (optional) + Condition string `json:"condition,omitempty"` + // what was done on purpose, in a line + What string `json:"what"` + // what the drill tests + Why string `json:"why"` +} + +// DrillAnswer is what drill answers. +type DrillAnswer = map[string]any + +// HealerActedEvent is the body of the event healer-acted. +type HealerActedEvent struct { + Act string `json:"act,omitempty"` + At string `json:"at"` + Budget string `json:"budget,omitempty"` + By string `json:"by,omitempty"` + Condition string `json:"condition"` + Event string `json:"event"` + Healer string `json:"healer"` + Kind string `json:"kind,omitempty"` + Outcome string `json:"outcome"` + Said string `json:"said,omitempty"` + Show string `json:"show,omitempty"` +} + +// The values of HealerActedEvent's outcome. +const ( + HealerActedEventOutcomeActing = "acting" + HealerActedEventOutcomeActed = "acted" + HealerActedEventOutcomeFailed = "failed" + HealerActedEventOutcomeEscalated = "escalated" +) + +// AttemptsByAttemptValue is one value of the state attempts under the key . +type AttemptsByAttemptValue struct { + Act string `json:"act"` + Begun string `json:"begun"` + // what the budget counts by: the condition's key unless the healer says another + BudgetKey string `json:"budget-key"` + Condition string `json:"condition"` + Finished string `json:"finished,omitempty"` + Healer string `json:"healer"` + Kind string `json:"kind"` + Outcome string `json:"outcome"` + Said string `json:"said,omitempty"` +} + +// The values of AttemptsByAttemptValue's outcome. +const ( + AttemptsByAttemptValueOutcomeActing = "acting" + AttemptsByAttemptValueOutcomeActed = "acted" + AttemptsByAttemptValueOutcomeFailed = "failed" + AttemptsByAttemptValueOutcomeEscalated = "escalated" +) + +// WouldByWouldValue is one value of the state would under the key . +type WouldByWouldValue struct { + Act string `json:"act"` + At string `json:"at"` + Condition string `json:"condition"` + Healer string `json:"healer"` + Kind string `json:"kind,omitempty"` + Why string `json:"why,omitempty"` +} diff --git a/health-and-repair/go/seats/meshhealers/go.mod b/health-and-repair/go/seats/meshhealers/go.mod new file mode 100644 index 0000000..9eb5a51 --- /dev/null +++ b/health-and-repair/go/seats/meshhealers/go.mod @@ -0,0 +1,3 @@ +module git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshhealers + +go 1.26.0 diff --git a/health-and-repair/go/seats/meshselfcheck/contract.go b/health-and-repair/go/seats/meshselfcheck/contract.go new file mode 100644 index 0000000..2c67551 --- /dev/null +++ b/health-and-repair/go/seats/meshselfcheck/contract.go @@ -0,0 +1,101 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// Package meshselfcheck is the contract of the seat "mesh-self-check", defined whole (seats/mesh-self-check/seat.json): The mesh checking itself (ADR 0564): probes run every five minutes, each failure a finding the seat's conditions raise. +package meshselfcheck + +// Seat is the seat's name. +const Seat = "mesh-self-check" + +const ( + VerbDoctor = "doctor" + VerbProbes = "probes" +) + +const ( + EventChecked = "checked" +) + +// Verbs is every verb the seat serves, in its definition's order: a new copy each call. +func Verbs() []string { + return []string{VerbDoctor, VerbProbes} +} + +// Events is every event published on the seat's subject: a new copy each call. +func Events() []string { + return []string{EventChecked} +} + +// Family is where the seat's events are kept: the shared events stream, filtered to the seat's subjects. +const ( + Family = "mesh-self-check.events" + FamilyMaxAgeDays = 7 +) + +// SubjectChecked is where checked is published. +const SubjectChecked = "mesh.seat.mesh-self-check.event.checked" + +// ProbeVerdict is one probe's verdict. +type ProbeVerdict struct { + Error string `json:"error,omitempty"` + Found []string `json:"found,omitempty"` + FoundCount int64 `json:"found-count,omitempty"` + ID string `json:"id"` + More string `json:"more,omitempty"` + Took string `json:"took,omitempty"` + Unconfirmed []string `json:"unconfirmed,omitempty"` + Verdict string `json:"verdict"` +} + +// The values of ProbeVerdict's verdict. +const ( + ProbeVerdictVerdictPassed = "passed" + ProbeVerdictVerdictFailed = "failed" + ProbeVerdictVerdictFailedToRun = "failed-to-run" + ProbeVerdictVerdictDeferred = "deferred" +) + +// DoctorInput is what doctor takes. +type DoctorInput struct { + // one probe's id (D6): its verdict alone, with every finding + Probe string `json:"probe,omitempty"` + // "true": run every probe now and answer the verdict + Run string `json:"run,omitempty"` +} + +// The values of DoctorInput's run. +const ( + DoctorInputRunTrue = "true" + DoctorInputRunFalse = "false" +) + +// DoctorAnswer is what doctor answers. +type DoctorAnswer = map[string]any + +// ProbesInput is what probes takes. +type ProbesInput struct { +} + +// ProbesAnswer is what probes answers. +type ProbesAnswer = map[string]any + +// CheckedEventCounts is a type of the contract. +type CheckedEventCounts struct { + Deferred int64 `json:"deferred,omitempty"` + Failed int64 `json:"failed,omitempty"` + FailedToRun int64 `json:"failed-to-run,omitempty"` + Passed int64 `json:"passed,omitempty"` +} + +// CheckedEvent is the body of the event checked. +type CheckedEvent struct { + At string `json:"at"` + Controller string `json:"controller,omitempty"` + Counts *CheckedEventCounts `json:"counts,omitempty"` + IntervalSeconds int64 `json:"interval-seconds,omitempty"` + Probes []ProbeVerdict `json:"probes"` + Run string `json:"run"` + Started string `json:"started,omitempty"` + Took string `json:"took,omitempty"` + Unsaid int64 `json:"unsaid,omitempty"` + Why string `json:"why,omitempty"` +} diff --git a/health-and-repair/go/seats/meshselfcheck/go.mod b/health-and-repair/go/seats/meshselfcheck/go.mod new file mode 100644 index 0000000..a45e4c3 --- /dev/null +++ b/health-and-repair/go/seats/meshselfcheck/go.mod @@ -0,0 +1,3 @@ +module git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshselfcheck + +go 1.26.0 diff --git a/health-and-repair/go/seats/meshwatchdogs/contract.go b/health-and-repair/go/seats/meshwatchdogs/contract.go new file mode 100644 index 0000000..05c76ca --- /dev/null +++ b/health-and-repair/go/seats/meshwatchdogs/contract.go @@ -0,0 +1,266 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// Package meshwatchdogs is the contract of the seat "mesh-watchdogs", defined whole (seats/mesh-watchdogs/seat.json): The watchman's seat (ADR 0564, ADR 0433): signals that expire unless renewed, what stopped moving, who is present, the dead letters. +package meshwatchdogs + +import ( + "bytes" + "encoding/json" + "errors" + "strings" +) + +// Seat is the seat's name. +const Seat = "mesh-watchdogs" + +const ( + VerbSignals = "signals" + VerbStuck = "stuck" + VerbPresence = "presence" + VerbDeadLetters = "dead-letters" + VerbResync = "resync" +) + +// Verbs is every verb the seat serves, in its definition's order: a new copy each call. +func Verbs() []string { + return []string{VerbSignals, VerbStuck, VerbPresence, VerbDeadLetters, VerbResync} +} + +const ( + StatePresence = "presence" + StatePulse = "pulse" + StateStuck = "stuck" +) + +// PresenceByNodeKey is the key of one value of the state presence under the form "". +func PresenceByNodeKey(node string) string { return node } + +// ParsePresenceByNodeKey reads the parts of a key of the form "", in the order of the form; the last result is false for a key of another form. +func ParsePresenceByNodeKey(key string) (string, bool) { + rest := key + if rest == "" { + return "", false + } + v0 := rest + rest = "" + if rest != "" { + return "", false + } + return v0, true +} + +// DecodePresenceByNodeValue reads one value of the state presence under the form "", refusing a field PresenceByNodeValue does not know and any text after the value. +func DecodePresenceByNodeValue(raw []byte) (PresenceByNodeValue, error) { + var v PresenceByNodeValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// PresenceByPartPartInstanceKey is the key of one value of the state presence under the form "part..". +func PresenceByPartPartInstanceKey(part, instance string) string { + return "part." + part + "." + instance +} + +// ParsePresenceByPartPartInstanceKey reads the parts of a key of the form "part..", in the order of the form; the last result is false for a key of another form. +func ParsePresenceByPartPartInstanceKey(key string) (string, string, bool) { + rest := key + if !strings.HasPrefix(rest, "part.") { + return "", "", false + } + rest = rest[5:] + v1, after1, found1 := strings.Cut(rest, ".") + if !found1 || v1 == "" { + return "", "", false + } + rest = after1 + if rest == "" { + return "", "", false + } + v3 := rest + rest = "" + if rest != "" { + return "", "", false + } + return v1, v3, true +} + +// DecodePresenceByPartPartInstanceValue reads one value of the state presence under the form "part..", refusing a field PresenceByPartPartInstanceValue does not know and any text after the value. +func DecodePresenceByPartPartInstanceValue(raw []byte) (PresenceByPartPartInstanceValue, error) { + var v PresenceByPartPartInstanceValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// PulseByBucketKeyKey is the key of one value of the state pulse under the form ".". +func PulseByBucketKeyKey(bucket, key string) string { return bucket + "." + key } + +// ParsePulseByBucketKeyKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. +func ParsePulseByBucketKeyKey(key string) (string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", false + } + rest = after0 + if rest == "" { + return "", "", false + } + v2 := rest + rest = "" + if rest != "" { + return "", "", false + } + return v0, v2, true +} + +// DecodePulseByBucketKeyValue reads one value of the state pulse under the form ".", refusing a field PulseByBucketKeyValue does not know and any text after the value. +func DecodePulseByBucketKeyValue(raw []byte) (PulseByBucketKeyValue, error) { + var v PulseByBucketKeyValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// StuckByBucketKeyKey is the key of one value of the state stuck under the form ".". +func StuckByBucketKeyKey(bucket, key string) string { return bucket + "." + key } + +// ParseStuckByBucketKeyKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. +func ParseStuckByBucketKeyKey(key string) (string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", false + } + rest = after0 + if rest == "" { + return "", "", false + } + v2 := rest + rest = "" + if rest != "" { + return "", "", false + } + return v0, v2, true +} + +// DecodeStuckByBucketKeyValue reads one value of the state stuck under the form ".", refusing a field StuckByBucketKeyValue does not know and any text after the value. +func DecodeStuckByBucketKeyValue(raw []byte) (StuckByBucketKeyValue, error) { + var v StuckByBucketKeyValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// SignalsInput is what signals takes. +type SignalsInput struct { + // one row, e.g. S1 + Row string `json:"row,omitempty"` +} + +// SignalsAnswer is what signals answers. +type SignalsAnswer = map[string]any + +// StuckInput is what stuck takes. +type StuckInput struct { + // only those on this machine + Machine string `json:"machine,omitempty"` +} + +// StuckAnswer is what stuck answers. +type StuckAnswer = map[string]any + +// PresenceInput is what presence takes. +type PresenceInput struct { + // one machine + Machine string `json:"machine,omitempty"` +} + +// PresenceAnswer is what presence answers. +type PresenceAnswer = map[string]any + +// DeadLettersInput is what dead-letters takes. +type DeadLettersInput struct { + // with deliver or drop: the cause in a word (dead-letter when absent) + Cause string `json:"cause,omitempty"` + // a consumer's name, or .: only what that one gave up on + Consumer string `json:"consumer,omitempty"` + // a dead letter's id: deliver it again to the consumer that gave it up (needs why) + Deliver string `json:"deliver,omitempty"` + // a dead letter's id: drop it for good (needs why) + Drop string `json:"drop,omitempty"` + // a dead letter's id, as the list gives it: that one whole, with its message + ID string `json:"id,omitempty"` + // how many to list, newest first (default 50); only when listing + Limit string `json:"limit,omitempty"` + // with deliver or drop: why, required and recorded + Why string `json:"why,omitempty"` +} + +// DeadLettersAnswer is what dead-letters answers. +type DeadLettersAnswer = map[string]any + +// ResyncInput is what resync takes. +type ResyncInput struct { +} + +// ResyncAnswer is what resync answers. +type ResyncAnswer = map[string]any + +// PresenceByNodeValue is one value of the state presence under the key . +type PresenceByNodeValue struct { + Online bool `json:"online"` + V int64 `json:"v,omitempty"` +} + +// PresenceByPartPartInstanceValue is one value of the state presence under the key part... +type PresenceByPartPartInstanceValue struct { + Online bool `json:"online"` + V int64 `json:"v,omitempty"` +} + +// PulseByBucketKeyValue is one value of the state pulse under the key .. +type PulseByBucketKeyValue struct { + Bucket string `json:"bucket"` + Key string `json:"key"` + Owes string `json:"owes,omitempty"` + Revision int64 `json:"revision"` + V int64 `json:"v,omitempty"` +} + +// StuckByBucketKeyValue is one value of the state stuck under the key .. +type StuckByBucketKeyValue struct { + Bucket string `json:"bucket"` + Key string `json:"key"` + // it has moved again + Moved bool `json:"moved,omitempty"` + Since string `json:"since"` + V int64 `json:"v,omitempty"` + // what it waited for, in words + Waited string `json:"waited,omitempty"` +} diff --git a/health-and-repair/go/seats/meshwatchdogs/go.mod b/health-and-repair/go/seats/meshwatchdogs/go.mod new file mode 100644 index 0000000..e2e0cf2 --- /dev/null +++ b/health-and-repair/go/seats/meshwatchdogs/go.mod @@ -0,0 +1,3 @@ +module git.novox.be/novox/mesh-contracts/health-and-repair/go/seats/meshwatchdogs + +go 1.26.0 diff --git a/health-and-repair/seats/index.json b/health-and-repair/seats/index.json new file mode 100644 index 0000000..8cbd1f8 --- /dev/null +++ b/health-and-repair/seats/index.json @@ -0,0 +1,10 @@ +{ + "format": 1, + "seats": [ + "mesh-conditions", + "mesh-hand-acts", + "mesh-healers", + "mesh-self-check", + "mesh-watchdogs" + ] +} diff --git a/health-and-repair/ts/seats/meshconditions/package.json b/health-and-repair/ts/seats/meshconditions/package.json new file mode 100644 index 0000000..f02f3b7 --- /dev/null +++ b/health-and-repair/ts/seats/meshconditions/package.json @@ -0,0 +1,19 @@ +{ + "description": "The contract of the seat mesh-conditions, generated from its definition.", + "devDependencies": { + "typescript": "^5.6.0" + }, + "exports": { + ".": "./dist/index.js" + }, + "files": [ + "dist" + ], + "name": "@novox/mesh-health-and-repair-contracts-mesh-conditions", + "scripts": { + "build": "tsc", + "prepare": "tsc" + }, + "type": "module", + "version": "0.1.0" +} diff --git a/health-and-repair/ts/seats/meshconditions/src/index.ts b/health-and-repair/ts/seats/meshconditions/src/index.ts new file mode 100644 index 0000000..2371334 --- /dev/null +++ b/health-and-repair/ts/seats/meshconditions/src/index.ts @@ -0,0 +1,280 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// The contract of the seat "mesh-conditions", defined whole (seats/mesh-conditions/seat.json): What is wrong with the mesh now (ADR 0564): the open conditions raised from the observers' findings and cleared when a finding goes. + +export const seat = "mesh-conditions"; + +export const verbs = { + conditions: "conditions", + silence: "silence", + resync: "resync", +} as const; + +export const events = { + conditionRaised: "condition-raised", + conditionChanged: "condition-changed", + conditionCleared: "condition-cleared", +} as const; + +export const family = { name: "mesh-conditions.events", maxAgeDays: 7 } as const; + +export const conditionRaisedSubject = "mesh.seat.mesh-conditions.event.condition-raised"; + +export const conditionChangedSubject = "mesh.seat.mesh-conditions.event.condition-changed"; + +export const conditionClearedSubject = "mesh.seat.mesh-conditions.event.condition-cleared"; + +export const states = { + conditions: "conditions", + findings: "findings", +} as const; + +/** The key of one value of the state conditions under the form "". */ +export function conditionsByKeyKey(key: string): string { + return key; +} + +/** The parts of a key of the form "", or undefined for a key of another form. */ +export function parseConditionsByKeyKey(key: string): { key: string } | undefined { + let rest = key; + if (rest === "") return undefined; + const key = rest; + rest = ""; + if (rest !== "") return undefined; + return { key }; +} + +/** The key of one value of the state findings under the form "". */ +export function findingsByKeyKey(key: string): string { + return key; +} + +/** The parts of a key of the form "", or undefined for a key of another form. */ +export function parseFindingsByKeyKey(key: string): { key: string } | undefined { + let rest = key; + if (rest === "") return undefined; + const key = rest; + rest = ""; + if (rest !== "") return undefined; + return { key }; +} + +/** one answer the operator may give from a notification */ +export interface Action { + "arguments"?: Record; + "label": string; + "level"?: "acknowledge" | "approve"; + "machine"?: string; + /** the seat verb the operator channel calls */ + "verb": string; +} + +export interface ConditionEventGapsItem { + "cleared"?: string; + "reopened"?: string; +} + +export interface ConditionEventSilenced { + "by"?: string; + "since"?: string; + "until"?: string; + "why"?: string; +} + +export interface ConditionEventTriedItem { + "at"?: string; + "by"?: string; + "outcome"?: string; + "what"?: string; +} + +/** a condition and what changed */ +export interface ConditionEvent { + "actions"?: Action[]; + "at": string; + "change": "raised" | "reopened" | "severity" | "resolver" | "silenced" | "silence-ended" | "cleared" | "words"; + "cleared"?: string; + "count"?: number; + "epoch"?: number; + "event": string; + /** newest first, at most 10 */ + "evidence"?: Evidence[]; + "explanation"?: string; + "first"?: string; + "gaps"?: ConditionEventGapsItem[]; + "headline"?: string; + /** scope.id.token */ + "key": string; + "kind"?: string; + "last-observed"?: string; + "looks-unread"?: string; + "needs"?: string; + "observations"?: number; + "raised"?: string; + "resolved"?: string; + /** self, operator, agent or healer: */ + "resolver"?: string; + "severity"?: "urgent" | "warning"; + /** the tool call that shows it */ + "show"?: string; + "silenced"?: ConditionEventSilenced; + /** the row, probe or event that saw it */ + "source"?: string; + "subject"?: Subject; + /** one line */ + "summary"?: string; + "tried"?: ConditionEventTriedItem[]; + "was"?: string; + "why"?: string; +} + +/** one observation, as it was said */ +export interface Evidence { + "at": string; + "said": string; +} + +/** what the condition is about */ +export interface Subject { + /** the other machines it concerns */ + "also"?: string[]; + /** its id within the scope */ + "id": string; + /** the machine it concerns, when there is one */ + "machine"?: string; + /** machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module */ + "scope": string; +} + +/** is what conditions takes. */ +export interface ConditionsInput { + /** with history: how many days back (default 7, at most 90) */ + "days"?: string; + /** "true": every raising, change, silence and clearing lately, oldest first */ + "history"?: "true" | "false"; + /** a condition's key: that one whole; with history, only its transitions */ + "key"?: string; + /** only those about this machine */ + "machine"?: string; + /** only this scope: machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module */ + "scope"?: string; + /** only urgent, or only warning */ + "severity"?: string; +} + +/** is what conditions answers. */ +export type ConditionsAnswer = Record; + +/** is what silence takes. */ +export interface SilenceInput { + /** the cause in a word (the condition's kind when absent) */ + "cause"?: string; + /** how long: 30m, 4h, 2d; at most 7d */ + "for": string; + /** the condition's key */ + "key": string; + /** why, required and recorded */ + "why": string; +} + +/** is what silence answers. */ +export type SilenceAnswer = Record; + +/** is what resync takes. */ +export interface ResyncInput { +} + +/** is what resync answers. */ +export type ResyncAnswer = Record; + +/** is the body of the event condition-raised. */ +export type ConditionRaisedEvent = ConditionEvent; + +/** is the body of the event condition-changed. */ +export type ConditionChangedEvent = ConditionEvent; + +/** is the body of the event condition-cleared. */ +export type ConditionClearedEvent = ConditionEvent; + +export interface ConditionsByKeyValueGapsItem { + "cleared"?: string; + "reopened"?: string; +} + +export interface ConditionsByKeyValueSilenced { + "by"?: string; + "since"?: string; + "until"?: string; + "why"?: string; +} + +export interface ConditionsByKeyValueTriedItem { + "at"?: string; + "by"?: string; + "outcome"?: string; + "what"?: string; +} + +/** is one value of the state conditions under the key . */ +export interface ConditionsByKeyValue { + "actions"?: Action[]; + "count"?: number; + "epoch"?: number; + /** newest first, at most 10 */ + "evidence"?: Evidence[]; + "explanation"?: string; + "first"?: string; + "gaps"?: ConditionsByKeyValueGapsItem[]; + "headline"?: string; + /** scope.id.token */ + "key": string; + "kind": string; + "last-observed": string; + "looks-unread"?: string; + "needs"?: string; + "observations"?: number; + "raised": string; + "resolved"?: string; + /** self, operator, agent or healer: */ + "resolver"?: string; + "severity": "urgent" | "warning"; + "silenced"?: ConditionsByKeyValueSilenced; + /** the row, probe or event that saw it */ + "source"?: string; + "subject": Subject; + /** one line */ + "summary"?: string; + "tried"?: ConditionsByKeyValueTriedItem[]; +} + +/** is one value of the state findings under the key . */ +export interface FindingsByKeyValue { + "actions"?: Action[]; + "at": string; + /** newest first, at most 10 */ + "evidence"?: Evidence[]; + "explanation"?: string; + "headline"?: string; + /** scope.id.token */ + "key": string; + "kind": string; + "needs"?: string; + /** the module that wrote it */ + "observer": string; + /** the key's last part when it is not the kind */ + "ref"?: string; + "resolved"?: string; + /** self, operator, agent or healer: */ + "resolver"?: string; + /** what of its own saw it last */ + "run"?: string; + "severity": "urgent" | "warning"; + /** the row, probe or event that saw it */ + "source": string; + "subject": Subject; + /** one line */ + "summary": string; + /** layout version, 1 */ + "v": number; +} + diff --git a/ts/seats/operatorchannel/tsconfig.json b/health-and-repair/ts/seats/meshconditions/tsconfig.json similarity index 100% rename from ts/seats/operatorchannel/tsconfig.json rename to health-and-repair/ts/seats/meshconditions/tsconfig.json diff --git a/health-and-repair/ts/seats/meshhandacts/package.json b/health-and-repair/ts/seats/meshhandacts/package.json new file mode 100644 index 0000000..c5da858 --- /dev/null +++ b/health-and-repair/ts/seats/meshhandacts/package.json @@ -0,0 +1,19 @@ +{ + "description": "The contract of the seat mesh-hand-acts, generated from its definition.", + "devDependencies": { + "typescript": "^5.6.0" + }, + "exports": { + ".": "./dist/index.js" + }, + "files": [ + "dist" + ], + "name": "@novox/mesh-health-and-repair-contracts-mesh-hand-acts", + "scripts": { + "build": "tsc", + "prepare": "tsc" + }, + "type": "module", + "version": "0.1.0" +} diff --git a/health-and-repair/ts/seats/meshhandacts/src/index.ts b/health-and-repair/ts/seats/meshhandacts/src/index.ts new file mode 100644 index 0000000..8568bb0 --- /dev/null +++ b/health-and-repair/ts/seats/meshhandacts/src/index.ts @@ -0,0 +1,160 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// The contract of the seat "mesh-hand-acts", defined whole (seats/mesh-hand-acts/seat.json): Acts done by hand, recorded with who, why and cause (ADR 0565): the log every module records through. + +export const seat = "mesh-hand-acts"; + +export const verbs = { + handAct: "hand-act", + handActs: "hand-acts", + warranted: "warranted", +} as const; + +export const events = { + actRecorded: "act-recorded", +} as const; + +export const family = { name: "mesh-hand-acts.events", maxAgeDays: 7 } as const; + +export const actRecordedSubject = "mesh.seat.mesh-hand-acts.event.act-recorded"; + +export const states = { + acts: "acts", + warrants: "warrants", +} as const; + +/** The key of one value of the state acts under the form "". */ +export function actsByIDKey(id: string): string { + return id; +} + +/** The parts of a key of the form "", or undefined for a key of another form. */ +export function parseActsByIDKey(key: string): { id: string } | undefined { + let rest = key; + if (rest === "") return undefined; + const id = rest; + rest = ""; + if (rest !== "") return undefined; + return { id }; +} + +/** The key of one value of the state warrants under the form ".". */ +export function warrantsByAskerAskKey(asker: string, ask: string): string { + return asker + "." + ask; +} + +/** The parts of a key of the form ".", or undefined for a key of another form. */ +export function parseWarrantsByAskerAskKey(key: string): { asker: string; ask: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const asker = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + if (rest === "") return undefined; + const ask = rest; + rest = ""; + if (rest !== "") return undefined; + return { asker, ask }; +} + +/** one act done by hand */ +export interface Act { + "arguments"?: string[]; + "ask"?: string; + "at": string; + /** the caller the runtime proved */ + "by": string; + "carried"?: string[]; + "cause": string; + "condition"?: string; + "id": string; + /** what the act was read to be from what it did */ + "kind"?: string; + /** which log holds it, set when it is read */ + "log"?: string; + "outcome"?: string; + "proofs"?: string[]; + "requested-by"?: string; + "verb": string; + "via"?: string; + "why": string; +} + +/** is what hand-act takes. */ +export interface HandActInput { + /** the cause in a word, or a condition's kind */ + "cause"?: string; + /** the key of the condition it addressed, if any (optional) */ + "condition"?: string; + /** the act's verb in the log: a repair when absent, `hand-act drill` for a drill, or a recorder's own */ + "verb"?: string; + /** what was done, in a line */ + "what": string; + /** why it had to be done by hand */ + "why": string; +} + +/** is what hand-act answers. */ +export type HandActAnswer = Record; + +/** is what hand-acts takes. */ +export interface HandActsInput { + /** only this cause */ + "cause"?: string; + /** how many days back (default 14) */ + "days"?: string; + /** "true": every warrant recorded */ + "warrants"?: "true" | "false"; + /** only acts whose who contains this */ + "who"?: string; +} + +/** is what hand-acts answers. */ +export type HandActsAnswer = Record; + +/** is what warranted takes. */ +export interface WarrantedInput { + /** its ask's id */ + "ask": string; + /** the module that asked */ + "asker": string; +} + +/** is what warranted answers. */ +export type WarrantedAnswer = Record; + +/** is the body of the event act-recorded. */ +export type ActRecordedEvent = Act; + +/** is one value of the state acts under the key . */ +export interface ActsByIDValue { + "arguments"?: string[]; + "ask"?: string; + "at": string; + /** the caller the runtime proved */ + "by": string; + "carried"?: string[]; + "cause": string; + "condition"?: string; + "id": string; + /** what the act was read to be from what it did */ + "kind"?: string; + /** which log holds it, set when it is read */ + "log"?: string; + "outcome"?: string; + "proofs"?: string[]; + "requested-by"?: string; + "verb": string; + "via"?: string; + "why": string; +} + +/** is one value of the state warrants under the key .. */ +export interface WarrantsByAskerAskValue { + "ask": string; + "asker": string; + "at"?: string; + "by"?: string; + "option"?: string; +} + diff --git a/ts/tsconfig.json b/health-and-repair/ts/seats/meshhandacts/tsconfig.json similarity index 100% rename from ts/tsconfig.json rename to health-and-repair/ts/seats/meshhandacts/tsconfig.json diff --git a/ts/package.json b/health-and-repair/ts/seats/meshhealers/package.json similarity index 60% rename from ts/package.json rename to health-and-repair/ts/seats/meshhealers/package.json index cd5565a..6ffcf90 100644 --- a/ts/package.json +++ b/health-and-repair/ts/seats/meshhealers/package.json @@ -1,5 +1,5 @@ { - "description": "The mesh's seat set, generated from every seat's definition.", + "description": "The contract of the seat mesh-healers, generated from its definition.", "devDependencies": { "typescript": "^5.6.0" }, @@ -9,7 +9,7 @@ "files": [ "dist" ], - "name": "@novox/mesh-contracts", + "name": "@novox/mesh-health-and-repair-contracts-mesh-healers", "scripts": { "build": "tsc", "prepare": "tsc" diff --git a/health-and-repair/ts/seats/meshhealers/src/index.ts b/health-and-repair/ts/seats/meshhealers/src/index.ts new file mode 100644 index 0000000..3e0c695 --- /dev/null +++ b/health-and-repair/ts/seats/meshhealers/src/index.ts @@ -0,0 +1,127 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// The contract of the seat "mesh-healers", defined whole (seats/mesh-healers/seat.json): The healers (ADR 0564): each a registered response to one kind of condition, within a budget and the mesh-wide brake. + +export const seat = "mesh-healers"; + +export const verbs = { + healers: "healers", + heal: "heal", + drill: "drill", +} as const; + +export const events = { + healerActed: "healer-acted", +} as const; + +export const family = { name: "mesh-healers.events", maxAgeDays: 7 } as const; + +export const healerActedSubject = "mesh.seat.mesh-healers.event.healer-acted"; + +export const states = { + attempts: "attempts", + would: "would", +} as const; + +/** The key of one value of the state attempts under the form "". */ +export function attemptsByAttemptKey(attempt: string): string { + return attempt; +} + +/** The parts of a key of the form "", or undefined for a key of another form. */ +export function parseAttemptsByAttemptKey(key: string): { attempt: string } | undefined { + let rest = key; + if (rest === "") return undefined; + const attempt = rest; + rest = ""; + if (rest !== "") return undefined; + return { attempt }; +} + +/** The key of one value of the state would under the form "". */ +export function wouldByWouldKey(would: string): string { + return would; +} + +/** The parts of a key of the form "", or undefined for a key of another form. */ +export function parseWouldByWouldKey(key: string): { would: string } | undefined { + let rest = key; + if (rest === "") return undefined; + const would = rest; + rest = ""; + if (rest !== "") return undefined; + return { would }; +} + +/** is what healers takes. */ +export interface HealersInput { + /** how many days of acts back (default 7) */ + "days"?: string; +} + +/** is what healers answers. */ +export type HealersAnswer = Record; + +/** is what heal takes. */ +export interface HealInput { + /** the condition's key */ + "key": string; + /** why now, kept with the act */ + "why": string; +} + +/** is what heal answers. */ +export type HealAnswer = Record; + +/** is what drill takes. */ +export interface DrillInput { + /** the key of the condition the drill is meant to raise, if any (optional) */ + "condition"?: string; + /** what was done on purpose, in a line */ + "what": string; + /** what the drill tests */ + "why": string; +} + +/** is what drill answers. */ +export type DrillAnswer = Record; + +/** is the body of the event healer-acted. */ +export interface HealerActedEvent { + "act"?: string; + "at": string; + "budget"?: string; + "by"?: string; + "condition": string; + "event": string; + "healer": string; + "kind"?: string; + "outcome": "acting" | "acted" | "failed" | "escalated"; + "said"?: string; + "show"?: string; +} + +/** is one value of the state attempts under the key . */ +export interface AttemptsByAttemptValue { + "act": string; + "begun": string; + /** what the budget counts by: the condition's key unless the healer says another */ + "budget-key": string; + "condition": string; + "finished"?: string; + "healer": string; + "kind": string; + "outcome": "acting" | "acted" | "failed" | "escalated"; + "said"?: string; +} + +/** is one value of the state would under the key . */ +export interface WouldByWouldValue { + "act": string; + "at": string; + "condition": string; + "healer": string; + "kind"?: string; + "why"?: string; +} + diff --git a/health-and-repair/ts/seats/meshhealers/tsconfig.json b/health-and-repair/ts/seats/meshhealers/tsconfig.json new file mode 100644 index 0000000..c610421 --- /dev/null +++ b/health-and-repair/ts/seats/meshhealers/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*.ts"] +} diff --git a/health-and-repair/ts/seats/meshselfcheck/package.json b/health-and-repair/ts/seats/meshselfcheck/package.json new file mode 100644 index 0000000..9cd3661 --- /dev/null +++ b/health-and-repair/ts/seats/meshselfcheck/package.json @@ -0,0 +1,19 @@ +{ + "description": "The contract of the seat mesh-self-check, generated from its definition.", + "devDependencies": { + "typescript": "^5.6.0" + }, + "exports": { + ".": "./dist/index.js" + }, + "files": [ + "dist" + ], + "name": "@novox/mesh-health-and-repair-contracts-mesh-self-check", + "scripts": { + "build": "tsc", + "prepare": "tsc" + }, + "type": "module", + "version": "0.1.0" +} diff --git a/health-and-repair/ts/seats/meshselfcheck/src/index.ts b/health-and-repair/ts/seats/meshselfcheck/src/index.ts new file mode 100644 index 0000000..b76fc1d --- /dev/null +++ b/health-and-repair/ts/seats/meshselfcheck/src/index.ts @@ -0,0 +1,70 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// The contract of the seat "mesh-self-check", defined whole (seats/mesh-self-check/seat.json): The mesh checking itself (ADR 0564): probes run every five minutes, each failure a finding the seat's conditions raise. + +export const seat = "mesh-self-check"; + +export const verbs = { + doctor: "doctor", + probes: "probes", +} as const; + +export const events = { + checked: "checked", +} as const; + +export const family = { name: "mesh-self-check.events", maxAgeDays: 7 } as const; + +export const checkedSubject = "mesh.seat.mesh-self-check.event.checked"; + +/** one probe's verdict */ +export interface ProbeVerdict { + "error"?: string; + "found"?: string[]; + "found-count"?: number; + "id": string; + "more"?: string; + "took"?: string; + "unconfirmed"?: string[]; + "verdict": "passed" | "failed" | "failed-to-run" | "deferred"; +} + +/** is what doctor takes. */ +export interface DoctorInput { + /** one probe's id (D6): its verdict alone, with every finding */ + "probe"?: string; + /** "true": run every probe now and answer the verdict */ + "run"?: "true" | "false"; +} + +/** is what doctor answers. */ +export type DoctorAnswer = Record; + +/** is what probes takes. */ +export interface ProbesInput { +} + +/** is what probes answers. */ +export type ProbesAnswer = Record; + +export interface CheckedEventCounts { + "deferred"?: number; + "failed"?: number; + "failed-to-run"?: number; + "passed"?: number; +} + +/** is the body of the event checked. */ +export interface CheckedEvent { + "at": string; + "controller"?: string; + "counts"?: CheckedEventCounts; + "interval-seconds"?: number; + "probes": ProbeVerdict[]; + "run": string; + "started"?: string; + "took"?: string; + "unsaid"?: number; + "why"?: string; +} + diff --git a/health-and-repair/ts/seats/meshselfcheck/tsconfig.json b/health-and-repair/ts/seats/meshselfcheck/tsconfig.json new file mode 100644 index 0000000..c610421 --- /dev/null +++ b/health-and-repair/ts/seats/meshselfcheck/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*.ts"] +} diff --git a/health-and-repair/ts/seats/meshwatchdogs/package.json b/health-and-repair/ts/seats/meshwatchdogs/package.json new file mode 100644 index 0000000..f6d5493 --- /dev/null +++ b/health-and-repair/ts/seats/meshwatchdogs/package.json @@ -0,0 +1,19 @@ +{ + "description": "The contract of the seat mesh-watchdogs, generated from its definition.", + "devDependencies": { + "typescript": "^5.6.0" + }, + "exports": { + ".": "./dist/index.js" + }, + "files": [ + "dist" + ], + "name": "@novox/mesh-health-and-repair-contracts-mesh-watchdogs", + "scripts": { + "build": "tsc", + "prepare": "tsc" + }, + "type": "module", + "version": "0.1.0" +} diff --git a/health-and-repair/ts/seats/meshwatchdogs/src/index.ts b/health-and-repair/ts/seats/meshwatchdogs/src/index.ts new file mode 100644 index 0000000..ed74cd9 --- /dev/null +++ b/health-and-repair/ts/seats/meshwatchdogs/src/index.ts @@ -0,0 +1,182 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// The contract of the seat "mesh-watchdogs", defined whole (seats/mesh-watchdogs/seat.json): The watchman's seat (ADR 0564, ADR 0433): signals that expire unless renewed, what stopped moving, who is present, the dead letters. + +export const seat = "mesh-watchdogs"; + +export const verbs = { + signals: "signals", + stuck: "stuck", + presence: "presence", + deadLetters: "dead-letters", + resync: "resync", +} as const; + +export const states = { + presence: "presence", + pulse: "pulse", + stuck: "stuck", +} as const; + +/** The key of one value of the state presence under the form "". */ +export function presenceByNodeKey(node: string): string { + return node; +} + +/** The parts of a key of the form "", or undefined for a key of another form. */ +export function parsePresenceByNodeKey(key: string): { node: string } | undefined { + let rest = key; + if (rest === "") return undefined; + const node = rest; + rest = ""; + if (rest !== "") return undefined; + return { node }; +} + +/** The key of one value of the state presence under the form "part..". */ +export function presenceByPartPartInstanceKey(part: string, instance: string): string { + return "part." + part + "." + instance; +} + +/** The parts of a key of the form "part..", or undefined for a key of another form. */ +export function parsePresenceByPartPartInstanceKey(key: string): { part: string; instance: string } | undefined { + let rest = key; + if (!rest.startsWith("part.")) return undefined; + rest = rest.slice(5); + const at1 = rest.indexOf("."); + if (at1 <= 0) return undefined; + const part = rest.slice(0, at1); + rest = rest.slice(at1 + 1); + if (rest === "") return undefined; + const instance = rest; + rest = ""; + if (rest !== "") return undefined; + return { part, instance }; +} + +/** The key of one value of the state pulse under the form ".". */ +export function pulseByBucketKeyKey(bucket: string, key: string): string { + return bucket + "." + key; +} + +/** The parts of a key of the form ".", or undefined for a key of another form. */ +export function parsePulseByBucketKeyKey(key: string): { bucket: string; key: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const bucket = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + if (rest === "") return undefined; + const key = rest; + rest = ""; + if (rest !== "") return undefined; + return { bucket, key }; +} + +/** The key of one value of the state stuck under the form ".". */ +export function stuckByBucketKeyKey(bucket: string, key: string): string { + return bucket + "." + key; +} + +/** The parts of a key of the form ".", or undefined for a key of another form. */ +export function parseStuckByBucketKeyKey(key: string): { bucket: string; key: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const bucket = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + if (rest === "") return undefined; + const key = rest; + rest = ""; + if (rest !== "") return undefined; + return { bucket, key }; +} + +/** is what signals takes. */ +export interface SignalsInput { + /** one row, e.g. S1 */ + "row"?: string; +} + +/** is what signals answers. */ +export type SignalsAnswer = Record; + +/** is what stuck takes. */ +export interface StuckInput { + /** only those on this machine */ + "machine"?: string; +} + +/** is what stuck answers. */ +export type StuckAnswer = Record; + +/** is what presence takes. */ +export interface PresenceInput { + /** one machine */ + "machine"?: string; +} + +/** is what presence answers. */ +export type PresenceAnswer = Record; + +/** is what dead-letters takes. */ +export interface DeadLettersInput { + /** with deliver or drop: the cause in a word (dead-letter when absent) */ + "cause"?: string; + /** a consumer's name, or .: only what that one gave up on */ + "consumer"?: string; + /** a dead letter's id: deliver it again to the consumer that gave it up (needs why) */ + "deliver"?: string; + /** a dead letter's id: drop it for good (needs why) */ + "drop"?: string; + /** a dead letter's id, as the list gives it: that one whole, with its message */ + "id"?: string; + /** how many to list, newest first (default 50); only when listing */ + "limit"?: string; + /** with deliver or drop: why, required and recorded */ + "why"?: string; +} + +/** is what dead-letters answers. */ +export type DeadLettersAnswer = Record; + +/** is what resync takes. */ +export interface ResyncInput { +} + +/** is what resync answers. */ +export type ResyncAnswer = Record; + +/** is one value of the state presence under the key . */ +export interface PresenceByNodeValue { + "online": boolean; + "v"?: number; +} + +/** is one value of the state presence under the key part... */ +export interface PresenceByPartPartInstanceValue { + "online": boolean; + "v"?: number; +} + +/** is one value of the state pulse under the key .. */ +export interface PulseByBucketKeyValue { + "bucket": string; + "key": string; + "owes"?: string; + "revision": number; + "v"?: number; +} + +/** is one value of the state stuck under the key .. */ +export interface StuckByBucketKeyValue { + "bucket": string; + "key": string; + /** it has moved again */ + "moved"?: boolean; + "since": string; + "v"?: number; + /** what it waited for, in words */ + "waited"?: string; +} + diff --git a/health-and-repair/ts/seats/meshwatchdogs/tsconfig.json b/health-and-repair/ts/seats/meshwatchdogs/tsconfig.json new file mode 100644 index 0000000..c610421 --- /dev/null +++ b/health-and-repair/ts/seats/meshwatchdogs/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*.ts"] +} diff --git a/go/clients/operatorchannelclient/ask.go b/operator-and-conversation/go/clients/operatorchannelclient/ask.go similarity index 99% rename from go/clients/operatorchannelclient/ask.go rename to operator-and-conversation/go/clients/operatorchannelclient/ask.go index ee93987..eb5a5ed 100644 --- a/go/clients/operatorchannelclient/ask.go +++ b/operator-and-conversation/go/clients/operatorchannelclient/ask.go @@ -18,7 +18,7 @@ import ( "strings" "time" - seat "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel" + seat "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" ) // The levels an option is answered at: how much proof its answer needs (novox/hq ADR 0234 §8, the glossary's diff --git a/go/clients/operatorchannelclient/ask_test.go b/operator-and-conversation/go/clients/operatorchannelclient/ask_test.go similarity index 99% rename from go/clients/operatorchannelclient/ask_test.go rename to operator-and-conversation/go/clients/operatorchannelclient/ask_test.go index 7166bbe..9d32152 100644 --- a/go/clients/operatorchannelclient/ask_test.go +++ b/operator-and-conversation/go/clients/operatorchannelclient/ask_test.go @@ -9,7 +9,7 @@ import ( "testing" "time" - seat "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel" + seat "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" ) var now = time.Date(2026, 10, 8, 14, 0, 0, 0, time.UTC) diff --git a/go/clients/operatorchannelclient/client.go b/operator-and-conversation/go/clients/operatorchannelclient/client.go similarity index 98% rename from go/clients/operatorchannelclient/client.go rename to operator-and-conversation/go/clients/operatorchannelclient/client.go index f67025a..b809fe1 100644 --- a/go/clients/operatorchannelclient/client.go +++ b/operator-and-conversation/go/clients/operatorchannelclient/client.go @@ -17,7 +17,7 @@ import ( "strings" "time" - seat "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel" + seat "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" stdio "git.novox.be/novox/mesh-sdk/go" ) diff --git a/go/clients/operatorchannelclient/client_test.go b/operator-and-conversation/go/clients/operatorchannelclient/client_test.go similarity index 98% rename from go/clients/operatorchannelclient/client_test.go rename to operator-and-conversation/go/clients/operatorchannelclient/client_test.go index 3739b99..45c2b0a 100644 --- a/go/clients/operatorchannelclient/client_test.go +++ b/operator-and-conversation/go/clients/operatorchannelclient/client_test.go @@ -6,7 +6,7 @@ import ( "testing" "time" - seat "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel" + seat "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" stdio "git.novox.be/novox/mesh-sdk/go" "git.novox.be/novox/mesh-sdk/go/process" ) diff --git a/go/clients/operatorchannelclient/condition.go b/operator-and-conversation/go/clients/operatorchannelclient/condition.go similarity index 97% rename from go/clients/operatorchannelclient/condition.go rename to operator-and-conversation/go/clients/operatorchannelclient/condition.go index af9485b..0fa5c5a 100644 --- a/go/clients/operatorchannelclient/condition.go +++ b/operator-and-conversation/go/clients/operatorchannelclient/condition.go @@ -7,7 +7,7 @@ import ( "reflect" "strings" - seat "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel" + seat "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" ) // ConditionSubject is where a module says a condition to the operator: the seat's by-caller condition accept diff --git a/go/clients/operatorchannelclient/condition_test.go b/operator-and-conversation/go/clients/operatorchannelclient/condition_test.go similarity index 98% rename from go/clients/operatorchannelclient/condition_test.go rename to operator-and-conversation/go/clients/operatorchannelclient/condition_test.go index c9f32d1..2450b24 100644 --- a/go/clients/operatorchannelclient/condition_test.go +++ b/operator-and-conversation/go/clients/operatorchannelclient/condition_test.go @@ -6,7 +6,7 @@ import ( "strings" "testing" - seat "git.novox.be/novox/mesh-contracts/go/seats/operatorchannel" + seat "git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel" ) func TestConditionSubject(t *testing.T) { // 1202/AC1 diff --git a/go/clients/operatorchannelclient/go.mod b/operator-and-conversation/go/clients/operatorchannelclient/go.mod similarity index 59% rename from go/clients/operatorchannelclient/go.mod rename to operator-and-conversation/go/clients/operatorchannelclient/go.mod index 375590f..dedb474 100644 --- a/go/clients/operatorchannelclient/go.mod +++ b/operator-and-conversation/go/clients/operatorchannelclient/go.mod @@ -1,14 +1,13 @@ -module git.novox.be/novox/mesh-contracts/go/clients/operatorchannelclient +module git.novox.be/novox/mesh-contracts/operator-and-conversation/go/clients/operatorchannelclient go 1.26.0 require ( - git.novox.be/novox/mesh-contracts/go/seats/operatorchannel v0.0.0-20261104172218-347a3053580a + git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel v0.0.0-20261104172218-347a3053580a git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261104174118-f94abb95aaef ) require ( - git.novox.be/novox/mesh-contracts/go v0.0.0-20261104172218-347a3053580a // indirect github.com/klauspost/compress v1.20.1 // indirect github.com/nats-io/nats.go v1.54.0 // indirect github.com/nats-io/nkeys v0.4.16 // indirect diff --git a/go/clients/operatorchannelclient/go.sum b/operator-and-conversation/go/clients/operatorchannelclient/go.sum similarity index 100% rename from go/clients/operatorchannelclient/go.sum rename to operator-and-conversation/go/clients/operatorchannelclient/go.sum diff --git a/go/seats/operatorchannel/contract.go b/operator-and-conversation/go/seats/operatorchannel/contract.go similarity index 87% rename from go/seats/operatorchannel/contract.go rename to operator-and-conversation/go/seats/operatorchannel/contract.go index bb087ee..540d371 100644 --- a/go/seats/operatorchannel/contract.go +++ b/operator-and-conversation/go/seats/operatorchannel/contract.go @@ -3,6 +3,13 @@ // Package operatorchannel is the contract of the seat "operator-channel", defined whole (seats/operator-channel/seat.json): Asking a person and answering on a channel (ADR 0259): held by the router, the messenger module, whose manifest declares the seat; written from that seat entry (design 26). package operatorchannel +import ( + "bytes" + "encoding/json" + "errors" + "strings" +) + // Seat is the seat's name. const Seat = "operator-channel" @@ -75,9 +82,45 @@ func HolderSubjectOffered(holder string) string { return "mesh.stream." + holder func HolderSubjectClosed(holder string) string { return "mesh.stream." + holder + ".event.closed" } const ( - RecordAsks = "asks" + StateAsks = "asks" ) +// AsksByAskerIDKey is the key of one value of the state asks under the form ".". +func AsksByAskerIDKey(asker, id string) string { return asker + "." + id } + +// ParseAsksByAskerIDKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. +func ParseAsksByAskerIDKey(key string) (string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", false + } + rest = after0 + if rest == "" { + return "", "", false + } + v2 := rest + rest = "" + if rest != "" { + return "", "", false + } + return v0, v2, true +} + +// DecodeAsksByAskerIDValue reads one value of the state asks under the form ".", refusing a field AsksByAskerIDValue does not know and any text after the value. +func DecodeAsksByAskerIDValue(raw []byte) (AsksByAskerIDValue, error) { + var v AsksByAskerIDValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + // Ask is a type of the contract. type Ask struct { // what it is about: a newer ask about it replaces the older @@ -328,5 +371,5 @@ const ( ClosedHolderEventHowUnlinked = "unlinked" ) -// AsksRecord is one value of the record asks. -type AsksRecord = map[string]any +// AsksByAskerIDValue is one value of the state asks under the key .. +type AsksByAskerIDValue = map[string]any diff --git a/go/seats/operatorchannel/contract_test.go b/operator-and-conversation/go/seats/operatorchannel/contract_test.go similarity index 100% rename from go/seats/operatorchannel/contract_test.go rename to operator-and-conversation/go/seats/operatorchannel/contract_test.go diff --git a/operator-and-conversation/go/seats/operatorchannel/go.mod b/operator-and-conversation/go/seats/operatorchannel/go.mod new file mode 100644 index 0000000..691961a --- /dev/null +++ b/operator-and-conversation/go/seats/operatorchannel/go.mod @@ -0,0 +1,3 @@ +module git.novox.be/novox/mesh-contracts/operator-and-conversation/go/seats/operatorchannel + +go 1.26.0 diff --git a/operator-and-conversation/seats/index.json b/operator-and-conversation/seats/index.json new file mode 100644 index 0000000..62a0f68 --- /dev/null +++ b/operator-and-conversation/seats/index.json @@ -0,0 +1,6 @@ +{ + "format": 1, + "seats": [ + "operator-channel" + ] +} diff --git a/ts/seats/operatorchannel/package.json b/operator-and-conversation/ts/seats/operatorchannel/package.json similarity index 80% rename from ts/seats/operatorchannel/package.json rename to operator-and-conversation/ts/seats/operatorchannel/package.json index e1d9bcb..e2143b1 100644 --- a/ts/seats/operatorchannel/package.json +++ b/operator-and-conversation/ts/seats/operatorchannel/package.json @@ -9,7 +9,7 @@ "files": [ "dist" ], - "name": "@novox/mesh-contracts-operator-channel", + "name": "@novox/mesh-operator-and-conversation-contracts-operator-channel", "scripts": { "build": "tsc", "prepare": "tsc" diff --git a/ts/seats/operatorchannel/src/index.ts b/operator-and-conversation/ts/seats/operatorchannel/src/index.ts similarity index 89% rename from ts/seats/operatorchannel/src/index.ts rename to operator-and-conversation/ts/seats/operatorchannel/src/index.ts index 16e5cb0..4b9e593 100644 --- a/ts/seats/operatorchannel/src/index.ts +++ b/operator-and-conversation/ts/seats/operatorchannel/src/index.ts @@ -58,6 +58,29 @@ export function closedHolderSubject(holder: string): string { return "mesh.stream." + holder + ".event.closed"; } +export const states = { + asks: "asks", +} as const; + +/** The key of one value of the state asks under the form ".". */ +export function asksByAskerIDKey(asker: string, id: string): string { + return asker + "." + id; +} + +/** The parts of a key of the form ".", or undefined for a key of another form. */ +export function parseAsksByAskerIDKey(key: string): { asker: string; id: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const asker = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + if (rest === "") return undefined; + const id = rest; + rest = ""; + if (rest !== "") return undefined; + return { asker, id }; +} + export interface Ask { /** what it is about: a newer ask about it replaces the older */ "about"?: string; @@ -277,6 +300,6 @@ export interface ClosedHolderEvent { "id": string; } -/** is one value of the record asks. */ -export type AsksRecord = Record; +/** is one value of the state asks under the key .. */ +export type AsksByAskerIDValue = Record; diff --git a/operator-and-conversation/ts/seats/operatorchannel/tsconfig.json b/operator-and-conversation/ts/seats/operatorchannel/tsconfig.json new file mode 100644 index 0000000..c610421 --- /dev/null +++ b/operator-and-conversation/ts/seats/operatorchannel/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*.ts"] +} diff --git a/provisioning/go/seats/meshprovisioner/contract.go b/provisioning/go/seats/meshprovisioner/contract.go new file mode 100644 index 0000000..1bacedd --- /dev/null +++ b/provisioning/go/seats/meshprovisioner/contract.go @@ -0,0 +1,285 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// Package meshprovisioner is the contract of the seat "mesh-provisioner", defined whole (seats/mesh-provisioner/seat.json): The provisioner (ADR 0349): which domain answers which kind of placeholder, what each module needs, what is applied where. +package meshprovisioner + +import ( + "bytes" + "encoding/json" + "errors" + "strings" +) + +// Seat is the seat's name. +const Seat = "mesh-provisioner" + +const ( + VerbProvisioning = "provisioning" +) + +// Verbs is every verb the seat serves, in its definition's order: a new copy each call. +func Verbs() []string { + return []string{VerbProvisioning} +} + +const ( + StatePlans = "plans" + StateProvisions = "provisions" +) + +// PlansByNodeModuleKey is the key of one value of the state plans under the form ".". +func PlansByNodeModuleKey(node, module string) string { return node + "." + module } + +// ParsePlansByNodeModuleKey reads the parts of a key of the form ".", in the order of the form; the last result is false for a key of another form. +func ParsePlansByNodeModuleKey(key string) (string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", false + } + rest = after0 + if rest == "" { + return "", "", false + } + v2 := rest + rest = "" + if rest != "" { + return "", "", false + } + return v0, v2, true +} + +// DecodePlansByNodeModuleValue reads one value of the state plans under the form ".", refusing a field PlansByNodeModuleValue does not know and any text after the value. +func DecodePlansByNodeModuleValue(raw []byte) (PlansByNodeModuleValue, error) { + var v PlansByNodeModuleValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// ProvisionsByNodeConsumerProvisionKey is the key of one value of the state provisions under the form "..". +func ProvisionsByNodeConsumerProvisionKey(node, consumer, provision string) string { + return node + "." + consumer + "." + provision +} + +// ParseProvisionsByNodeConsumerProvisionKey reads the parts of a key of the form "..", in the order of the form; the last result is false for a key of another form. +func ParseProvisionsByNodeConsumerProvisionKey(key string) (string, string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", "", false + } + rest = after0 + v2, after2, found2 := strings.Cut(rest, ".") + if !found2 || v2 == "" { + return "", "", "", false + } + rest = after2 + if rest == "" { + return "", "", "", false + } + v4 := rest + rest = "" + if rest != "" { + return "", "", "", false + } + return v0, v2, v4, true +} + +// DecodeProvisionsByNodeConsumerProvisionValue reads one value of the state provisions under the form "..", refusing a field ProvisionsByNodeConsumerProvisionValue does not know and any text after the value. +func DecodeProvisionsByNodeConsumerProvisionValue(raw []byte) (ProvisionsByNodeConsumerProvisionValue, error) { + var v ProvisionsByNodeConsumerProvisionValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// ProvisionsByNodeConsumerProvisionLocalKey is the key of one value of the state provisions under the form "...". +func ProvisionsByNodeConsumerProvisionLocalKey(node, consumer, provision, local string) string { + return node + "." + consumer + "." + provision + "." + local +} + +// ParseProvisionsByNodeConsumerProvisionLocalKey reads the parts of a key of the form "...", in the order of the form; the last result is false for a key of another form. +func ParseProvisionsByNodeConsumerProvisionLocalKey(key string) (string, string, string, string, bool) { + rest := key + v0, after0, found0 := strings.Cut(rest, ".") + if !found0 || v0 == "" { + return "", "", "", "", false + } + rest = after0 + v2, after2, found2 := strings.Cut(rest, ".") + if !found2 || v2 == "" { + return "", "", "", "", false + } + rest = after2 + v4, after4, found4 := strings.Cut(rest, ".") + if !found4 || v4 == "" { + return "", "", "", "", false + } + rest = after4 + if rest == "" { + return "", "", "", "", false + } + v6 := rest + rest = "" + if rest != "" { + return "", "", "", "", false + } + return v0, v2, v4, v6, true +} + +// DecodeProvisionsByNodeConsumerProvisionLocalValue reads one value of the state provisions under the form "...", refusing a field ProvisionsByNodeConsumerProvisionLocalValue does not know and any text after the value. +func DecodeProvisionsByNodeConsumerProvisionLocalValue(raw []byte) (ProvisionsByNodeConsumerProvisionLocalValue, error) { + var v ProvisionsByNodeConsumerProvisionLocalValue + dec := json.NewDecoder(bytes.NewReader(raw)) + dec.DisallowUnknownFields() + if err := dec.Decode(&v); err != nil { + return v, err + } + if dec.More() { + return v, errors.New("text after the value") + } + return v, nil +} + +// ProvisioningInput is what provisioning takes. +type ProvisioningInput struct { + // for collect: a collection + Collection string `json:"collection,omitempty"` + // a module: for collect, a contributor + Module string `json:"module,omitempty"` + // a node + Node string `json:"node,omitempty"` + // registry (when not given), needs, applied or collect + What string `json:"what,omitempty"` +} + +// ProvisioningAnswer is what provisioning answers. +type ProvisioningAnswer = map[string]any + +// PlansByNodeModuleValue is one value of the state plans under the key .. +type PlansByNodeModuleValue struct { + Module string `json:"module"` + Node string `json:"node"` + // the module's plan + Plan map[string]any `json:"plan"` + V int64 `json:"v,omitempty"` +} + +// ProvisionsByNodeConsumerProvisionValueConsumer is a type of the contract. +type ProvisionsByNodeConsumerProvisionValueConsumer struct { + Module string `json:"module,omitempty"` + Node string `json:"node,omitempty"` +} + +// ProvisionsByNodeConsumerProvisionValueProvider is a type of the contract. +type ProvisionsByNodeConsumerProvisionValueProvider struct { + Module string `json:"module,omitempty"` + Node string `json:"node,omitempty"` +} + +// ProvisionsByNodeConsumerProvisionValueReference is a type of the contract. +type ProvisionsByNodeConsumerProvisionValueReference struct { + Generation int64 `json:"generation,omitempty"` + ID string `json:"id,omitempty"` +} + +// ProvisionsByNodeConsumerProvisionValueWarrant is a type of the contract. +type ProvisionsByNodeConsumerProvisionValueWarrant struct { + Ask string `json:"ask,omitempty"` + At string `json:"at,omitempty"` + By string `json:"by,omitempty"` + Option string `json:"option,omitempty"` +} + +// ProvisionsByNodeConsumerProvisionValue is one value of the state provisions under the key ... +type ProvisionsByNodeConsumerProvisionValue struct { + Ask string `json:"ask,omitempty"` + At string `json:"at"` + Consumer ProvisionsByNodeConsumerProvisionValueConsumer `json:"consumer"` + DependsOn []string `json:"depends-on,omitempty"` + Local string `json:"local,omitempty"` + // the plan's provision + Plan map[string]any `json:"plan,omitempty"` + Provider ProvisionsByNodeConsumerProvisionValueProvider `json:"provider"` + Provision string `json:"provision"` + Reference *ProvisionsByNodeConsumerProvisionValueReference `json:"reference,omitempty"` + State string `json:"state"` + V int64 `json:"v,omitempty"` + Warrant *ProvisionsByNodeConsumerProvisionValueWarrant `json:"warrant,omitempty"` + Why string `json:"why,omitempty"` +} + +// The values of ProvisionsByNodeConsumerProvisionValue's state. +const ( + ProvisionsByNodeConsumerProvisionValueStateAsked = "asked" + ProvisionsByNodeConsumerProvisionValueStateProvided = "provided" + ProvisionsByNodeConsumerProvisionValueStateWaits = "waits" + ProvisionsByNodeConsumerProvisionValueStateFailed = "failed" + ProvisionsByNodeConsumerProvisionValueStateRetiring = "retiring" +) + +// ProvisionsByNodeConsumerProvisionLocalValueConsumer is a type of the contract. +type ProvisionsByNodeConsumerProvisionLocalValueConsumer struct { + Module string `json:"module,omitempty"` + Node string `json:"node,omitempty"` +} + +// ProvisionsByNodeConsumerProvisionLocalValueProvider is a type of the contract. +type ProvisionsByNodeConsumerProvisionLocalValueProvider struct { + Module string `json:"module,omitempty"` + Node string `json:"node,omitempty"` +} + +// ProvisionsByNodeConsumerProvisionLocalValueReference is a type of the contract. +type ProvisionsByNodeConsumerProvisionLocalValueReference struct { + Generation int64 `json:"generation,omitempty"` + ID string `json:"id,omitempty"` +} + +// ProvisionsByNodeConsumerProvisionLocalValueWarrant is a type of the contract. +type ProvisionsByNodeConsumerProvisionLocalValueWarrant struct { + Ask string `json:"ask,omitempty"` + At string `json:"at,omitempty"` + By string `json:"by,omitempty"` + Option string `json:"option,omitempty"` +} + +// ProvisionsByNodeConsumerProvisionLocalValue is one value of the state provisions under the key .... +type ProvisionsByNodeConsumerProvisionLocalValue struct { + Ask string `json:"ask,omitempty"` + At string `json:"at"` + Consumer ProvisionsByNodeConsumerProvisionLocalValueConsumer `json:"consumer"` + DependsOn []string `json:"depends-on,omitempty"` + Local string `json:"local,omitempty"` + // the plan's provision + Plan map[string]any `json:"plan,omitempty"` + Provider ProvisionsByNodeConsumerProvisionLocalValueProvider `json:"provider"` + Provision string `json:"provision"` + Reference *ProvisionsByNodeConsumerProvisionLocalValueReference `json:"reference,omitempty"` + State string `json:"state"` + V int64 `json:"v,omitempty"` + Warrant *ProvisionsByNodeConsumerProvisionLocalValueWarrant `json:"warrant,omitempty"` + Why string `json:"why,omitempty"` +} + +// The values of ProvisionsByNodeConsumerProvisionLocalValue's state. +const ( + ProvisionsByNodeConsumerProvisionLocalValueStateAsked = "asked" + ProvisionsByNodeConsumerProvisionLocalValueStateProvided = "provided" + ProvisionsByNodeConsumerProvisionLocalValueStateWaits = "waits" + ProvisionsByNodeConsumerProvisionLocalValueStateFailed = "failed" + ProvisionsByNodeConsumerProvisionLocalValueStateRetiring = "retiring" +) diff --git a/provisioning/go/seats/meshprovisioner/go.mod b/provisioning/go/seats/meshprovisioner/go.mod new file mode 100644 index 0000000..8604b33 --- /dev/null +++ b/provisioning/go/seats/meshprovisioner/go.mod @@ -0,0 +1,3 @@ +module git.novox.be/novox/mesh-contracts/provisioning/go/seats/meshprovisioner + +go 1.26.0 diff --git a/provisioning/seats/index.json b/provisioning/seats/index.json new file mode 100644 index 0000000..185b64d --- /dev/null +++ b/provisioning/seats/index.json @@ -0,0 +1,9 @@ +{ + "format": 1, + "seats": [ + "mesh-provisioner", + "node-mounts", + "node-nfs-server", + "node-provisioner" + ] +} diff --git a/provisioning/ts/seats/meshprovisioner/package.json b/provisioning/ts/seats/meshprovisioner/package.json new file mode 100644 index 0000000..ff54abf --- /dev/null +++ b/provisioning/ts/seats/meshprovisioner/package.json @@ -0,0 +1,19 @@ +{ + "description": "The contract of the seat mesh-provisioner, generated from its definition.", + "devDependencies": { + "typescript": "^5.6.0" + }, + "exports": { + ".": "./dist/index.js" + }, + "files": [ + "dist" + ], + "name": "@novox/mesh-provisioning-contracts-mesh-provisioner", + "scripts": { + "build": "tsc", + "prepare": "tsc" + }, + "type": "module", + "version": "0.1.0" +} diff --git a/provisioning/ts/seats/meshprovisioner/src/index.ts b/provisioning/ts/seats/meshprovisioner/src/index.ts new file mode 100644 index 0000000..ae048d9 --- /dev/null +++ b/provisioning/ts/seats/meshprovisioner/src/index.ts @@ -0,0 +1,188 @@ +// Code generated by seatgen from the seat definitions; DO NOT EDIT. + +// The contract of the seat "mesh-provisioner", defined whole (seats/mesh-provisioner/seat.json): The provisioner (ADR 0349): which domain answers which kind of placeholder, what each module needs, what is applied where. + +export const seat = "mesh-provisioner"; + +export const verbs = { + provisioning: "provisioning", +} as const; + +export const states = { + plans: "plans", + provisions: "provisions", +} as const; + +/** The key of one value of the state plans under the form ".". */ +export function plansByNodeModuleKey(node: string, module: string): string { + return node + "." + module; +} + +/** The parts of a key of the form ".", or undefined for a key of another form. */ +export function parsePlansByNodeModuleKey(key: string): { node: string; module: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const node = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + if (rest === "") return undefined; + const module = rest; + rest = ""; + if (rest !== "") return undefined; + return { node, module }; +} + +/** The key of one value of the state provisions under the form "..". */ +export function provisionsByNodeConsumerProvisionKey(node: string, consumer: string, provision: string): string { + return node + "." + consumer + "." + provision; +} + +/** The parts of a key of the form "..", or undefined for a key of another form. */ +export function parseProvisionsByNodeConsumerProvisionKey(key: string): { node: string; consumer: string; provision: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const node = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + const at2 = rest.indexOf("."); + if (at2 <= 0) return undefined; + const consumer = rest.slice(0, at2); + rest = rest.slice(at2 + 1); + if (rest === "") return undefined; + const provision = rest; + rest = ""; + if (rest !== "") return undefined; + return { node, consumer, provision }; +} + +/** The key of one value of the state provisions under the form "...". */ +export function provisionsByNodeConsumerProvisionLocalKey(node: string, consumer: string, provision: string, local: string): string { + return node + "." + consumer + "." + provision + "." + local; +} + +/** The parts of a key of the form "...", or undefined for a key of another form. */ +export function parseProvisionsByNodeConsumerProvisionLocalKey(key: string): { node: string; consumer: string; provision: string; local: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const node = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + const at2 = rest.indexOf("."); + if (at2 <= 0) return undefined; + const consumer = rest.slice(0, at2); + rest = rest.slice(at2 + 1); + const at4 = rest.indexOf("."); + if (at4 <= 0) return undefined; + const provision = rest.slice(0, at4); + rest = rest.slice(at4 + 1); + if (rest === "") return undefined; + const local = rest; + rest = ""; + if (rest !== "") return undefined; + return { node, consumer, provision, local }; +} + +/** is what provisioning takes. */ +export interface ProvisioningInput { + /** for collect: a collection */ + "collection"?: string; + /** a module: for collect, a contributor */ + "module"?: string; + /** a node */ + "node"?: string; + /** registry (when not given), needs, applied or collect */ + "what"?: string; +} + +/** is what provisioning answers. */ +export type ProvisioningAnswer = Record; + +/** is one value of the state plans under the key .. */ +export interface PlansByNodeModuleValue { + "module": string; + "node": string; + /** the module's plan */ + "plan": Record; + "v"?: number; +} + +export interface ProvisionsByNodeConsumerProvisionValueConsumer { + "module"?: string; + "node"?: string; +} + +export interface ProvisionsByNodeConsumerProvisionValueProvider { + "module"?: string; + "node"?: string; +} + +export interface ProvisionsByNodeConsumerProvisionValueReference { + "generation"?: number; + "id"?: string; +} + +export interface ProvisionsByNodeConsumerProvisionValueWarrant { + "ask"?: string; + "at"?: string; + "by"?: string; + "option"?: string; +} + +/** is one value of the state provisions under the key ... */ +export interface ProvisionsByNodeConsumerProvisionValue { + "ask"?: string; + "at": string; + "consumer": ProvisionsByNodeConsumerProvisionValueConsumer; + "depends-on"?: string[]; + "local"?: string; + /** the plan's provision */ + "plan"?: Record; + "provider": ProvisionsByNodeConsumerProvisionValueProvider; + "provision": string; + "reference"?: ProvisionsByNodeConsumerProvisionValueReference; + "state": "asked" | "provided" | "waits" | "failed" | "retiring"; + "v"?: number; + "warrant"?: ProvisionsByNodeConsumerProvisionValueWarrant; + "why"?: string; +} + +export interface ProvisionsByNodeConsumerProvisionLocalValueConsumer { + "module"?: string; + "node"?: string; +} + +export interface ProvisionsByNodeConsumerProvisionLocalValueProvider { + "module"?: string; + "node"?: string; +} + +export interface ProvisionsByNodeConsumerProvisionLocalValueReference { + "generation"?: number; + "id"?: string; +} + +export interface ProvisionsByNodeConsumerProvisionLocalValueWarrant { + "ask"?: string; + "at"?: string; + "by"?: string; + "option"?: string; +} + +/** is one value of the state provisions under the key .... */ +export interface ProvisionsByNodeConsumerProvisionLocalValue { + "ask"?: string; + "at": string; + "consumer": ProvisionsByNodeConsumerProvisionLocalValueConsumer; + "depends-on"?: string[]; + "local"?: string; + /** the plan's provision */ + "plan"?: Record; + "provider": ProvisionsByNodeConsumerProvisionLocalValueProvider; + "provision": string; + "reference"?: ProvisionsByNodeConsumerProvisionLocalValueReference; + "state": "asked" | "provided" | "waits" | "failed" | "retiring"; + "v"?: number; + "warrant"?: ProvisionsByNodeConsumerProvisionLocalValueWarrant; + "why"?: string; +} + diff --git a/provisioning/ts/seats/meshprovisioner/tsconfig.json b/provisioning/ts/seats/meshprovisioner/tsconfig.json new file mode 100644 index 0000000..c610421 --- /dev/null +++ b/provisioning/ts/seats/meshprovisioner/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true + }, + "include": ["src/**/*.ts"] +} diff --git a/ts/seats/meshconfig/src/index.ts b/ts/seats/meshconfig/src/index.ts index 2c5bb50..e8fa023 100644 --- a/ts/seats/meshconfig/src/index.ts +++ b/ts/seats/meshconfig/src/index.ts @@ -32,6 +32,33 @@ export function retiredHolderSubject(holder: string): string { return "mesh.stream." + holder + ".event.retired"; } +export const states = { + resolved: "resolved", +} as const; + +/** The key of one value of the state resolved under the form "..". */ +export function resolvedByNodeModuleKeyKey(node: string, module: string, key: string): string { + return node + "." + module + "." + key; +} + +/** The parts of a key of the form "..", or undefined for a key of another form. */ +export function parseResolvedByNodeModuleKeyKey(key: string): { node: string; module: string; key: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const node = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + const at2 = rest.indexOf("."); + if (at2 <= 0) return undefined; + const module = rest.slice(0, at2); + rest = rest.slice(at2 + 1); + if (rest === "") return undefined; + const key = rest; + rest = ""; + if (rest !== "") return undefined; + return { node, module, key }; +} + /** is what config takes. */ export interface ConfigInput { /** list (when not given), set, clear or history */ @@ -117,3 +144,17 @@ export interface RetiredHolderEvent { "node"?: string; } +/** is one value of the state resolved under the key ... */ +export interface ResolvedByNodeModuleKeyValue { + "key": string; + "module": string; + "node": string; + /** the predefined key the value falls back to */ + "predefined"?: string; + /** an unset value is the operator's own to set */ + "required"?: boolean; + "source": string; + "trusted"?: boolean; + "value"?: unknown; +} + diff --git a/ts/seats/nodeengine/src/index.ts b/ts/seats/nodeengine/src/index.ts index d37717b..39feb40 100644 --- a/ts/seats/nodeengine/src/index.ts +++ b/ts/seats/nodeengine/src/index.ts @@ -43,6 +43,121 @@ export function manifestReadsChangedSubject(caller: string): string { return "mesh.seat.node-engine.event.manifest-reads-changed." + caller; } +export const states = { + have: "have", + composed: "composed", + data: "data", + packages: "packages", + health: "health", +} as const; + +/** The key of one value of the state have under the form ".". */ +export function haveByNodeModuleKey(node: string, module: string): string { + return node + "." + module; +} + +/** The parts of a key of the form ".", or undefined for a key of another form. */ +export function parseHaveByNodeModuleKey(key: string): { node: string; module: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const node = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + if (rest === "") return undefined; + const module = rest; + rest = ""; + if (rest !== "") return undefined; + return { node, module }; +} + +/** The key of one value of the state have under the form ".node". */ +export function haveByNodeNodeKey(node: string): string { + return node + ".node"; +} + +/** The parts of a key of the form ".node", or undefined for a key of another form. */ +export function parseHaveByNodeNodeKey(key: string): { node: string } | undefined { + let rest = key; + const at0 = rest.indexOf(".node"); + if (at0 <= 0) return undefined; + const node = rest.slice(0, at0); + rest = rest.slice(at0 + 5); + if (rest !== "") return undefined; + return { node }; +} + +/** The key of one value of the state composed under the form "..". */ +export function composedByNodeModuleFingerprintKey(node: string, module: string, fingerprint: string): string { + return node + "." + module + "." + fingerprint; +} + +/** The parts of a key of the form "..", or undefined for a key of another form. */ +export function parseComposedByNodeModuleFingerprintKey(key: string): { node: string; module: string; fingerprint: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const node = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + const at2 = rest.indexOf("."); + if (at2 <= 0) return undefined; + const module = rest.slice(0, at2); + rest = rest.slice(at2 + 1); + if (rest === "") return undefined; + const fingerprint = rest; + rest = ""; + if (rest !== "") return undefined; + return { node, module, fingerprint }; +} + +/** The key of one value of the state data under the form ".". */ +export function dataByNodeModuleKey(node: string, module: string): string { + return node + "." + module; +} + +/** The parts of a key of the form ".", or undefined for a key of another form. */ +export function parseDataByNodeModuleKey(key: string): { node: string; module: string } | undefined { + let rest = key; + const at0 = rest.indexOf("."); + if (at0 <= 0) return undefined; + const node = rest.slice(0, at0); + rest = rest.slice(at0 + 1); + if (rest === "") return undefined; + const module = rest; + rest = ""; + if (rest !== "") return undefined; + return { node, module }; +} + +/** The key of one value of the state packages under the form "". */ +export function packagesByNodeKey(node: string): string { + return node; +} + +/** The parts of a key of the form "", or undefined for a key of another form. */ +export function parsePackagesByNodeKey(key: string): { node: string } | undefined { + let rest = key; + if (rest === "") return undefined; + const node = rest; + rest = ""; + if (rest !== "") return undefined; + return { node }; +} + +/** The key of one value of the state health under the form "". */ +export function healthByNodeKey(node: string): string { + return node; +} + +/** The parts of a key of the form "", or undefined for a key of another form. */ +export function parseHealthByNodeKey(key: string): { node: string } | undefined { + let rest = key; + if (rest === "") return undefined; + const node = rest; + rest = ""; + if (rest !== "") return undefined; + return { node }; +} + /** one act asked through the seat, as acts reads it (ADR 0563 §9) */ export interface Act { /** the act: hand-over, access-act, judge-directories or setuid-search */ @@ -505,3 +620,103 @@ export type RefusedEvent = ActSaid; /** is the body of the event manifest-reads-changed. */ export type ManifestReadsChangedEvent = ManifestReadsChanged; +/** is one value of the state have under the key .. */ +export interface HaveByNodeModuleValue { + "build"?: string; + "build-alone"?: boolean; + /** the composition's digest */ + "composed"?: string; + "detail"?: string; + /** the fingerprint held */ + "fp"?: string; + /** the accounts handed */ + "handed"?: Record; + /** how it is */ + "health": string; + "judgement"?: string; + "kept"?: string[]; + /** how far it has come */ + "phase": string; + /** a pull the engine waits for */ + "pull-wait"?: Record; + "sealed"?: string[]; + "sealed-at"?: Record; + /** the user list written */ + "user-list"?: Record; + "v": number; + /** what it waits for */ + "waits"?: string; +} + +/** is one value of the state have under the key .node. */ +export interface HaveByNodeNodeValue { + "busy"?: Record; + "candidates"?: Record>; + "composed"?: string; + /** own, or the controller's */ + "composes"?: string; + "composes-why"?: string; + "filters"?: Record; + "profile"?: Record; + /** the node's record: its tunnel and certificate */ + "record"?: Record; + "sealed"?: string[]; + "sealed-at"?: Record; + "strays"?: Record; + "v": number; + "windows"?: Record; +} + +/** is one value of the state composed under the key ... */ +export interface ComposedByNodeModuleFingerprintValue { + "Data"?: boolean; + "FP": string; + "Filled"?: string[]; + "Module": string; + "Node": string; + /** the node's own adoption, identities, groups, libraries and provisioner account */ + "Own"?: Record; + /** the module's plan */ + "Plan"?: Record; + /** why the composition was refused */ + "Refused"?: string; + "Resources"?: Record[]; + "V"?: number; +} + +/** is one value of the state data under the key .. */ +export interface DataByNodeModuleValue { + "digest": string; + "items": Record[]; + "module": string; + "node": string; + "v"?: number; +} + +export interface PackagesByNodeValuePackagesItem { + "absent"?: boolean; + "modules"?: string[]; + "package"?: string; +} + +/** is one value of the state packages under the key . */ +export interface PackagesByNodeValue { + "node": string; + "packages": PackagesByNodeValuePackagesItem[]; +} + +/** is one value of the state health under the key . */ +export interface HealthByNodeValue { + "at": string; + "contract"?: number; + "fallback"?: Record; + "found"?: Record[]; + "network"?: Record; + "node": string; + "peers"?: Record[]; + "relogin"?: Record[]; + "root"?: Record[]; + "units"?: Record; + "v": number; +} + diff --git a/ts/src/index.ts b/ts/src/index.ts deleted file mode 100644 index 6df2ae8..0000000 --- a/ts/src/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -// Code generated by seatgen from the seat definitions; DO NOT EDIT. - -export * as seats from "./seats.js"; diff --git a/ts/src/seats.ts b/ts/src/seats.ts deleted file mode 100644 index 6c51e36..0000000 --- a/ts/src/seats.ts +++ /dev/null @@ -1,8185 +0,0 @@ -// Code generated by seatgen from the seat definitions; DO NOT EDIT. - -// The mesh's seat list, generated from every seat's definition (design 26). - -export interface Verb { - name: string; - description: string; - input?: Record; - output?: Record; - optional?: boolean; - replaces?: string[]; -} - -export interface Seat { - name: string; - scope: "mesh" | "node"; - grain: "whole" | "seat-list"; - decision: string; - delivers?: string; - replicated?: boolean; - accepts?: string[]; - emits?: string[]; - pulledByKind?: boolean; - yields?: boolean; - heldByTheEngine?: boolean; - serves?: Verb[]; -} - -export const names = { - meshStore: "mesh-store", - meshBroker: "mesh-broker", - meshVault: "mesh-vault", - meshConfig: "mesh-config", - meshProvisioner: "mesh-provisioner", - meshArtifactStore: "mesh-artifact-store", - meshCatalog: "mesh-catalog", - meshDelivery: "mesh-delivery", - meshPlacement: "mesh-placement", - meshDeliveryChecker: "mesh-delivery-checker", - meshConditions: "mesh-conditions", - meshWatchdogs: "mesh-watchdogs", - meshSelfCheck: "mesh-self-check", - meshHealers: "mesh-healers", - meshWants: "mesh-wants", - meshHandActs: "mesh-hand-acts", - meshEnrolment: "mesh-enrolment", - meshArtifactSweep: "mesh-artifact-sweep", - operatorChannel: "operator-channel", - npmPackageRegistry: "npm-package-registry", - goPackageRegistry: "go-package-registry", - containerImageRegistry: "container-image-registry", - ociArtifactRegistry: "oci-artifact-registry", - git: "git", - nodeBuildGo: "node-build-go", - nodeBuildNode: "node-build-node", - nodeBuildImage: "node-build-image", - meshDnsResolver: "mesh-dns-resolver", - nodeHostname: "node-hostname", - nodeIntrusionPrevention: "node-intrusion-prevention", - nodePacketFilter: "node-packet-filter", - nodeServiceManager: "node-service-manager", - nodePackageManager: "node-package-manager", - nodeContainerRuntime: "node-container-runtime", - nodeEnvironment: "node-environment", - nodeProvisioner: "node-provisioner", - nodeLoginShell: "node-login-shell", - nodePower: "node-power", - nodeHotkeys: "node-hotkeys", - nodeMessageBus: "node-message-bus", - nodeBackup: "node-backup", - thePrivateNetwork: "the-private-network", - nodeUplink: "node-uplink", - nodeResolver: "node-resolver", - nodeNfsServer: "node-nfs-server", - nodeMounts: "node-mounts", - nodeReads: "node-reads", - nodeFiles: "node-files", - nodeEngine: "node-engine", - nodeLoginManager: "node-login-manager", - nodeDisplayServer: "node-display-server", - nodeDisplaySession: "node-display-session", - nodeTerminalEmulator: "node-terminal-emulator", - nodeLauncher: "node-launcher", - nodeNotifier: "node-notifier", - nodeLockScreen: "node-lock-screen", - nodeClipboard: "node-clipboard", - nodeBar: "node-bar", - nodeCompositor: "node-compositor", - nodeSecretService: "node-secret-service", -} as const; - -export const list: readonly Seat[] = [ - { - "decision": "novox/hq ADR 0079", - "delivers": "postgres-database", - "grain": "seat-list", - "name": "mesh-store", - "scope": "mesh", - "serves": [ - { - "description": "Every database the store holds, with its on-disk size.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "databases", - "replaces": [ - "psql -l" - ] - }, - { - "description": "One read-only statement against one database the store holds.", - "input": { - "properties": { - "database": { - "description": "the database to query", - "type": "string" - }, - "sql": { - "description": "the read-only statement", - "type": "string" - } - }, - "required": [ - "database", - "sql" - ], - "type": "object" - }, - "name": "query", - "replaces": [ - "psql" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0079", - "delivers": "mesh-bus", - "emits": [ - "created", - "refused", - "drifted", - "drift-cleared", - "full", - "full-cleared", - "orphaned", - "recreated", - "dropped" - ], - "grain": "whole", - "name": "mesh-broker", - "scope": "mesh", - "serves": [ - { - "description": "Every entry the bus module created, its template, its settings and its fill against its cap.", - "input": { - "properties": { - "module": { - "description": "one module (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "list", - "optional": true, - "output": { - "properties": { - "entries": { - "description": "every entry", - "items": { - "description": "one entry as the bus holds it", - "properties": { - "bus-name": { - "description": "its name on the bus, _", - "type": "string" - }, - "configuration": { - "description": "its own configuration, where no template fits", - "type": "object" - }, - "fill": { - "properties": { - "bytes": { - "description": "bytes held", - "type": "integer" - }, - "cap": { - "description": "its cap in bytes", - "type": "integer" - }, - "count": { - "description": "messages, keys or objects held", - "type": "integer" - } - }, - "required": [ - "count", - "bytes", - "cap" - ], - "type": "object" - }, - "local-name": { - "description": "its local name in the module's manifest", - "type": "string" - }, - "module": { - "description": "the module asking it", - "type": "string" - }, - "options": { - "description": "the template's options the module set, within their bounds", - "type": "object" - }, - "settings": { - "description": "the settings the bus holds", - "type": "object" - }, - "state": { - "description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", - "enum": [ - "held", - "refused", - "waiting", - "drifted", - "full", - "orphaned" - ], - "type": "string" - }, - "storage-type": { - "description": "how the bus keeps it", - "enum": [ - "stream", - "key-value", - "object-store", - "ledger" - ], - "type": "string" - }, - "template": { - "description": "its template; empty for a configuration of its own", - "enum": [ - "state", - "events", - "work-queue", - "log", - "objects", - "ledger", - "" - ], - "type": "string" - }, - "why": { - "description": "why it is in its state, in plain words; empty when held as asked", - "type": "string" - } - }, - "required": [ - "module", - "local-name", - "storage-type", - "bus-name" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "entries" - ], - "type": "object" - } - }, - { - "description": "One entry, its template, settings and fill.", - "input": { - "properties": { - "local-name": { - "description": "the entry's local name", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - } - }, - "required": [ - "module", - "local-name" - ], - "type": "object" - }, - "name": "show", - "optional": true, - "output": { - "description": "one entry as the bus holds it", - "properties": { - "bus-name": { - "description": "its name on the bus, _", - "type": "string" - }, - "configuration": { - "description": "its own configuration, where no template fits", - "type": "object" - }, - "fill": { - "properties": { - "bytes": { - "description": "bytes held", - "type": "integer" - }, - "cap": { - "description": "its cap in bytes", - "type": "integer" - }, - "count": { - "description": "messages, keys or objects held", - "type": "integer" - } - }, - "required": [ - "count", - "bytes", - "cap" - ], - "type": "object" - }, - "local-name": { - "description": "its local name in the module's manifest", - "type": "string" - }, - "module": { - "description": "the module asking it", - "type": "string" - }, - "options": { - "description": "the template's options the module set, within their bounds", - "type": "object" - }, - "settings": { - "description": "the settings the bus holds", - "type": "object" - }, - "state": { - "description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", - "enum": [ - "held", - "refused", - "waiting", - "drifted", - "full", - "orphaned" - ], - "type": "string" - }, - "storage-type": { - "description": "how the bus keeps it", - "enum": [ - "stream", - "key-value", - "object-store", - "ledger" - ], - "type": "string" - }, - "template": { - "description": "its template; empty for a configuration of its own", - "enum": [ - "state", - "events", - "work-queue", - "log", - "objects", - "ledger", - "" - ], - "type": "string" - }, - "why": { - "description": "why it is in its state, in plain words; empty when held as asked", - "type": "string" - } - }, - "required": [ - "module", - "local-name", - "storage-type", - "bus-name" - ], - "type": "object" - } - }, - { - "description": "Compare what the bus holds with what was asked, entry by entry; a difference that only grows is said as such.", - "input": { - "properties": { - "module": { - "description": "one module (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "check", - "optional": true, - "output": { - "properties": { - "differences": { - "description": "every difference", - "items": { - "properties": { - "asked": { - "description": "asked", - "type": "object" - }, - "grows": { - "description": "the difference only grows: apply may act on it", - "type": "boolean" - }, - "held": { - "description": "held", - "type": "object" - }, - "local-name": { - "description": "the entry", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - } - }, - "required": [ - "module", - "local-name" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "differences" - ], - "type": "object" - } - }, - { - "description": "Entries the bus holds that no manifest asks for; never one whose manifest the judge refused or outgrew, nor one of the foundation's old sections.", - "input": { - "description": "no arguments", - "properties": {}, - "type": "object" - }, - "name": "orphans", - "optional": true, - "output": { - "properties": { - "entries": { - "description": "every orphan", - "items": { - "description": "one entry as the bus holds it", - "properties": { - "bus-name": { - "description": "its name on the bus, _", - "type": "string" - }, - "configuration": { - "description": "its own configuration, where no template fits", - "type": "object" - }, - "fill": { - "properties": { - "bytes": { - "description": "bytes held", - "type": "integer" - }, - "cap": { - "description": "its cap in bytes", - "type": "integer" - }, - "count": { - "description": "messages, keys or objects held", - "type": "integer" - } - }, - "required": [ - "count", - "bytes", - "cap" - ], - "type": "object" - }, - "local-name": { - "description": "its local name in the module's manifest", - "type": "string" - }, - "module": { - "description": "the module asking it", - "type": "string" - }, - "options": { - "description": "the template's options the module set, within their bounds", - "type": "object" - }, - "settings": { - "description": "the settings the bus holds", - "type": "object" - }, - "state": { - "description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", - "enum": [ - "held", - "refused", - "waiting", - "drifted", - "full", - "orphaned" - ], - "type": "string" - }, - "storage-type": { - "description": "how the bus keeps it", - "enum": [ - "stream", - "key-value", - "object-store", - "ledger" - ], - "type": "string" - }, - "template": { - "description": "its template; empty for a configuration of its own", - "enum": [ - "state", - "events", - "work-queue", - "log", - "objects", - "ledger", - "" - ], - "type": "string" - }, - "why": { - "description": "why it is in its state, in plain words; empty when held as asked", - "type": "string" - } - }, - "required": [ - "module", - "local-name", - "storage-type", - "bus-name" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "entries" - ], - "type": "object" - } - }, - { - "description": "Create one entry now.", - "input": { - "description": "one piece of bus state as a manifest asks it", - "properties": { - "configuration": { - "description": "its own configuration, where no template fits", - "type": "object" - }, - "local-name": { - "description": "its local name in the module's manifest", - "type": "string" - }, - "module": { - "description": "the module asking it", - "type": "string" - }, - "options": { - "description": "the template's options the module set, within their bounds", - "type": "object" - }, - "storage-type": { - "description": "how the bus keeps it", - "enum": [ - "stream", - "key-value", - "object-store", - "ledger" - ], - "type": "string" - }, - "template": { - "description": "its template; empty for a configuration of its own", - "enum": [ - "state", - "events", - "work-queue", - "log", - "objects", - "ledger", - "" - ], - "type": "string" - } - }, - "required": [ - "module", - "local-name", - "storage-type" - ], - "type": "object" - }, - "name": "create", - "optional": true, - "output": { - "description": "one entry as the bus holds it", - "properties": { - "bus-name": { - "description": "its name on the bus, _", - "type": "string" - }, - "configuration": { - "description": "its own configuration, where no template fits", - "type": "object" - }, - "fill": { - "properties": { - "bytes": { - "description": "bytes held", - "type": "integer" - }, - "cap": { - "description": "its cap in bytes", - "type": "integer" - }, - "count": { - "description": "messages, keys or objects held", - "type": "integer" - } - }, - "required": [ - "count", - "bytes", - "cap" - ], - "type": "object" - }, - "local-name": { - "description": "its local name in the module's manifest", - "type": "string" - }, - "module": { - "description": "the module asking it", - "type": "string" - }, - "options": { - "description": "the template's options the module set, within their bounds", - "type": "object" - }, - "settings": { - "description": "the settings the bus holds", - "type": "object" - }, - "state": { - "description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", - "enum": [ - "held", - "refused", - "waiting", - "drifted", - "full", - "orphaned" - ], - "type": "string" - }, - "storage-type": { - "description": "how the bus keeps it", - "enum": [ - "stream", - "key-value", - "object-store", - "ledger" - ], - "type": "string" - }, - "template": { - "description": "its template; empty for a configuration of its own", - "enum": [ - "state", - "events", - "work-queue", - "log", - "objects", - "ledger", - "" - ], - "type": "string" - }, - "why": { - "description": "why it is in its state, in plain words; empty when held as asked", - "type": "string" - } - }, - "required": [ - "module", - "local-name", - "storage-type", - "bus-name" - ], - "type": "object" - } - }, - { - "description": "Apply a difference that only grows: a larger cap, a longer age.", - "input": { - "properties": { - "local-name": { - "description": "the entry's local name", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - } - }, - "required": [ - "module", - "local-name" - ], - "type": "object" - }, - "name": "apply", - "optional": true, - "output": { - "description": "one entry as the bus holds it", - "properties": { - "bus-name": { - "description": "its name on the bus, _", - "type": "string" - }, - "configuration": { - "description": "its own configuration, where no template fits", - "type": "object" - }, - "fill": { - "properties": { - "bytes": { - "description": "bytes held", - "type": "integer" - }, - "cap": { - "description": "its cap in bytes", - "type": "integer" - }, - "count": { - "description": "messages, keys or objects held", - "type": "integer" - } - }, - "required": [ - "count", - "bytes", - "cap" - ], - "type": "object" - }, - "local-name": { - "description": "its local name in the module's manifest", - "type": "string" - }, - "module": { - "description": "the module asking it", - "type": "string" - }, - "options": { - "description": "the template's options the module set, within their bounds", - "type": "object" - }, - "settings": { - "description": "the settings the bus holds", - "type": "object" - }, - "state": { - "description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", - "enum": [ - "held", - "refused", - "waiting", - "drifted", - "full", - "orphaned" - ], - "type": "string" - }, - "storage-type": { - "description": "how the bus keeps it", - "enum": [ - "stream", - "key-value", - "object-store", - "ledger" - ], - "type": "string" - }, - "template": { - "description": "its template; empty for a configuration of its own", - "enum": [ - "state", - "events", - "work-queue", - "log", - "objects", - "ledger", - "" - ], - "type": "string" - }, - "why": { - "description": "why it is in its state, in plain words; empty when held as asked", - "type": "string" - } - }, - "required": [ - "module", - "local-name", - "storage-type", - "bus-name" - ], - "type": "object" - } - }, - { - "description": "Rebuild an entry whose settings differ in a way that cannot grow, from its data where the storage type allows, after a stream snapshot.", - "input": { - "properties": { - "local-name": { - "description": "the entry's local name", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - }, - "warrant": { - "description": "the operator's warrant, as the operator channel decided it (ADR 0259 §6)", - "type": "string" - }, - "why": { - "description": "why, kept in the record", - "type": "string" - } - }, - "required": [ - "module", - "local-name", - "warrant", - "why" - ], - "type": "object" - }, - "name": "recreate", - "optional": true, - "output": { - "description": "one entry as the bus holds it", - "properties": { - "bus-name": { - "description": "its name on the bus, _", - "type": "string" - }, - "configuration": { - "description": "its own configuration, where no template fits", - "type": "object" - }, - "fill": { - "properties": { - "bytes": { - "description": "bytes held", - "type": "integer" - }, - "cap": { - "description": "its cap in bytes", - "type": "integer" - }, - "count": { - "description": "messages, keys or objects held", - "type": "integer" - } - }, - "required": [ - "count", - "bytes", - "cap" - ], - "type": "object" - }, - "local-name": { - "description": "its local name in the module's manifest", - "type": "string" - }, - "module": { - "description": "the module asking it", - "type": "string" - }, - "options": { - "description": "the template's options the module set, within their bounds", - "type": "object" - }, - "settings": { - "description": "the settings the bus holds", - "type": "object" - }, - "state": { - "description": "the entry's state: held as asked, refused, waiting to be made, drifted from what is asked, full, or orphaned (no manifest asks it)", - "enum": [ - "held", - "refused", - "waiting", - "drifted", - "full", - "orphaned" - ], - "type": "string" - }, - "storage-type": { - "description": "how the bus keeps it", - "enum": [ - "stream", - "key-value", - "object-store", - "ledger" - ], - "type": "string" - }, - "template": { - "description": "its template; empty for a configuration of its own", - "enum": [ - "state", - "events", - "work-queue", - "log", - "objects", - "ledger", - "" - ], - "type": "string" - }, - "why": { - "description": "why it is in its state, in plain words; empty when held as asked", - "type": "string" - } - }, - "required": [ - "module", - "local-name", - "storage-type", - "bus-name" - ], - "type": "object" - } - }, - { - "description": "Remove one orphan, after a stream snapshot.", - "input": { - "properties": { - "local-name": { - "description": "the entry's local name", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - }, - "warrant": { - "description": "the operator's warrant, as the operator channel decided it (ADR 0259 §6)", - "type": "string" - }, - "why": { - "description": "why, kept in the record", - "type": "string" - } - }, - "required": [ - "module", - "local-name", - "warrant", - "why" - ], - "type": "object" - }, - "name": "drop", - "optional": true, - "output": { - "properties": { - "dropped": { - "description": "it is gone", - "type": "boolean" - }, - "snapshot": { - "description": "the snapshot taken first", - "type": "string" - } - }, - "required": [ - "dropped" - ], - "type": "object" - } - } - ] - }, - { - "decision": "novox/hq ADR 0161", - "delivers": "secret", - "grain": "whole", - "name": "mesh-vault", - "scope": "mesh", - "serves": [ - { - "description": "One secret, sealed to the public key the asker sends (the sealed box of ADR 0206), with its generation; answered only to an asker the vault allows, as the bus proved it. Never the value in the clear, and never to a private key.", - "input": { - "properties": { - "name": { - "description": "the secret's name", - "type": "string" - }, - "public_key": { - "description": "the asker's X25519 public key as SPKI PEM — never a private key", - "type": "string" - } - }, - "required": [ - "name", - "public_key" - ], - "type": "object" - }, - "name": "get", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "Ask the operator for a module's own secret at a desk, from the controller or a caller the vault's manifest names, named as the bus proved it (novox/hq ADR 0563 §8): the ask is recorded in the vault's store and bounded before the prompt opens, and what is typed is sealed to a key the vault made for the call and opens there alone.", - "input": { - "properties": { - "asked_by": { - "description": "who asked, as the bus named its caller", - "type": "string" - }, - "call": { - "description": "the second step: the call the first step answered", - "type": "string" - }, - "desk": { - "description": "the machine whose prompt asks", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - }, - "name": { - "description": "the secret's name in the module", - "type": "string" - }, - "node": { - "description": "the machine the secret is for", - "type": "string" - }, - "outcome": { - "description": "instead of sealed: how the prompt ended", - "type": "string" - }, - "sealed": { - "description": "the second step: the value sealed to the call's key — never the value", - "type": "string" - } - }, - "type": "object" - }, - "name": "secret-ask", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "Take the credential a provider's software issued for a provision, from the controller alone, which asks the provider's tool with a key the vault made for the call; kept as the provision's one credential, or, when the tool answers a value per holder named, each as that holder's own; the same value again writes nothing.", - "input": { - "properties": { - "call": { - "description": "the second step: the call the first step answered", - "type": "string" - }, - "holders": { - "description": "the provision's holders from that provider, each {node, module}: its tool may answer a value for each, kept as that holder's own and handed to it alone", - "items": { - "properties": { - "module": { - "description": "the consuming module", - "type": "string" - }, - "node": { - "description": "the holder's machine", - "type": "string" - } - }, - "type": "object" - }, - "type": "array" - }, - "module": { - "description": "the provider's module", - "type": "string" - }, - "provider": { - "description": "the provider's machine", - "type": "string" - }, - "provision": { - "description": "the provision", - "type": "string" - }, - "sealed": { - "description": "the second step: the value sealed to the call's key — never the value", - "type": "string" - } - }, - "type": "object" - }, - "name": "issued", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "Make a secret anew. Refused for a value a provider's software issued and for an accepted credential. Never answers the value.", - "input": { - "properties": { - "by": { - "description": "who asked, when the controller carries the act", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - }, - "name": { - "description": "the secret's name in the module", - "type": "string" - }, - "node": { - "description": "the machine, for a module's own secret", - "type": "string" - }, - "provider": { - "description": "the provider's machine", - "type": "string" - }, - "provision": { - "description": "the provision", - "type": "string" - } - }, - "type": "object" - }, - "name": "rotate", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "Take one value of the controller's store in, as it is, from the node-engine of a machine it was sealed to and no other caller: name the row, its origin and generation and get a call and a key made for it, then send the call, the value sealed to that key and a key to read back to. Never replaces a value the vault holds; answers what it kept, read back sealed to that key.", - "input": { - "properties": { - "call": { - "description": "the second step: the call the first step answered", - "type": "string" - }, - "consumer": { - "description": "the consumer's machine", - "type": "string" - }, - "consumer_module": { - "description": "the consuming module", - "type": "string" - }, - "generation": { - "description": "the controller's generation for the value", - "type": "string" - }, - "local": { - "description": "the credential's local name, when it has one", - "type": "string" - }, - "made_at": { - "description": "when it was made (RFC 3339)", - "type": "string" - }, - "module": { - "description": "its module", - "type": "string" - }, - "name": { - "description": "its name in the module", - "type": "string" - }, - "node": { - "description": "a module's own secret: its machine", - "type": "string" - }, - "origin": { - "description": "made, accepted or issued, as the controller's store says", - "type": "string" - }, - "provider": { - "description": "a pair credential: the provider's machine", - "type": "string" - }, - "provision": { - "description": "the provision", - "type": "string" - }, - "read_back_to": { - "description": "the second step: a raw X25519 public key the read-back is sealed to", - "type": "string" - }, - "sealed": { - "description": "the second step: the value sealed to the call's key — never the value", - "type": "string" - } - }, - "type": "object" - }, - "name": "move-in", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "State this machine's sealing key to the vault, in its own name, from the node-engine of the machine alone: at its start and whenever the key changes. A first or changed key is recorded and the machine's core secrets are said changed; the same key again changes nothing.", - "input": { - "properties": { - "key": { - "description": "the machine's sealing key, a raw X25519 public key in base64", - "type": "string" - } - }, - "type": "object" - }, - "name": "node-key", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "Answer the node-engine of one machine, about its own machine alone (as the bus proved the caller, never an argument), its modules' own secrets and the pair credentials it consumes or provides that it names, each sealed to the key it stated with node-key. A credential the vault does not hold is named missing; never a value in the clear.", - "input": { - "properties": { - "credentials": { - "description": "the pair credentials the machine's composition reads, each {role: consumer|provider, provision, consumer, consumer_module, provider, local}", - "items": { - "type": "object" - }, - "type": "array" - } - }, - "type": "object" - }, - "name": "own", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "Mint one bus account the grants part wants, now (novox/hq ADR 0561 §9): only while the controller's lease is handed away, never a node's account or the controller's, never anew for one the vault holds a value or an adopted hash for. Never the value.", - "input": { - "properties": { - "user": { - "description": "the bus user, e.g. ace.mesh-build-go", - "type": "string" - } - }, - "required": [ - "user" - ], - "type": "object" - }, - "name": "issue-account", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "Sign with the vault's key or authority (novox/hq ADR 0550 §2): kind node-certificate signs the certificate of a machine's internal name, asked by that machine's node-engine for the serving key its record states, built from bus state alone, or by mesh-enrolment for a node it enrols, for the serving key the node presented at redeem (ADR 0587 §3); kind enrolment signs a redeem answer under its own context, for mesh-enrolment alone. Answers the certificate or the signature and the signer's fingerprint, and says signed; never a key.", - "input": { - "properties": { - "kind": { - "description": "node-certificate or enrolment", - "type": "string" - }, - "node": { - "description": "the machine, for a node certificate: the caller's own, or the node mesh-enrolment enrols", - "type": "string" - }, - "reply": { - "description": "the redeem answer's bytes in base64, for an enrolment", - "type": "string" - }, - "serving_key": { - "description": "from mesh-enrolment: the serving key's public half the enrolling node presented", - "type": "string" - } - }, - "required": [ - "kind" - ], - "type": "object" - }, - "name": "sign", - "optional": true, - "output": { - "description": "for a node certificate the certificate as PEM and the authority that signed it, for an enrolment the signature in base64; always the signer's fingerprint and the digest; never a key's private half", - "properties": { - "authority": { - "description": "the authority's certificate that signed it, as PEM", - "type": "string" - }, - "certificate": { - "description": "the node certificate as PEM, for a node certificate", - "type": "string" - }, - "digest": { - "description": "sha256: of the bytes signed", - "type": "string" - }, - "kind": { - "description": "node-certificate or enrolment, as asked", - "type": "string" - }, - "node": { - "description": "the machine, for a node certificate", - "type": "string" - }, - "signature": { - "description": "the signature in base64, for an enrolment", - "type": "string" - }, - "signer": { - "description": "the signer's fingerprint", - "type": "string" - } - }, - "required": [ - "kind", - "signer", - "digest" - ], - "type": "object" - } - }, - { - "description": "Make a one-time token for a node mesh-enrolment issues on the operator's warrant: its secret kept with its copy sealed to the operator key and its hash written as the bus user enrol.. Answers only what the joining machine must trust (the bus's address, the bus certificate's fingerprint, the vault's public signing key), never the token; with sealed, the token sealed to the operator key, for tokens to relay; with drop, the token and its hash dropped, at the enrolment's end. Asked by mesh-enrolment alone, only from the lease write. The token carries the first tunnel mesh-enrolment built at issue (ADR 0587 §3, insight of 2026-11-02).", - "input": { - "properties": { - "drop": { - "description": "\"true\": drop the node's token and its hash", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "expires": { - "description": "when the token expires (RFC 3339), as the enrolment's key says", - "type": "string" - }, - "node": { - "description": "the node the token is for", - "type": "string" - }, - "sealed": { - "description": "\"true\": answer the token sealed to the operator key", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "tunnel": { - "additionalProperties": false, - "description": "the node's first tunnel, built by mesh-enrolment at issue and carried in the token: the hub's public key, the hub's endpoint, the node's tunnel address, its own tunnel key and the network's range", - "properties": { - "address": { - "description": "the joining node's tunnel address", - "type": "string" - }, - "hub_endpoint": { - "description": "the hub's endpoint, host:port", - "type": "string" - }, - "hub_key": { - "description": "the hub's tunnel public key", - "type": "string" - }, - "key": { - "description": "the joining machine's own tunnel public key, as given at issue", - "type": "string" - }, - "range": { - "description": "the private network's range, a prefix", - "type": "string" - } - }, - "required": [ - "address", - "hub_endpoint", - "hub_key", - "key", - "range" - ], - "type": "object" - } - }, - "required": [ - "node" - ], - "type": "object" - }, - "name": "enrolment-token", - "optional": true, - "output": { - "description": "what the joining machine must trust, never the token: the bus's address, its certificate's fingerprint and the vault's public signing key; the token sealed to the operator key when asked sealed; or that a drop left neither the token nor its hash", - "properties": { - "broker": { - "description": "the bus's address the machine dials", - "type": "string" - }, - "dropped": { - "description": "a drop left neither the token nor its hash", - "type": "boolean" - }, - "fingerprint": { - "description": "the fingerprint of the bus's certificate", - "type": "string" - }, - "node": { - "description": "the node the token is for", - "type": "string" - }, - "sealed": { - "description": "the token sealed to the operator key, when asked sealed, relayed as it is", - "type": "string" - }, - "signer": { - "description": "the vault's public signing key in base64", - "type": "string" - } - }, - "required": [ - "node" - ], - "type": "object" - } - }, - { - "description": "Mint a joining node's bus account node., sealed to the sealing key it presented, and write its hash; asked again for the same node and keys, answers the account it already minted; with retire, the account it minted for a revoked enrolment retired. Writes the node's node-logins key naming node.. Asked by mesh-enrolment alone. Never the value in the clear.", - "input": { - "properties": { - "keys_digest": { - "description": "the digest of every public key the node presented (delivery.KeysDigest)", - "type": "string" - }, - "node": { - "description": "the joining node", - "type": "string" - }, - "retire": { - "description": "\"true\": retire the account minted for this node", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "sealing_key": { - "description": "the sealing key's public half the node presented, which the account is sealed to", - "type": "string" - } - }, - "required": [ - "node" - ], - "type": "object" - }, - "name": "node-account", - "optional": true, - "output": { - "description": "the login minted and the account sealed to the sealing key the node presented, never in the clear; or that a retire left no account", - "properties": { - "account": { - "description": "the account sealed to the node's sealing key, in base64", - "type": "string" - }, - "login": { - "description": "the login minted", - "type": "string" - }, - "node": { - "description": "the joining node", - "type": "string" - }, - "retired": { - "description": "a retire left no account", - "type": "boolean" - } - }, - "required": [ - "node" - ], - "type": "object" - } - }, - { - "description": "Begin a node's account rotation to its other login: answered only for a call carrying a hand act recorded through mesh-hand-acts, which the vault reads back by its id. Makes the other login's value sealed to that node, writes it rotating in node-logins and says rotation-begun; says node-account-ready once the bus node's have shows a list holding both logins. One node at a time across the mesh, the bus node last: a call while a rotation runs waits until it is done.", - "input": { - "properties": { - "act": { - "description": "the id of the hand act recorded for it in mesh-hand-acts", - "type": "string" - }, - "node": { - "description": "the node whose account rotates", - "type": "string" - }, - "why": { - "description": "why, in a sentence", - "type": "string" - } - }, - "required": [ - "node", - "why", - "act" - ], - "type": "object" - }, - "name": "rotate-node", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "Confirm a node's switch to its new login: asked by the node-engine dialled as that login, the runtime's proof of the caller being the confirmation. The old login's hash is then removed, and rotation-done said once the bus node's have shows the list without it.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "node-account-confirm", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "Abandon a node's rotation before its old login is retired: the new login and its value dropped, the old one kept; a node that switched switches back first.", - "input": { - "properties": { - "node": { - "description": "the node whose rotation is abandoned", - "type": "string" - } - }, - "required": [ - "node" - ], - "type": "object" - }, - "name": "abandon", - "optional": true, - "output": { - "description": "the holder's answer", - "type": "object" - } - }, - { - "description": "Retire one module's own secret, by node, module and name, on the operator's warrant bound to exactly those three (design 32 *A value no longer read*). Emits own-retired.", - "input": { - "properties": { - "ask": { - "description": "the id of the operator's ask the warrant answers", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - }, - "name": { - "description": "the own secret's name", - "type": "string" - }, - "node": { - "description": "the node", - "type": "string" - }, - "warrant": { - "description": "the operator's warrant for this node, module and name: the hand act's id", - "type": "string" - } - }, - "required": [ - "node", - "module", - "name", - "warrant", - "ask" - ], - "type": "object" - }, - "name": "retire", - "optional": true, - "output": { - "properties": { - "retired": { - "description": "the secret is gone", - "type": "boolean" - } - }, - "required": [ - "retired" - ], - "type": "object" - } - } - ] - }, - { - "decision": "novox/hq ADR 0348, ADR 0565", - "grain": "whole", - "name": "mesh-config", - "scope": "mesh", - "serves": [ - { - "description": "Set, clear, list or read the history of config values: mesh config or node config for a predefined key, or a module's own value on one node, each set with why, validated, and naming each module on each node whose value moves.", - "input": { - "properties": { - "action": { - "description": "list (when not given), set, clear or history", - "type": "string" - }, - "by": { - "description": "who gave the value at the controller's terminal", - "type": "string" - }, - "key": { - "description": "the config key", - "type": "string" - }, - "limit": { - "description": "for history: at most this many, newest", - "type": "number" - }, - "module": { - "description": "a module: its own value, with node", - "type": "string" - }, - "node": { - "description": "a node", - "type": "string" - }, - "value": { - "description": "for set: the value, of the key's type: a number, a string, a boolean, or for a trusted key an object or a list" - }, - "why": { - "description": "for set and clear: why", - "type": "string" - } - }, - "type": "object" - }, - "name": "config", - "optional": true, - "output": { - "description": "the values asked, or the one set or cleared, or its history", - "type": "object" - } - }, - { - "description": "The open renames of config values (design 32 *A config value renamed*): per rename the module, the old and new name, when it moved, and the nodes still holding the old name open.", - "input": { - "properties": { - "module": { - "description": "one module (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "renames", - "optional": true, - "output": { - "properties": { - "renames": { - "description": "every open rename", - "items": { - "properties": { - "at": { - "description": "when it moved, RFC 3339", - "type": "string" - }, - "from": { - "description": "the old name", - "type": "string" - }, - "module": { - "description": "the module", - "type": "string" - }, - "open": { - "description": "the nodes still holding the old name open", - "items": { - "type": "string" - }, - "type": "array" - }, - "to": { - "description": "the new name", - "type": "string" - } - }, - "required": [ - "module", - "from", - "to" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "renames" - ], - "type": "object" - } - } - ] - }, - { - "decision": "novox/hq ADR 0349", - "grain": "seat-list", - "name": "mesh-provisioner", - "scope": "mesh", - "serves": [ - { - "description": "Read what the provisioner keeps: the registry (which domain answers which kind of placeholder), the needs each module on each node recorded, what is applied where, or the items of every collection, narrowed by node and module. Reads only.", - "input": { - "properties": { - "collection": { - "description": "for collect: a collection", - "type": "string" - }, - "module": { - "description": "a module: for collect, a contributor", - "type": "string" - }, - "node": { - "description": "a node", - "type": "string" - }, - "what": { - "description": "registry (when not given), needs, applied or collect", - "type": "string" - } - }, - "type": "object" - }, - "name": "provisioning", - "optional": true - } - ] - }, - { - "decision": "novox/hq ADR 0075", - "grain": "seat-list", - "name": "mesh-artifact-store", - "scope": "mesh" - }, - { - "decision": "novox/hq ADR 0604", - "grain": "seat-list", - "name": "mesh-catalog", - "scope": "mesh", - "serves": [ - { - "description": "Every module the trunk holds, each with its repository, its directory there, the merge commit it was last changed at and its fingerprint (or why it has none): the catalog of the mesh, read from its holder's state. Reads only.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "modules", - "optional": true - }, - { - "description": "One module whole: its repository, directory, commit and fingerprint, and its manifest as the trunk holds it. Older versions are in its repository's history. Reads only.", - "input": { - "properties": { - "module": { - "description": "the module's name", - "type": "string" - } - }, - "required": [ - "module" - ], - "type": "object" - }, - "name": "module", - "optional": true - }, - { - "description": "Which modules provide a provision (a module provides its own name too) and which require it, from the module graph's provision edges. Reads only.", - "input": { - "properties": { - "provision": { - "description": "the provision, such as postgres-database", - "type": "string" - } - }, - "required": [ - "provision" - ], - "type": "object" - }, - "name": "provides", - "optional": true - }, - { - "description": "The modules whose build edges reach a module, directly or through others (a base, a mesh package it publishes, the build module that builds them): what a change of it builds again, from the module graph. Reads only.", - "input": { - "properties": { - "module": { - "description": "the module's name", - "type": "string" - } - }, - "required": [ - "module" - ], - "type": "object" - }, - "name": "dependents", - "optional": true - } - ] - }, - { - "decision": "novox/hq ADR 0239", - "grain": "seat-list", - "name": "mesh-delivery", - "scope": "mesh", - "serves": [ - { - "description": "Every delivery not final, and those that ended in the last day, one line each: its id (owner/repository@commit), its state, its group, what it waits for and since when. Narrowed by state, repository or group.", - "input": { - "properties": { - "all": { - "description": "\"true\": the final ones of the last thirty days too", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "group": { - "description": "a group's id (its branch name)", - "type": "string" - }, - "repository": { - "description": "owner/repository", - "type": "string" - }, - "state": { - "description": "one state, e.g. delivering or held", - "type": "string" - } - }, - "type": "object" - }, - "name": "deliveries" - }, - { - "description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: the delivery view (nox-delivery): each machine's want and have, and since when", - "input": { - "properties": {}, - "type": "object" - }, - "name": "times", - "optional": true - }, - { - "description": "One delivery or group whole: its delivery plan (what it builds, what each machine receives, what is not an ordinary send), every transition with when and why, the machine steps of its walk, its group and its order.", - "input": { - "properties": { - "id": { - "description": "a delivery's id, or a group's", - "type": "string" - } - }, - "required": [ - "id" - ], - "type": "object" - }, - "name": "show" - }, - { - "description": "Every delivery group: its members in order, why each pair is ordered (declared, built by, version skew, engine before controller, by name), its composed check and its state, derived from its members.", - "input": { - "properties": { - "all": { - "description": "\"true\": the groups that ended too", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "type": "object" - }, - "name": "groups" - }, - { - "description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: the merge check's composition, which composes every machine a change touches", - "input": { - "properties": {}, - "type": "object" - }, - "name": "what-if", - "optional": true - }, - { - "description": "What the mesh's checks said of a pull request's head or of one commit: each of the commit's mesh statuses (mesh/check, the one a main requires from R4e, hq's mesh/repo-check, mesh/delivery, …) with its state, description and when it was set; the merge check's full verdict as the controller said it — each layer's summary, the machine that ran it, when, its build id and its report; and whether the branch's protection would let it merge, every required status being success.", - "input": { - "properties": { - "commit": { - "description": "a commit's sha, or the start of one, instead of a pull request", - "type": "string" - }, - "number": { - "description": "a pull request's number: its head is read", - "type": "string" - }, - "repository": { - "description": "owner/repository", - "type": "string" - } - }, - "required": [ - "repository" - ], - "type": "object" - }, - "name": "checks", - "optional": true - }, - { - "description": "The state table every delivery runs by: each transition with its guard, each state's bound and what healer H2 may do once it has passed; and the machine steps' table.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "table" - }, - { - "description": "Every delivery held past its state's bound, with the transition the table lets healer H2 take for it, or none.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "stalled" - }, - { - "description": "Check a rejected or ready delivery again: it goes back to proposed and the controller is asked for its check. With why.", - "input": { - "properties": { - "id": { - "description": "the delivery's id", - "type": "string" - }, - "why": { - "description": "why, kept with the transition", - "type": "string" - } - }, - "required": [ - "id", - "why" - ], - "type": "object" - }, - "name": "recheck" - }, - { - "description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: the assigner's asks (hold, restore, backup, record), answered on the phone or in the delivery view", - "input": { - "properties": {}, - "type": "object" - }, - "name": "release", - "optional": true - }, - { - "description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: a bad fingerprint is held and its canary set back by the assigner (ADR 0429 §6, ADR 0539 clause 18)", - "input": { - "properties": {}, - "type": "object" - }, - "name": "stop", - "optional": true - }, - { - "description": "Healer H2's verb: take the one transition the table names for a delivery held past its bound. Refused for any other.", - "input": { - "properties": { - "id": { - "description": "the delivery's id", - "type": "string" - }, - "why": { - "description": "the condition it answers", - "type": "string" - } - }, - "required": [ - "id", - "why" - ], - "type": "object" - }, - "name": "close" - }, - { - "description": "Removed in R6 (ADR 0539): refused by name, naming its replacement: nothing to retire: the gate keeps no delivery after its merge", - "input": { - "properties": {}, - "type": "object" - }, - "name": "retire-history", - "optional": true - }, - { - "description": "Ask the operator to settle deliveries held because their merge reached the trunk without a passing check: one ask on the operator's channel for all of them, as an Approve is asked. The verb moves nothing; on the operator's answer each goes to merged, and whether it is on the machines is derived as for any merge (novox/hq ADR 0556). Refused for a delivery not held, or held for another reason, naming why.", - "input": { - "additionalProperties": false, - "properties": { - "ids": { - "description": "the held deliveries' ids (owner/repository@commit)", - "items": { - "type": "string" - }, - "minItems": 1, - "type": "array" - }, - "why": { - "description": "why they may be settled, shown to the operator", - "type": "string" - } - }, - "required": [ - "ids", - "why" - ], - "type": "object" - }, - "name": "settle", - "optional": true - } - ] - }, - { - "decision": "novox/hq ADR 0567", - "grain": "seat-list", - "name": "mesh-placement", - "scope": "mesh", - "serves": [ - { - "description": "Read placement: one node's key, the nodes one module is placed on, or every key, each with its revision, and how full the state is against its cap. With history, one node's or one module's changes in order: a revision the state keeps whole, an older one as the proposal's changes from its placement-changed event.", - "input": { - "properties": { - "history": { - "description": "\"true\": the changes in order, from the stream and the state's revisions", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "module": { - "description": "one module: the nodes it is placed on", - "type": "string" - }, - "node": { - "description": "one node", - "type": "string" - }, - "seats": { - "description": "\"true\": who holds each seat, and on which node (the state seat-holders); with node, that node's", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "type": "object" - }, - "name": "placement", - "optional": true - }, - { - "description": "Every open proposal of placement: its changes, the revision of each key it was laid over, its candidates, every node's answer, the differences computed and answered, its verdict and its ask. Narrowed to one by id.", - "input": { - "properties": { - "id": { - "description": "one proposal's id", - "type": "string" - } - }, - "type": "object" - }, - "name": "proposals", - "optional": true - }, - { - "description": "Propose a change of placement, with why: laid over the current keys and refused at once, naming every finding, when the result cannot be placed (two modules on one host port, a provider not placed, a seat held once held twice, two nodes with one address, two hubs, a module the indexer does not hold, a node with no record). Otherwise the nodes it touches compose it in shadow and answer, and on a passing verdict the operator is asked to approve it. Who proposes is the caller, as the runtime proves it. An add-node from mesh-enrolment, whose warrant names its node and address, is written with no ask of its own: its Approve is the enrolment's (ADR 0587 §7).", - "input": { - "additionalProperties": false, - "properties": { - "changes": { - "description": "the changes, each an object with `op`: place (node, module, ports, bindings), take-off (node, module), pin (node, provision, provider {node, module}), unpin (node, provision), hold (node, module, claim, scope, kind, site), unhold (node, module, claim), decide (node, field, value: a decided field of the node, its address among them), config (node, module, mesh-wide, layer, mesh-wide-values, values), add-node (node, decided, keys: a joining node, mesh-enrolment's alone, the proposal's one change, with the enrolment's warrant, its tunnel key alone at the operator's yes; ADR 0587 §7), remove-node (node: a joining node never confirmed, taken off when its enrolment ends; mesh-enrolment's alone, on the same warrant)", - "items": { - "required": [ - "op", - "node" - ], - "type": "object" - }, - "minItems": 1, - "type": "array" - }, - "warrant": { - "description": "for an add-node alone: the enrolment's warrant {node, address, ask, warrant}, naming the change's node and address", - "required": [ - "node", - "address", - "ask", - "warrant" - ], - "type": "object" - }, - "why": { - "description": "why, shown to the operator with the proposal", - "type": "string" - } - }, - "required": [ - "why", - "changes" - ], - "type": "object" - }, - "name": "propose", - "optional": true - }, - { - "description": "Withdraw an open proposal: its key and its candidates are deleted and placement-withdrawn carries it whole. With why.", - "input": { - "properties": { - "id": { - "description": "the proposal's id", - "type": "string" - }, - "why": { - "description": "why, kept in the event", - "type": "string" - } - }, - "required": [ - "id", - "why" - ], - "type": "object" - }, - "name": "withdraw", - "optional": true - }, - { - "description": "Judge an open proposal now with the answers that stand, and raise its ask again when its verdict passes and the operator's channel does not hold the ask. Nothing more: the recovery of a proposal whose judgement or ask was lost.", - "input": { - "properties": { - "id": { - "description": "the proposal's id", - "type": "string" - } - }, - "required": [ - "id" - ], - "type": "object" - }, - "name": "again", - "optional": true - } - ] - }, - { - "decision": "novox/hq ADR 0547", - "grain": "whole", - "name": "mesh-delivery-checker", - "scope": "mesh", - "serves": [ - { - "description": "Try a pushed head before its pull request is opened: the checker judges it as it judges a pull request's head, merged onto its trunk's head, its unit and integration levels through the build modules, and owes the forge nothing. Answers the try's id at once; try-read reads where it stands. A try of the same head that waits or runs is the answer to a second ask; at most 4 wait or run at once, mesh-wide.", - "input": { - "properties": { - "head": { - "description": "the full commit to try, 40 or 64 hex digits", - "type": "string" - }, - "level": { - "description": "unit or integration: only this level; both when absent", - "type": "string" - }, - "repository": { - "description": "/", - "type": "string" - }, - "tests": { - "description": "a go test -run pattern: only those tests run, and nothing is kept", - "type": "string" - }, - "trunk": { - "description": "the trunk commit to merge it onto, or the trunk's branch for its head now; the trunk's head when absent", - "type": "string" - } - }, - "required": [ - "repository", - "head" - ], - "type": "object" - }, - "name": "try", - "optional": true, - "output": { - "properties": { - "id": { - "description": "the try's id", - "type": "string" - }, - "state": { - "description": "its state", - "type": "string" - } - }, - "required": [ - "id" - ], - "type": "object" - }, - "replaces": [ - "sh merge-check.sh", - "docker run postgres" - ] - }, - { - "description": "Where a try stands, as stored: queued, running, or its end (pass, fail, or error when it could not run), with what was said of it, the head and the trunk commit it was merged onto, the time it took and what failed first. Read-only.", - "input": { - "properties": { - "id": { - "description": "the try's id, try-", - "type": "string" - } - }, - "required": [ - "id" - ], - "type": "object" - }, - "name": "try-read", - "optional": true, - "output": { - "description": "the try: its state, its steps and its verdict when it has one", - "type": "object" - } - }, - { - "description": "End a try that waits or runs, as error, by hand: say why (kept in what it says). Its place among the tries is free at once and the next try of its head asks anew with a new id. A try that ended already is refused.", - "input": { - "properties": { - "id": { - "description": "the try's id, try-", - "type": "string" - }, - "why": { - "description": "why it is ended, in words", - "type": "string" - } - }, - "required": [ - "id", - "why" - ], - "type": "object" - }, - "name": "try-end", - "optional": true, - "output": { - "properties": { - "ended": { - "description": "it is ended", - "type": "boolean" - }, - "id": { - "description": "the try", - "type": "string" - } - }, - "required": [ - "id", - "ended" - ], - "type": "object" - } - } - ] - }, - { - "decision": "novox/hq ADR 0564", - "emits": [ - "condition-raised", - "condition-changed", - "condition-cleared" - ], - "grain": "seat-list", - "name": "mesh-conditions", - "scope": "mesh", - "serves": [ - { - "description": "What is wrong with the mesh now: every open condition, urgent first, then oldest, raised from the observers' findings (the watchman, the self-check, the healers, the assigner) and cleared when the finding goes, never by hand. Given a key, that one whole with its evidence; with history, every transition lately.", - "input": { - "properties": { - "days": { - "description": "with history: how many days back (default 7, at most 90)", - "type": "string" - }, - "history": { - "description": "\"true\": every raising, change, silence and clearing lately, oldest first", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "key": { - "description": "a condition's key: that one whole; with history, only its transitions", - "type": "string" - }, - "machine": { - "description": "only those about this machine", - "type": "string" - }, - "scope": { - "description": "only this scope: machine, plan, call, build, merge, provider, seat, bus, core, probe, mesh, delivery or module", - "type": "string" - }, - "severity": { - "description": "only urgent, or only warning", - "type": "string" - } - }, - "type": "object" - }, - "name": "conditions" - }, - { - "description": "Stop one condition's messages for a while, a hand act with why: it stays open and in status, and the silence is kept as its transition in history.", - "input": { - "properties": { - "cause": { - "description": "the cause in a word (the condition's kind when absent)", - "type": "string" - }, - "for": { - "description": "how long: 30m, 4h, 2d; at most 7d", - "type": "string" - }, - "key": { - "description": "the condition's key", - "type": "string" - }, - "why": { - "description": "why, required and recorded", - "type": "string" - } - }, - "required": [ - "key", - "for", - "why" - ], - "type": "object" - }, - "name": "silence" - }, - { - "description": "Read every observer's findings again and raise and clear what differs from the open conditions: the recovery of a keeper that missed a change.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "resync" - } - ] - }, - { - "decision": "novox/hq ADR 0564", - "grain": "seat-list", - "name": "mesh-watchdogs", - "scope": "mesh", - "serves": [ - { - "description": "Every watchdog of the signals table the watchman keeps, each a key that expires unless its signal renews it: its row, what renews it, its bound, the age of its newest signal, and the finding its expiry wrote.", - "input": { - "properties": { - "row": { - "description": "one row, e.g. S1", - "type": "string" - } - }, - "type": "object" - }, - "name": "signals" - }, - { - "description": "Every want, build or step the watchman says stopped moving past its bound, with since when and the words of its finding.", - "input": { - "properties": { - "machine": { - "description": "only those on this machine", - "type": "string" - } - }, - "type": "object" - }, - "name": "stuck" - }, - { - "description": "Every machine and tool runner the watchman sees connected, with its last word, and those it says silent; a machine that said it sleeps is shown as sleeping, not silent.", - "input": { - "properties": { - "machine": { - "description": "one machine", - "type": "string" - } - }, - "type": "object" - }, - "name": "presence" - }, - { - "description": "Every message a consumer on the bus gave up on, kept in DEAD_LETTERS: whose consumer, the subject, how often it was handed over and when it was given up, newest first. With id: that one whole. With deliver or drop, a hand act with why.", - "input": { - "properties": { - "cause": { - "description": "with deliver or drop: the cause in a word (dead-letter when absent)", - "type": "string" - }, - "consumer": { - "description": "a consumer's name, or .: only what that one gave up on", - "type": "string" - }, - "deliver": { - "description": "a dead letter's id: deliver it again to the consumer that gave it up (needs why)", - "type": "string" - }, - "drop": { - "description": "a dead letter's id: drop it for good (needs why)", - "type": "string" - }, - "id": { - "description": "a dead letter's id, as the list gives it: that one whole, with its message", - "type": "string" - }, - "limit": { - "description": "how many to list, newest first (default 50); only when listing", - "type": "string" - }, - "why": { - "description": "with deliver or drop: why, required and recorded", - "type": "string" - } - }, - "type": "object" - }, - "name": "dead-letters" - }, - { - "description": "Rebuild presence from the bus's connections, and the pulses and findings from the keys the watchman watches, and take every dead-letter notice waiting: the recovery of a watchman that missed a change.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "resync" - } - ] - }, - { - "decision": "novox/hq ADR 0564", - "emits": [ - "checked" - ], - "grain": "seat-list", - "name": "mesh-self-check", - "scope": "mesh", - "serves": [ - { - "description": "The self-check: the last run's verdict at once, each probe passed, failed, deferred (its facts not on the bus yet, named) or could not run, and how long ago. With run, a run now; with probe, one probe's verdict whole. Runs every five minutes on its own; each failure is a finding.", - "input": { - "properties": { - "probe": { - "description": "one probe's id (D6): its verdict alone, with every finding", - "type": "string" - }, - "run": { - "description": "\"true\": run every probe now and answer the verdict", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "type": "object" - }, - "name": "doctor" - }, - { - "description": "The registry: what each probe asserts, where it reads its facts, and the finding it writes.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "probes" - } - ] - }, - { - "decision": "novox/hq ADR 0564", - "emits": [ - "healer-acted" - ], - "grain": "seat-list", - "name": "mesh-healers", - "scope": "mesh", - "serves": [ - { - "description": "The healers: each registered response to one kind of condition, its repair, its budget and what happens when it is spent; every act taken lately with its outcome, and the mesh-wide brake.", - "input": { - "properties": { - "days": { - "description": "how many days of acts back (default 7)", - "type": "string" - } - }, - "type": "object" - }, - "name": "healers" - }, - { - "description": "Take one healer's act for one open condition now, within its budget and the brake.", - "input": { - "properties": { - "key": { - "description": "the condition's key", - "type": "string" - }, - "why": { - "description": "why now, kept with the act", - "type": "string" - } - }, - "required": [ - "key", - "why" - ], - "type": "object" - }, - "name": "heal" - }, - { - "description": "Record a drill: something broken on purpose to see the mesh raise and clear it, with what it tests. A drill is never a repair: no healer is wanted for it however often it is run.", - "input": { - "properties": { - "condition": { - "description": "the key of the condition the drill is meant to raise, if any (optional)", - "type": "string" - }, - "what": { - "description": "what was done on purpose, in a line", - "type": "string" - }, - "why": { - "description": "what the drill tests", - "type": "string" - } - }, - "required": [ - "what", - "why" - ], - "type": "object" - }, - "name": "drill" - } - ] - }, - { - "decision": "novox/hq ADR 0565", - "emits": [ - "policy-changed" - ], - "grain": "seat-list", - "name": "mesh-wants", - "scope": "mesh", - "serves": [ - { - "description": "Whether each module's new builds wait for a person: its policy (roll, together or record) and where it comes from (a person, the module, the bus, its irreplaceable data, the default); the bus is never rolled. With module, that one; with module, policy and why, a person's choice for it, recorded as a hand act; with policy default, the choice taken back. With compare, each node's own composition and the assigner's findings.", - "input": { - "properties": { - "compare": { - "description": "\"true\": each node's own composition, by its digest, and the assigner's findings", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "module": { - "description": "one module", - "type": "string" - }, - "policy": { - "description": "with module: roll, together, record, or default to take a person's choice back", - "type": "string" - }, - "why": { - "description": "with policy: why, required and kept", - "type": "string" - } - }, - "type": "object" - }, - "name": "upgrade" - }, - { - "description": "What a bus upgrade would do: the fingerprint of the bus module each node runs against the indexer's newest, and the last bus steps from the hand-act log and the assigner's puts of the bus. Read-only: the step itself is the put on the operator's warrant.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "bus" - } - ] - }, - { - "decision": "novox/hq ADR 0565", - "emits": [ - "act-recorded" - ], - "grain": "seat-list", - "name": "mesh-hand-acts", - "scope": "mesh", - "serves": [ - { - "description": "Record one act done by hand: what, why, its cause in a word or a condition's kind (the word a second act for the same reason uses) and the condition it addresses. Who did it is the caller the runtime proves, never said by the caller. Said as act-recorded.", - "input": { - "properties": { - "cause": { - "description": "the cause in a word, or a condition's kind", - "type": "string" - }, - "condition": { - "description": "the key of the condition it addressed, if any (optional)", - "type": "string" - }, - "verb": { - "description": "the act's verb in the log: a repair when absent, `hand-act drill` for a drill, or a recorder's own", - "type": "string" - }, - "what": { - "description": "what was done, in a line", - "type": "string" - }, - "why": { - "description": "why it had to be done by hand", - "type": "string" - } - }, - "required": [ - "what", - "why" - ], - "type": "object" - }, - "name": "hand-act" - }, - { - "description": "What was done by hand lately, over this log and the controller's: who, why and the cause of each, and the causes recorded twice within a fortnight (a healer is wanted).", - "input": { - "properties": { - "cause": { - "description": "only this cause", - "type": "string" - }, - "days": { - "description": "how many days back (default 14)", - "type": "string" - }, - "warrants": { - "description": "\"true\": every warrant recorded", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "who": { - "description": "only acts whose who contains this", - "type": "string" - } - }, - "type": "object" - }, - "name": "hand-acts" - }, - { - "description": "Record what a module did on the operator's warrant: who chose, through which channel, with which proofs and which answer read from the router's own record of that module's ask, never from the caller; once per ask. Refused for an ask still open, ended without a choice, or not that module's.", - "input": { - "properties": { - "ask": { - "description": "its ask's id", - "type": "string" - }, - "asker": { - "description": "the module that asked", - "type": "string" - } - }, - "required": [ - "asker", - "ask" - ], - "type": "object" - }, - "name": "warranted" - } - ] - }, - { - "decision": "novox/hq ADR 0587", - "emits": [ - "enrolment-issued", - "node-enrolled", - "enrolment-refused", - "enrolment-revoked", - "enrolment-expired" - ], - "grain": "seat-list", - "name": "mesh-enrolment", - "scope": "mesh", - "serves": [ - { - "description": "Issue a one-time token for a new machine, on the operator's Approve: refused at once, naming every finding, for a name that is not a node name or that a node record, a placement key or an open enrolment holds, and for every call before the controller's lease is handed away. Otherwise reserves the lowest free private-network address, asks the operator under the seat's own name and, on the warrant, adds the node to placement with its tunnel key, asks the vault for the token and says enrolment-issued. The token itself is read only through tokens.", - "input": { - "properties": { - "expires": { - "description": "how long the token lasts, a duration such as 1h or 24h: one hour when not given, at most 24 hours", - "type": "string" - }, - "node": { - "description": "the new machine's name in the mesh", - "type": "string" - }, - "overlay_key": { - "description": "the new machine's private network (tunnel) public key, as `nox-mesh-host key` prints it", - "type": "string" - }, - "why": { - "description": "why it joins, shown to the operator with the ask", - "type": "string" - } - }, - "required": [ - "node", - "why", - "overlay_key" - ], - "type": "object" - }, - "name": "issue" - }, - { - "description": "Spend a token: asked by a joining node-engine dialled as enrol. alone, with the public halves of its keys and its identity key's proof over them. Spends the token, asks the vault for the node's account and certificate, writes the node into placement, and answers the account sealed to the node, its certificate and the vault's public half, signed by the vault. Asked again with the same keys it finishes what is not done and answers the same; with other keys it is refused.", - "input": { - "additionalProperties": false, - "properties": { - "node": { - "description": "the node the token was issued for", - "type": "string" - }, - "overlay_key": { - "description": "the private network key's public half", - "type": "string" - }, - "profile": { - "description": "what the machine is, as its record says it", - "type": "object" - }, - "proof": { - "description": "the identity key's signature over the redeem's proof bytes (delivery.RedeemProof), base64", - "type": "string" - }, - "public_key": { - "description": "the identity key's public half, base64", - "type": "string" - }, - "sealing_key": { - "description": "the sealing key's public half, which the account is sealed to", - "type": "string" - }, - "serving_key": { - "description": "the serving key's public half, which the certificate binds", - "type": "string" - }, - "tunnel": { - "description": "a tunnel found on the machine: everything but its private key", - "type": "object" - } - }, - "required": [ - "node", - "public_key", - "sealing_key", - "proof" - ], - "type": "object" - }, - "name": "redeem" - }, - { - "description": "Confirm an enrolment: asked by the node-engine dialled as its own node login alone, the runtime's proof of that caller being the confirmation. Says node-enrolled and ends the enrolment.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "confirm" - }, - { - "description": "End an enrolment at any stage, with why, without an Approve: withdraws an open ask, deletes the node's key and its address, has the vault retire an account it already minted for it, records the act as a hand act and says enrolment-revoked.", - "input": { - "properties": { - "node": { - "description": "the node", - "type": "string" - }, - "why": { - "description": "why, recorded with the hand act", - "type": "string" - } - }, - "required": [ - "node", - "why" - ], - "type": "object" - }, - "name": "revoke" - }, - { - "description": "Read every open enrolment, or one: its stage, expiry, ask and revision, the bucket's fill against its cap. For a node at live, its token sealed to the operator key, asked of the vault at that moment and relayed as the vault sealed it. With history, the ended enrolments from the seat's events, as far back as the events are kept.", - "input": { - "properties": { - "history": { - "description": "\"true\": the ended enrolments too", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "node": { - "description": "one node", - "type": "string" - } - }, - "type": "object" - }, - "name": "tokens" - }, - { - "description": "Raise an issue's ask again when the operator's channel does not hold it, and nothing more.", - "input": { - "properties": { - "node": { - "description": "the node whose ask is raised again", - "type": "string" - } - }, - "required": [ - "node" - ], - "type": "object" - }, - "name": "again" - } - ] - }, - { - "decision": "novox/hq ADR 0571", - "emits": [ - "artifacts-gone" - ], - "grain": "seat-list", - "name": "mesh-artifact-sweep", - "scope": "mesh", - "serves": [ - { - "description": "Every artifact the mesh made that lives in its artifact store, each with its repository, digest, kind (image or archive) and state: kept, and why (a want names it, or one of the five newest builds of its module in the builder's log), eligible (made, kept for no reason, not yet let go), or unrecorded (in the store and named by nothing the mesh made: never removed). Reads only.", - "input": { - "properties": { - "collected": { - "description": "\"true\": list the collected references too (counted always)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "repository": { - "description": "only this repository's references, as / (the counts stay whole)", - "type": "string" - } - }, - "type": "object" - }, - "name": "artifacts" - }, - { - "description": "Whether the artifact store holds each reference given, asked with one HEAD each: held, gone (the store answered 404) or unknown with why. The builder asks it before it reuses a done key. Reads only.", - "input": { - "properties": { - "references": { - "description": "the artifacts' references, artifact-store:////blobs/sha256:: a list, or one text separated by commas", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "references" - ], - "type": "object" - }, - "name": "held" - }, - { - "description": "The artifact store's sweep, now: what is eligible is let go of. Without confirm a dry run: what would be let go, nothing held or deleted. With confirm and why: every kept archive held first, then each eligible artifact let go of, oldest first, an index with the platform manifests no kept index names, each deletion recorded in the hand-act log. Bounded by most (500 by default, at most 5000) and 45 seconds; what is left is said. Never touches a digest the mesh did not make.", - "input": { - "properties": { - "confirm": { - "description": "\"true\": let go of what is eligible (needs why); without it nothing is done", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "most": { - "description": "let go of at most this many (500 by default, at most 5000)", - "type": "string" - }, - "why": { - "description": "with confirm: why, required and recorded", - "type": "string" - } - }, - "type": "object" - }, - "name": "collect" - }, - { - "description": "The bases the mesh copied into its artifact store, each kept (a kept build stood on it), eligible (only builds no longer kept stood on it) or unrecorded (no build the builder's log holds stood on it: never removed), by what the builder's log says each build stood on. Records nothing by hand.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "mirrors" - }, - { - "description": "Every container image one machine's want and have name, each with the resources naming it: what the machine keeps when it prunes its images. Reads only.", - "input": { - "properties": { - "node": { - "description": "the machine's name", - "type": "string" - } - }, - "required": [ - "node" - ], - "type": "object" - }, - "name": "images" - } - ] - }, - { - "decision": "novox/hq ADR 0109", - "delivers": "npm-package-registry", - "emits": [ - "published", - "refused", - "removed", - "kept" - ], - "grain": "whole", - "name": "npm-package-registry", - "scope": "mesh", - "serves": [ - { - "description": "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).", - "input": { - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "parts": { - "description": "every part's digest and size; none when the digest is already held", - "items": { - "description": "one part of a transfer", - "properties": { - "digest": { - "description": "the part's digest, sha256:: an image's layer or config, a package's file, a bundle's blob", - "type": "string" - }, - "size": { - "description": "its size in bytes", - "type": "integer" - } - }, - "required": [ - "digest", - "size" - ], - "type": "object" - }, - "type": "array" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "transfer": { - "description": "the transfer's name, the caller's own; the same name opens the same transfer again", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "transfer", - "name", - "kind", - "digest" - ], - "type": "object" - }, - "name": "publish", - "optional": true, - "output": { - "properties": { - "held": { - "description": "the digest is held already: nothing to send; the outcome follows as an event", - "type": "boolean" - }, - "lacks": { - "description": "the digests of the parts the holder lacks, to send by publish-part", - "items": { - "type": "string" - }, - "type": "array" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer" - ], - "type": "object" - } - }, - { - "description": "Send one chunk of one part of an open transfer; the last chunk of the last part ends the transfer, and the holder hashes what it received, asks builder_versions, stores it and says published or refused. A chunk's message stays under the bus's largest message (1 MiB), so at most 760 KiB of data, base64.", - "input": { - "properties": { - "data": { - "description": "the chunk's bytes, base64 (at most 760 KiB before encoding)", - "type": "string" - }, - "last": { - "description": "this chunk is the part's last", - "type": "boolean" - }, - "part": { - "description": "the part's digest", - "type": "string" - }, - "sequence": { - "description": "the chunk's place in its part, from 0", - "type": "integer" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer", - "part", - "sequence", - "last", - "data" - ], - "type": "object" - }, - "name": "publish-part", - "optional": true, - "output": { - "properties": { - "complete": { - "description": "the part is whole and its bytes match its digest", - "type": "boolean" - }, - "part": { - "description": "the part", - "type": "string" - }, - "received": { - "description": "the chunks of the part held so far", - "type": "integer" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer", - "part" - ], - "type": "object" - } - }, - { - "description": "The versions the seat holds of one name, newest first: each one's version, tag, digest, kind and when it was published.", - "input": { - "properties": { - "kind": { - "description": "narrowed to one kind (optional)", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "name": "versions", - "optional": true, - "output": { - "properties": { - "name": { - "description": "the name", - "type": "string" - }, - "versions": { - "description": "newest first", - "items": { - "description": "one published thing, as every registry event and answer names it", - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "name", - "kind", - "digest" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "name", - "versions" - ], - "type": "object" - } - }, - { - "description": "Remove one tagged version; the holder refuses an untagged one but for the retention removal of a bundle, archive or copied base by the artifact sweep's holder. Every removal emits removed with its reason. A tagged version the store cannot remove alone, because another tag names its digest, stays whole, its tag and bytes kept: the answer says kept with the reason shared-digest and the tags still naming the digest, removed false, and kept is emitted, never removed; the caller ends there without a retry.", - "input": { - "properties": { - "digest": { - "description": "the digest (for a bundle, archive or copied base)", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - }, - "reason": { - "description": "why it goes", - "enum": [ - "tag-gone", - "retention" - ], - "type": "string" - }, - "tag": { - "description": "the tag (for a tagged version)", - "type": "string" - }, - "version": { - "description": "the version", - "type": "string" - } - }, - "required": [ - "name", - "reason" - ], - "type": "object" - }, - "name": "remove", - "optional": true, - "output": { - "properties": { - "artifact": { - "description": "one published thing, as every registry event and answer names it", - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "name", - "kind", - "digest" - ], - "type": "object" - }, - "kept": { - "description": "it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)", - "type": "boolean" - }, - "reason": { - "description": "why it was kept: shared-digest, another tag names its digest", - "enum": [ - "shared-digest" - ], - "type": "string" - }, - "removed": { - "description": "it is gone", - "type": "boolean" - }, - "tags": { - "description": "the other tags still naming its digest, when kept", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "removed" - ], - "type": "object" - } - } - ] - }, - { - "decision": "novox/hq ADR 0605", - "delivers": "go-package-registry", - "emits": [ - "published", - "refused", - "removed", - "kept" - ], - "grain": "whole", - "name": "go-package-registry", - "scope": "mesh", - "serves": [ - { - "description": "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).", - "input": { - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "parts": { - "description": "every part's digest and size; none when the digest is already held", - "items": { - "description": "one part of a transfer", - "properties": { - "digest": { - "description": "the part's digest, sha256:: an image's layer or config, a package's file, a bundle's blob", - "type": "string" - }, - "size": { - "description": "its size in bytes", - "type": "integer" - } - }, - "required": [ - "digest", - "size" - ], - "type": "object" - }, - "type": "array" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "transfer": { - "description": "the transfer's name, the caller's own; the same name opens the same transfer again", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "transfer", - "name", - "kind", - "digest" - ], - "type": "object" - }, - "name": "publish", - "optional": true, - "output": { - "properties": { - "held": { - "description": "the digest is held already: nothing to send; the outcome follows as an event", - "type": "boolean" - }, - "lacks": { - "description": "the digests of the parts the holder lacks, to send by publish-part", - "items": { - "type": "string" - }, - "type": "array" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer" - ], - "type": "object" - } - }, - { - "description": "Send one chunk of one part of an open transfer; the last chunk of the last part ends the transfer, and the holder hashes what it received, asks builder_versions, stores it and says published or refused. A chunk's message stays under the bus's largest message (1 MiB), so at most 760 KiB of data, base64.", - "input": { - "properties": { - "data": { - "description": "the chunk's bytes, base64 (at most 760 KiB before encoding)", - "type": "string" - }, - "last": { - "description": "this chunk is the part's last", - "type": "boolean" - }, - "part": { - "description": "the part's digest", - "type": "string" - }, - "sequence": { - "description": "the chunk's place in its part, from 0", - "type": "integer" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer", - "part", - "sequence", - "last", - "data" - ], - "type": "object" - }, - "name": "publish-part", - "optional": true, - "output": { - "properties": { - "complete": { - "description": "the part is whole and its bytes match its digest", - "type": "boolean" - }, - "part": { - "description": "the part", - "type": "string" - }, - "received": { - "description": "the chunks of the part held so far", - "type": "integer" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer", - "part" - ], - "type": "object" - } - }, - { - "description": "The versions the seat holds of one name, newest first: each one's version, tag, digest, kind and when it was published.", - "input": { - "properties": { - "kind": { - "description": "narrowed to one kind (optional)", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "name": "versions", - "optional": true, - "output": { - "properties": { - "name": { - "description": "the name", - "type": "string" - }, - "versions": { - "description": "newest first", - "items": { - "description": "one published thing, as every registry event and answer names it", - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "name", - "kind", - "digest" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "name", - "versions" - ], - "type": "object" - } - }, - { - "description": "Remove one tagged version; the holder refuses an untagged one but for the retention removal of a bundle, archive or copied base by the artifact sweep's holder. Every removal emits removed with its reason. A tagged version the store cannot remove alone, because another tag names its digest, stays whole, its tag and bytes kept: the answer says kept with the reason shared-digest and the tags still naming the digest, removed false, and kept is emitted, never removed; the caller ends there without a retry.", - "input": { - "properties": { - "digest": { - "description": "the digest (for a bundle, archive or copied base)", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - }, - "reason": { - "description": "why it goes", - "enum": [ - "tag-gone", - "retention" - ], - "type": "string" - }, - "tag": { - "description": "the tag (for a tagged version)", - "type": "string" - }, - "version": { - "description": "the version", - "type": "string" - } - }, - "required": [ - "name", - "reason" - ], - "type": "object" - }, - "name": "remove", - "optional": true, - "output": { - "properties": { - "artifact": { - "description": "one published thing, as every registry event and answer names it", - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "name", - "kind", - "digest" - ], - "type": "object" - }, - "kept": { - "description": "it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)", - "type": "boolean" - }, - "reason": { - "description": "why it was kept: shared-digest, another tag names its digest", - "enum": [ - "shared-digest" - ], - "type": "string" - }, - "removed": { - "description": "it is gone", - "type": "boolean" - }, - "tags": { - "description": "the other tags still naming its digest, when kept", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "removed" - ], - "type": "object" - } - } - ] - }, - { - "decision": "novox/hq ADR 0605", - "emits": [ - "published", - "refused", - "removed", - "kept", - "held", - "released" - ], - "grain": "whole", - "name": "container-image-registry", - "scope": "mesh", - "serves": [ - { - "description": "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).", - "input": { - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "parts": { - "description": "every part's digest and size; none when the digest is already held", - "items": { - "description": "one part of a transfer", - "properties": { - "digest": { - "description": "the part's digest, sha256:: an image's layer or config, a package's file, a bundle's blob", - "type": "string" - }, - "size": { - "description": "its size in bytes", - "type": "integer" - } - }, - "required": [ - "digest", - "size" - ], - "type": "object" - }, - "type": "array" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "transfer": { - "description": "the transfer's name, the caller's own; the same name opens the same transfer again", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "transfer", - "name", - "kind", - "digest" - ], - "type": "object" - }, - "name": "publish", - "optional": true, - "output": { - "properties": { - "held": { - "description": "the digest is held already: nothing to send; the outcome follows as an event", - "type": "boolean" - }, - "lacks": { - "description": "the digests of the parts the holder lacks, to send by publish-part", - "items": { - "type": "string" - }, - "type": "array" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer" - ], - "type": "object" - } - }, - { - "description": "Send one chunk of one part of an open transfer; the last chunk of the last part ends the transfer, and the holder hashes what it received, asks builder_versions, stores it and says published or refused. A chunk's message stays under the bus's largest message (1 MiB), so at most 760 KiB of data, base64.", - "input": { - "properties": { - "data": { - "description": "the chunk's bytes, base64 (at most 760 KiB before encoding)", - "type": "string" - }, - "last": { - "description": "this chunk is the part's last", - "type": "boolean" - }, - "part": { - "description": "the part's digest", - "type": "string" - }, - "sequence": { - "description": "the chunk's place in its part, from 0", - "type": "integer" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer", - "part", - "sequence", - "last", - "data" - ], - "type": "object" - }, - "name": "publish-part", - "optional": true, - "output": { - "properties": { - "complete": { - "description": "the part is whole and its bytes match its digest", - "type": "boolean" - }, - "part": { - "description": "the part", - "type": "string" - }, - "received": { - "description": "the chunks of the part held so far", - "type": "integer" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer", - "part" - ], - "type": "object" - } - }, - { - "description": "The versions the seat holds of one name, newest first: each one's version, tag, digest, kind and when it was published.", - "input": { - "properties": { - "kind": { - "description": "narrowed to one kind (optional)", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "name": "versions", - "optional": true, - "output": { - "properties": { - "name": { - "description": "the name", - "type": "string" - }, - "versions": { - "description": "newest first", - "items": { - "description": "one published thing, as every registry event and answer names it", - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "name", - "kind", - "digest" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "name", - "versions" - ], - "type": "object" - } - }, - { - "description": "Remove one tagged version; the holder refuses an untagged one but for the retention removal of a bundle, archive or copied base by the artifact sweep's holder. Every removal emits removed with its reason. A tagged version the store cannot remove alone, because another tag names its digest, stays whole, its tag and bytes kept: the answer says kept with the reason shared-digest and the tags still naming the digest, removed false, and kept is emitted, never removed; the caller ends there without a retry.", - "input": { - "properties": { - "digest": { - "description": "the digest (for a bundle, archive or copied base)", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - }, - "reason": { - "description": "why it goes", - "enum": [ - "tag-gone", - "retention" - ], - "type": "string" - }, - "tag": { - "description": "the tag (for a tagged version)", - "type": "string" - }, - "version": { - "description": "the version", - "type": "string" - } - }, - "required": [ - "name", - "reason" - ], - "type": "object" - }, - "name": "remove", - "optional": true, - "output": { - "properties": { - "artifact": { - "description": "one published thing, as every registry event and answer names it", - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "name", - "kind", - "digest" - ], - "type": "object" - }, - "kept": { - "description": "it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)", - "type": "boolean" - }, - "reason": { - "description": "why it was kept: shared-digest, another tag names its digest", - "enum": [ - "shared-digest" - ], - "type": "string" - }, - "removed": { - "description": "it is gone", - "type": "boolean" - }, - "tags": { - "description": "the other tags still naming its digest, when kept", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "removed" - ], - "type": "object" - } - }, - { - "description": "Copy an outside image into the store by its pinned digest; emits published or refused.", - "input": { - "properties": { - "digest": { - "description": "its pinned digest, sha256:", - "type": "string" - }, - "name": { - "description": "the name it is kept under", - "type": "string" - }, - "source": { - "description": "the outside image's reference", - "type": "string" - } - }, - "required": [ - "source", - "digest", - "name" - ], - "type": "object" - }, - "name": "mirror", - "optional": true, - "output": { - "properties": { - "digest": { - "description": "the digest stored", - "type": "string" - }, - "reference": { - "description": "the reference the seat answers", - "type": "string" - } - }, - "required": [ - "reference", - "digest" - ], - "type": "object" - } - }, - { - "description": "The tags of one image and the digest each names.", - "input": { - "properties": { - "name": { - "description": "the image", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "name": "tags", - "optional": true, - "output": { - "properties": { - "name": { - "description": "the image", - "type": "string" - }, - "tags": { - "description": "every tag", - "items": { - "properties": { - "digest": { - "description": "its digest", - "type": "string" - }, - "tag": { - "description": "the tag", - "type": "string" - } - }, - "required": [ - "tag", - "digest" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "name", - "tags" - ], - "type": "object" - } - }, - { - "description": "Keep one untagged bundle, archive or copied base from removal by retention, as the artifact sweep's rules keep it (ADR 0617 §2); never an image of the holder's own. Emits held.", - "input": { - "properties": { - "digest": { - "description": "the digest", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - }, - "reason": { - "description": "which of the sweep's rules keeps it", - "type": "string" - } - }, - "required": [ - "name", - "digest", - "reason" - ], - "type": "object" - }, - "name": "hold", - "optional": true, - "output": { - "properties": { - "held": { - "description": "it is held", - "type": "boolean" - }, - "since": { - "description": "when the hold began, RFC 3339", - "type": "string" - } - }, - "required": [ - "held" - ], - "type": "object" - } - }, - { - "description": "Let a hold go; emits released. A release of what is not held answers released false and emits nothing, never an error: the outcome asked holds already. By hand only through the holder's own tool, the operator's session as the proved caller, recorded as a hand act.", - "input": { - "properties": { - "digest": { - "description": "the digest", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - }, - "reason": { - "description": "why it is let go", - "type": "string" - } - }, - "required": [ - "name", - "digest", - "reason" - ], - "type": "object" - }, - "name": "release", - "optional": true, - "output": { - "properties": { - "released": { - "description": "a hold was let go; false when there was none", - "type": "boolean" - } - }, - "required": [ - "released" - ], - "type": "object" - } - }, - { - "description": "What the store holds of one repository, or of every repository the seat holds when none is named: every image manifest and index, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).", - "input": { - "properties": { - "name": { - "description": "the repository, /; every repository of the seat when not given", - "type": "string" - } - }, - "type": "object" - }, - "name": "list", - "optional": true, - "output": { - "properties": { - "repositories": { - "description": "each repository named, in order of its name", - "items": { - "properties": { - "manifests": { - "description": "each manifest it holds, by digest", - "items": { - "properties": { - "artifact": { - "description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by", - "type": "string" - }, - "digest": { - "description": "the manifest's digest, sha256:", - "type": "string" - }, - "held": { - "description": "a hold keeps it from retention", - "type": "boolean" - }, - "index": { - "description": "it is an index, naming a manifest per platform", - "type": "boolean" - }, - "platforms": { - "description": "for an index, the digests of the manifests it names", - "items": { - "type": "string" - }, - "type": "array" - }, - "size": { - "description": "its bytes: its own content and every blob it names", - "type": "integer" - }, - "tags": { - "description": "the tags naming it, none for an untagged one", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "digest", - "index", - "size", - "held" - ], - "type": "object" - }, - "type": "array" - }, - "name": { - "description": "the repository", - "type": "string" - } - }, - "required": [ - "name", - "manifests" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "repositories" - ], - "type": "object" - } - } - ] - }, - { - "decision": "novox/hq ADR 0605", - "emits": [ - "published", - "refused", - "removed", - "kept", - "held", - "released" - ], - "grain": "whole", - "name": "oci-artifact-registry", - "scope": "mesh", - "serves": [ - { - "description": "Open a transfer, naming the artifact and the digest of every part; answers the digests the holder lacks, which the caller then sends by publish-part. A transfer already open answers the parts held, so a cut transfer goes on from there. With no parts and a version, the held digest is published under that version and nothing is stored (the trunk's claim).", - "input": { - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "parts": { - "description": "every part's digest and size; none when the digest is already held", - "items": { - "description": "one part of a transfer", - "properties": { - "digest": { - "description": "the part's digest, sha256:: an image's layer or config, a package's file, a bundle's blob", - "type": "string" - }, - "size": { - "description": "its size in bytes", - "type": "integer" - } - }, - "required": [ - "digest", - "size" - ], - "type": "object" - }, - "type": "array" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "transfer": { - "description": "the transfer's name, the caller's own; the same name opens the same transfer again", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "transfer", - "name", - "kind", - "digest" - ], - "type": "object" - }, - "name": "publish", - "optional": true, - "output": { - "properties": { - "held": { - "description": "the digest is held already: nothing to send; the outcome follows as an event", - "type": "boolean" - }, - "lacks": { - "description": "the digests of the parts the holder lacks, to send by publish-part", - "items": { - "type": "string" - }, - "type": "array" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer" - ], - "type": "object" - } - }, - { - "description": "Send one chunk of one part of an open transfer; the last chunk of the last part ends the transfer, and the holder hashes what it received, asks builder_versions, stores it and says published or refused. A chunk's message stays under the bus's largest message (1 MiB), so at most 760 KiB of data, base64.", - "input": { - "properties": { - "data": { - "description": "the chunk's bytes, base64 (at most 760 KiB before encoding)", - "type": "string" - }, - "last": { - "description": "this chunk is the part's last", - "type": "boolean" - }, - "part": { - "description": "the part's digest", - "type": "string" - }, - "sequence": { - "description": "the chunk's place in its part, from 0", - "type": "integer" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer", - "part", - "sequence", - "last", - "data" - ], - "type": "object" - }, - "name": "publish-part", - "optional": true, - "output": { - "properties": { - "complete": { - "description": "the part is whole and its bytes match its digest", - "type": "boolean" - }, - "part": { - "description": "the part", - "type": "string" - }, - "received": { - "description": "the chunks of the part held so far", - "type": "integer" - }, - "transfer": { - "description": "the transfer", - "type": "string" - } - }, - "required": [ - "transfer", - "part" - ], - "type": "object" - } - }, - { - "description": "The versions the seat holds of one name, newest first: each one's version, tag, digest, kind and when it was published.", - "input": { - "properties": { - "kind": { - "description": "narrowed to one kind (optional)", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "name": "versions", - "optional": true, - "output": { - "properties": { - "name": { - "description": "the name", - "type": "string" - }, - "versions": { - "description": "newest first", - "items": { - "description": "one published thing, as every registry event and answer names it", - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "name", - "kind", - "digest" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "name", - "versions" - ], - "type": "object" - } - }, - { - "description": "Remove one tagged version; the holder refuses an untagged one but for the retention removal of a bundle, archive or copied base by the artifact sweep's holder. Every removal emits removed with its reason. A tagged version the store cannot remove alone, because another tag names its digest, stays whole, its tag and bytes kept: the answer says kept with the reason shared-digest and the tags still naming the digest, removed false, and kept is emitted, never removed; the caller ends there without a retry.", - "input": { - "properties": { - "digest": { - "description": "the digest (for a bundle, archive or copied base)", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - }, - "reason": { - "description": "why it goes", - "enum": [ - "tag-gone", - "retention" - ], - "type": "string" - }, - "tag": { - "description": "the tag (for a tagged version)", - "type": "string" - }, - "version": { - "description": "the version", - "type": "string" - } - }, - "required": [ - "name", - "reason" - ], - "type": "object" - }, - "name": "remove", - "optional": true, - "output": { - "properties": { - "artifact": { - "description": "one published thing, as every registry event and answer names it", - "properties": { - "commit": { - "description": "the source commit it was built from", - "type": "string" - }, - "digest": { - "description": "its digest, sha256: (a Go package's h1: hash beside it in the version's record)", - "type": "string" - }, - "fingerprint": { - "description": "the build's fingerprint", - "type": "string" - }, - "kind": { - "description": "what it is", - "enum": [ - "package", - "image", - "bundle", - "archive", - "upstream" - ], - "type": "string" - }, - "name": { - "description": "the package's, image's, bundle's or archive's name; a package's is its name in its own language (design 32): a Go package's the go.mod module path (git.novox.be/novox/mesh-sdk/go), an npm package's package.json's name (@novox/mesh-sdk)", - "type": "string" - }, - "tag": { - "description": "the tag it carries, for a tagged version", - "type": "string" - }, - "version": { - "description": "the version the builder gave it (design 51 *The versions*); empty for a bundle or archive published by its digest alone", - "type": "string" - } - }, - "required": [ - "name", - "kind", - "digest" - ], - "type": "object" - }, - "kept": { - "description": "it stays whole, its tag and its bytes: the store cannot remove the tag alone while another tag names its digest (reason, tags)", - "type": "boolean" - }, - "reason": { - "description": "why it was kept: shared-digest, another tag names its digest", - "enum": [ - "shared-digest" - ], - "type": "string" - }, - "removed": { - "description": "it is gone", - "type": "boolean" - }, - "tags": { - "description": "the other tags still naming its digest, when kept", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "removed" - ], - "type": "object" - } - }, - { - "description": "Keep one untagged bundle, archive or copied base from removal by retention, as the artifact sweep's rules keep it (ADR 0617 §2); never an image of the holder's own. Emits held.", - "input": { - "properties": { - "digest": { - "description": "the digest", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - }, - "reason": { - "description": "which of the sweep's rules keeps it", - "type": "string" - } - }, - "required": [ - "name", - "digest", - "reason" - ], - "type": "object" - }, - "name": "hold", - "optional": true, - "output": { - "properties": { - "held": { - "description": "it is held", - "type": "boolean" - }, - "since": { - "description": "when the hold began, RFC 3339", - "type": "string" - } - }, - "required": [ - "held" - ], - "type": "object" - } - }, - { - "description": "Let a hold go; emits released. A release of what is not held answers released false and emits nothing, never an error: the outcome asked holds already. By hand only through the holder's own tool, the operator's session as the proved caller, recorded as a hand act.", - "input": { - "properties": { - "digest": { - "description": "the digest", - "type": "string" - }, - "name": { - "description": "the name", - "type": "string" - }, - "reason": { - "description": "why it is let go", - "type": "string" - } - }, - "required": [ - "name", - "digest", - "reason" - ], - "type": "object" - }, - "name": "release", - "optional": true, - "output": { - "properties": { - "released": { - "description": "a hold was let go; false when there was none", - "type": "boolean" - } - }, - "required": [ - "released" - ], - "type": "object" - } - }, - { - "description": "What the store holds of one repository, or of every repository the seat holds when none is named: every manifest keeping one bundle or archive, each with its digest, whether it is an index (and the manifests an index names), the tags naming it, its size and whether it is held. Read from the store itself, so a manifest no tag names is listed too; it reads only (ADR 0632 §3).", - "input": { - "properties": { - "name": { - "description": "the repository, /; every repository of the seat when not given", - "type": "string" - } - }, - "type": "object" - }, - "name": "list", - "optional": true, - "output": { - "properties": { - "repositories": { - "description": "each repository named, in order of its name", - "items": { - "properties": { - "manifests": { - "description": "each manifest it holds, by digest", - "items": { - "properties": { - "artifact": { - "description": "for a manifest keeping one bundle or archive, the digest of that blob: what a reference names it by", - "type": "string" - }, - "digest": { - "description": "the manifest's digest, sha256:", - "type": "string" - }, - "held": { - "description": "a hold keeps it from retention", - "type": "boolean" - }, - "index": { - "description": "it is an index, naming a manifest per platform", - "type": "boolean" - }, - "platforms": { - "description": "for an index, the digests of the manifests it names", - "items": { - "type": "string" - }, - "type": "array" - }, - "size": { - "description": "its bytes: its own content and every blob it names", - "type": "integer" - }, - "tags": { - "description": "the tags naming it, none for an untagged one", - "items": { - "type": "string" - }, - "type": "array" - } - }, - "required": [ - "digest", - "index", - "size", - "held" - ], - "type": "object" - }, - "type": "array" - }, - "name": { - "description": "the repository", - "type": "string" - } - }, - "required": [ - "name", - "manifests" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "repositories" - ], - "type": "object" - } - } - ] - }, - { - "decision": "novox/hq ADR 0111", - "delivers": "git", - "grain": "seat-list", - "name": "git", - "scope": "mesh" - }, - { - "accepts": [ - "delivery", - "check", - "recheck" - ], - "decision": "novox/hq ADR 0525, ADR 0536, ADR 0553", - "grain": "seat-list", - "name": "node-build-go", - "pulledByKind": true, - "scope": "node", - "serves": [ - { - "description": "The Go asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "current" - }, - { - "description": "Take no new Go ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "pause" - }, - { - "description": "Take Go asks again on this node.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "resume" - } - ], - "yields": true - }, - { - "accepts": [ - "delivery", - "check", - "recheck" - ], - "decision": "novox/hq ADR 0525, ADR 0536, ADR 0553", - "grain": "seat-list", - "name": "node-build-node", - "pulledByKind": true, - "scope": "node", - "serves": [ - { - "description": "The Node asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "current" - }, - { - "description": "Take no new Node ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "pause" - }, - { - "description": "Take Node asks again on this node.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "resume" - } - ], - "yields": true - }, - { - "accepts": [ - "delivery", - "check", - "recheck" - ], - "decision": "novox/hq ADR 0525, ADR 0536, ADR 0553", - "grain": "seat-list", - "name": "node-build-image", - "pulledByKind": true, - "scope": "node", - "serves": [ - { - "description": "The image asks this node runs now (each ask's id, kind, module and when it started), its build capacity, and whether it is paused, taking no new ask.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "current" - }, - { - "description": "Take no new image ask on this node until resumed; an ask running here ends in its place. Kept across a restart of the build module (its pause key). A capacity of 0 is the lasting way to keep a node from building.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "pause" - }, - { - "description": "Take image asks again on this node.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "resume" - } - ], - "yields": true - }, - { - "decision": "novox/hq ADR 0194, ADR 0223", - "delivers": "wildcard-resolution", - "grain": "seat-list", - "name": "mesh-dns-resolver", - "replicated": true, - "scope": "mesh" - }, - { - "decision": "novox/hq ADR 0199, ADR 0223", - "grain": "seat-list", - "name": "node-hostname", - "scope": "node", - "serves": [ - { - "description": "Every line of this machine's /etc/hosts, each marked whose it is: the operator's, or the block of the module or tool that writes it.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "entries", - "replaces": [ - "cat /etc/hosts", - "getent hosts" - ] - }, - { - "description": "Add one address and its names to the operator's lines of this machine's /etc/hosts — a name for this machine's own programs, not the mesh's.", - "input": { - "properties": { - "address": { - "description": "the IPv4 or IPv6 address", - "type": "string" - }, - "names": { - "description": "the names for it, separated by spaces", - "type": "string" - } - }, - "required": [ - "address", - "names" - ], - "type": "object" - }, - "name": "add", - "replaces": [ - "edit /etc/hosts", - "HOSTALIASES" - ] - }, - { - "description": "Remove one name, or every line of one address, from the operator's lines of this machine's /etc/hosts. A line a module writes is refused, naming the module.", - "input": { - "properties": { - "name": { - "description": "a host name, or an address to remove every line of", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "name": "remove", - "replaces": [ - "sed -i /etc/hosts" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0121", - "grain": "seat-list", - "name": "node-intrusion-prevention", - "scope": "node", - "serves": [ - { - "description": "Every jail on this machine with how many it is watching and holding now, and the totals since the jail started; one jail's detail when named.", - "input": { - "properties": { - "jail": { - "description": "one jail (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "status", - "replaces": [ - "fail2ban-client status" - ] - }, - { - "description": "Every address banned on this machine right now, with the jail that holds it and when the ban ends.", - "input": { - "properties": { - "jail": { - "description": "one jail (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "banned", - "replaces": [ - "fail2ban-client banned" - ] - }, - { - "description": "Ban one address in one jail now, for the jail's ban time — an operator's act on the live ban list, which the mesh never writes itself.", - "input": { - "properties": { - "ip": { - "description": "the address", - "type": "string" - }, - "jail": { - "description": "the jail to hold it", - "type": "string" - } - }, - "required": [ - "ip", - "jail" - ], - "type": "object" - }, - "name": "ban", - "replaces": [ - "fail2ban-client set banip" - ] - }, - { - "description": "Let one address go, from one jail or from every jail when none is named.", - "input": { - "properties": { - "ip": { - "description": "the address", - "type": "string" - }, - "jail": { - "description": "one jail (optional)", - "type": "string" - } - }, - "required": [ - "ip" - ], - "type": "object" - }, - "name": "unban", - "replaces": [ - "fail2ban-client unban", - "fail2ban-client set unbanip" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0121", - "grain": "seat-list", - "name": "node-packet-filter", - "scope": "node", - "serves": [ - { - "description": "The packet filter as this machine enforces it now: the nftables ruleset and, where the tool exists, the legacy filter's listings. Narrowed to one table or chain when asked.", - "input": { - "properties": { - "chain": { - "description": "one chain of that table (optional)", - "type": "string" - }, - "table": { - "description": "one nftables table, as `family name` (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "rules", - "replaces": [ - "nft list ruleset", - "iptables -L", - "iptables-save" - ] - }, - { - "description": "Load the mesh's own filter again from the file the mesh writes, and answer with the mesh's table as loaded.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "reload", - "replaces": [ - "nft -f" - ] - }, - { - "description": "Remove one rule set the mesh did not write, named exactly as the host reports it (novox/hq ADR 0168) — `chain X (iptables-legacy)` or `table ip6 filter, chain DOCKER-USER`. Refuses the mesh's tables, the runtime's own chains, a built-in chain and an active found firewall's chains. An operator's act, by name, never a flush.", - "input": { - "properties": { - "where": { - "description": "the rule set, as `node show` lists it", - "type": "string" - } - }, - "required": [ - "where" - ], - "type": "object" - }, - "name": "remove", - "replaces": [ - "nft delete", - "iptables -X" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0177", - "grain": "seat-list", - "name": "node-service-manager", - "scope": "node", - "serves": [ - { - "description": "The units the service manager knows in a scope, each with its load, active and sub state; narrowed to a pattern when asked.", - "input": { - "properties": { - "pattern": { - "description": "a glob the unit's name must match (optional)", - "type": "string" - }, - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - } - }, - "type": "object" - }, - "name": "units", - "replaces": [ - "systemctl list-units" - ] - }, - { - "description": "One unit as the service manager sees it now: its states, whether it starts at boot, its main process, and whether the mesh declares it.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "name": "status", - "replaces": [ - "systemctl status", - "systemctl is-active" - ] - }, - { - "description": "Start one unit. For a unit the mesh declares, the answer says the host will restore what its declaration says at the next apply.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "name": "start", - "replaces": [ - "systemctl start" - ] - }, - { - "description": "Stop one unit; for a mesh-declared unit the answer says the host will restore its declared state.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "name": "stop", - "replaces": [ - "systemctl stop" - ] - }, - { - "description": "Restart one unit.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "name": "restart", - "replaces": [ - "systemctl restart" - ] - }, - { - "description": "Make one unit start at boot (or at the account's login, in user scope).", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "name": "enable", - "replaces": [ - "systemctl enable" - ] - }, - { - "description": "Stop one unit starting at boot (or at login, in user scope).", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "name": "disable", - "replaces": [ - "systemctl disable" - ] - }, - { - "description": "The last lines of one unit's journal (at most 2000), in a time window and narrowed to a priority and to lines holding a text when asked. A secret the unit printed is shown as [redacted: ].", - "input": { - "properties": { - "lines": { - "description": "how many lines from the end of what matches (default 100, at most 2000)", - "type": "string" - }, - "match": { - "description": "only the lines holding this text, as written — a fixed string, not a pattern (optional)", - "type": "string" - }, - "priority": { - "description": "only entries this severe or more: 0-7 or emerg, alert, crit, err, warning, notice, info, debug (optional)", - "type": "string" - }, - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "since": { - "description": "the window's start: an RFC 3339 time (2026-10-07T09:30:00Z) or relative to now (-30min, -2h, yesterday) (optional)", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - }, - "until": { - "description": "the window's end, in the same forms (optional; now when absent)", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "name": "journal", - "replaces": [ - "journalctl" - ] - }, - { - "description": "Every failed unit on this machine, in the system manager and in the operator account's; a manager that does not answer is reported with its error, never as nothing failed.", - "input": { - "properties": { - "scope": { - "description": "\"system\" or \"user\": only that manager (both when absent)", - "type": "string" - } - }, - "type": "object" - }, - "name": "failed", - "optional": true, - "replaces": [ - "systemctl --failed" - ] - }, - { - "description": "Clear one unit's failed record in its manager — its failed state and its start-limit count — and answer its state after. It removes no file and starts nothing.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "name": "reset-failed", - "replaces": [ - "systemctl reset-failed" - ] - }, - { - "description": "What wants, requires or triggers one unit, read-only: the manager's reverse dependencies (WantedBy, RequiredBy, UpheldBy, BoundBy, TriggeredBy and the rest) and every enable link naming it in the scope's configuration directories (*.wants, *.requires, *.upholds), each with where it points and whether it dangles — the account's own directory included in user scope.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - } - }, - "required": [ - "unit" - ], - "type": "object" - }, - "name": "wanted-by", - "replaces": [ - "systemctl list-dependencies --reverse", - "systemctl show -p WantedBy", - "ls ~/.config/systemd/user/*.wants" - ] - }, - { - "description": "Remove the dangling enable links named for one unit — links in a *.wants, *.requires or *.upholds directory of the account's or the machine's own configuration whose target does not exist, which `disable` leaves once the unit's file is gone — on the person's word: `why` is required. Each is recorded (its path, its target, the reason) before it is removed, and the manager reloaded; a link whose target exists, or one a package, a generator or the runtime placed, is left and said.", - "input": { - "properties": { - "scope": { - "description": "\"system\" (the default) or \"user\": the operator account's own manager", - "type": "string" - }, - "unit": { - "description": "the unit's name, as the service manager knows it", - "type": "string" - }, - "why": { - "description": "the person's reason for removing the links, kept in the record", - "type": "string" - } - }, - "required": [ - "unit", - "why" - ], - "type": "object" - }, - "name": "unlink-dangling", - "replaces": [ - "rm ~/.config/systemd/user/*.wants/", - "unlink /etc/systemd/system/*.wants/" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0207", - "grain": "seat-list", - "name": "node-package-manager", - "scope": "node" - }, - { - "decision": "novox/hq ADR 0166, ADR 0207", - "grain": "seat-list", - "name": "node-container-runtime", - "scope": "node" - }, - { - "decision": "novox/hq ADR 0203", - "grain": "seat-list", - "name": "node-environment", - "scope": "node" - }, - { - "decision": "novox/hq ADR 0349", - "grain": "seat-list", - "name": "node-provisioner", - "scope": "node" - }, - { - "decision": "novox/hq ADR 0204", - "grain": "seat-list", - "name": "node-login-shell", - "scope": "node", - "serves": [ - { - "description": "Run one command on this machine as the operator account, in a non-interactive login shell in its home; answers with what it printed and how it exited.", - "input": { - "properties": { - "command": { - "description": "the command line, as you would type it", - "type": "string" - }, - "timeout_seconds": { - "description": "give up after this long, at most 25 (default 20)", - "type": "string" - } - }, - "required": [ - "command" - ], - "type": "object" - }, - "name": "execute", - "optional": true - } - ] - }, - { - "decision": "novox/hq ADR 0211", - "grain": "seat-list", - "name": "node-power", - "scope": "node" - }, - { - "decision": "novox/hq ADR 0212", - "grain": "seat-list", - "name": "node-hotkeys", - "scope": "node" - }, - { - "decision": "novox/hq ADR 0215", - "grain": "seat-list", - "name": "node-message-bus", - "scope": "node" - }, - { - "decision": "novox/hq ADR 0214", - "grain": "seat-list", - "name": "node-backup", - "scope": "node", - "serves": [ - { - "description": "What this machine backs up: each module, what it declared, its last good night, how many restore points are kept and the repository's size.", - "input": { - "properties": { - "module": { - "description": "one module (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "backed-up", - "replaces": [ - "restic snapshots" - ] - }, - { - "description": "Take a backup now, of one module or of every module on this machine — before a migration, a retirement or anything else that could go wrong.", - "input": { - "properties": { - "module": { - "description": "one module (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "now", - "replaces": [ - "restic backup" - ] - }, - { - "description": "Restore one module's data from a restore point BESIDE the live data, never over it: each directory as .restored-. Swapping it in is a person's act. Lists the restore points when none is named.", - "input": { - "properties": { - "module": { - "description": "the module", - "type": "string" - }, - "path": { - "description": "one of the module's directories (all of them when omitted)", - "type": "string" - }, - "snapshot": { - "description": "the restore point (from `backed-up`; the newest when omitted)", - "type": "string" - } - }, - "required": [ - "module" - ], - "type": "object" - }, - "name": "restore", - "replaces": [ - "restic restore" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0110", - "grain": "seat-list", - "name": "the-private-network", - "scope": "node" - }, - { - "decision": "novox/hq ADR 0117", - "grain": "seat-list", - "name": "node-uplink", - "scope": "node", - "serves": [ - { - "description": "The machine's resolver file as it is now: the resolvers, search domains and options it lists, whether it is the file the mesh declares, and when it is not, who wrote it as far as the machine shows — its header, a backup a VPN client left beside it, a link in its place, a known writer running.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "resolvers", - "optional": true - }, - { - "description": "Every network link on the machine: its state, its addresses, whether the default route leaves through it, and the resolvers and search domains the network manager knows for it — a VPN's tunnel included.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "links", - "optional": true - } - ] - }, - { - "decision": "novox/hq ADR 0247", - "grain": "seat-list", - "name": "node-resolver", - "scope": "node", - "serves": [ - { - "description": "What this machine's own resolver sends where: the mesh's resolvers, which answer every name not routed elsewhere, and each link given servers of its own with the domains routed to them. Also the resolver file's outside writes it kept, newest first: when, who wrote it as far as the file says, whether a module took it, and when the resolver's own file was put back.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "routes", - "replaces": [ - "resolvectl status", - "resolvectl dns", - "resolvectl domain" - ] - }, - { - "description": "Send these domains, and every name under them, to these servers over this link — and only them: the link is never the machine's default route for names, and the mesh's own domain is refused. Replaces whatever the link was given before. A link that goes away takes its route with it.", - "input": { - "properties": { - "domains": { - "description": "the domains to route there, separated by spaces or commas", - "type": "string" - }, - "link": { - "description": "the network link the servers are reached over, by name (a VPN's tunnel interface)", - "type": "string" - }, - "servers": { - "description": "the servers' addresses, separated by spaces or commas", - "type": "string" - } - }, - "required": [ - "link", - "domains", - "servers" - ], - "type": "object" - }, - "name": "route", - "replaces": [ - "resolvectl dns", - "resolvectl domain", - "resolvectl default-route" - ] - }, - { - "description": "Take one link's route away: its domains go back to the mesh's resolvers. Nothing changes when the link has none.", - "input": { - "properties": { - "link": { - "description": "the network link, by name", - "type": "string" - } - }, - "required": [ - "link" - ], - "type": "object" - }, - "name": "unroute", - "replaces": [ - "resolvectl revert" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0263", - "grain": "seat-list", - "name": "node-nfs-server", - "scope": "node", - "serves": [ - { - "description": "Every share this machine exports: its name, its path, read-write or read-only, the owner every client is mapped to (uid and gid), each node it is exported to with that node's private address and access (only the nodes the server grants it to and that ask for it), what is granted and not asked for or asked for and not granted, and whether the kernel holds it now. Also the exports found that are not the mesh's: a dataset's sharenfs property, a line in /etc/exports.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "exports", - "replaces": [ - "exportfs -v", - "cat /etc/exports", - "zfs get sharenfs" - ] - }, - { - "description": "Which machines have mounted which share now, as the NFS server knows its clients.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "clients", - "replaces": [ - "ss -tn sport = :2049", - "cat /proc/fs/nfsd/clients/*/info" - ] - }, - { - "description": "Whether this machine exports one share for the mesh now, and whether its NFS service is up; with an address, also whether the share is exported to that address: a node's own private address, when the server grants it the share and the node asks for it. What a machine mounting the share asks before it mounts.", - "input": { - "properties": { - "address": { - "description": "a node's private address, to ask whether the share is exported to it (optional)", - "type": "string" - }, - "share": { - "description": "the share, by its name", - "type": "string" - } - }, - "required": [ - "share" - ], - "type": "object" - }, - "name": "test", - "replaces": [ - "showmount -e" - ] - }, - { - "description": "Have the kernel read this machine's export files again now (exportfs -ra) and answer the shares as it then holds them. The module's own process writes its export file; this is for after a change made outside it. Changes no shared path.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "reload", - "replaces": [ - "exportfs -ra" - ] - }, - { - "description": "Take over an export a person made by hand: clear a dataset's sharenfs property for a path the mesh's own export now serves — only when that export is live. Without confirm, says what it would do and changes nothing.", - "input": { - "properties": { - "confirm": { - "description": "\"true\": change it (needs why); anything else is a dry run", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "path": { - "description": "the shared path whose hand-made export is taken over", - "type": "string" - }, - "why": { - "description": "why, for the record", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "name": "adopt", - "replaces": [ - "zfs set sharenfs=off" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0263", - "grain": "seat-list", - "name": "node-mounts", - "scope": "node", - "serves": [ - { - "description": "Every mount point on this machine: its fstab line, the mesh's mount and automount units for it, its state (armed, mounted, absent, unreachable, failed) and since when, and which settings asked for it. A password in a mount's options is never shown.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "list", - "replaces": [ - "cat /etc/fstab", - "findmnt", - "systemctl list-units --type=mount" - ] - }, - { - "description": "Whether one share's or occasional source's server answers from this machine now, without touching its mount point.", - "input": { - "properties": { - "name": { - "description": "the share or source, by its name", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "name": "test", - "replaces": [ - "showmount -e", - "ping" - ] - }, - { - "description": "Mount one share or occasional source now, rather than at its first access.", - "input": { - "properties": { - "name": { - "description": "the share or source, by its name", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "name": "mount", - "replaces": [ - "mount" - ] - }, - { - "description": "Release one share or occasional source now; its automount stays armed, so the next access mounts it again.", - "input": { - "properties": { - "name": { - "description": "the share or source, by its name", - "type": "string" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "name": "unmount", - "replaces": [ - "umount" - ] - }, - { - "description": "Take over a mount a person wrote by hand: comment out the /etc/fstab line for a mount point the mesh's own units now serve, keeping a copy of the file — only when the mesh's automount for it is armed. Without confirm, says what it would do and changes nothing.", - "input": { - "properties": { - "confirm": { - "description": "\"true\": change it (needs why); anything else is a dry run", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "mountpoint": { - "description": "the mount point whose fstab line is taken over", - "type": "string" - }, - "why": { - "description": "why, for the record", - "type": "string" - } - }, - "required": [ - "mountpoint" - ], - "type": "object" - }, - "name": "adopt", - "replaces": [ - "sed -i /etc/fstab" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0310", - "grain": "seat-list", - "name": "node-reads", - "scope": "node", - "serves": [ - { - "description": "Each component of one path or several, from / down: its type, owner and group (name and number), mode, whether it is a link and to what, and whether it is a mount point and of which file system. A link is not followed unless follow is \"true\". Read as the holder's own account, which holds no secret: a component it may not reach says so. At most 200 paths in one call.", - "input": { - "properties": { - "follow": { - "description": "\"true\": also say where the path leads once every link is followed", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "paths": { - "description": "one absolute path, or several separated by line breaks", - "type": "string" - } - }, - "required": [ - "paths" - ], - "type": "object" - }, - "name": "stat", - "replaces": [ - "stat", - "ls -ld", - "namei -l", - "findmnt -T" - ] - }, - { - "description": "What one link points to, as written, and where the path leads once every link is followed, and whether that exists.", - "input": { - "properties": { - "path": { - "description": "an absolute path", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "name": "readlink", - "replaces": [ - "readlink", - "readlink -f", - "realpath" - ] - }, - { - "description": "Every entry of one directory with its type, owner, group, mode, size and, for a link, its target. At most 2000 entries; a longer directory is cut and says so. A place a secret is kept is refused by name.", - "input": { - "properties": { - "path": { - "description": "an absolute path to a directory", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "name": "list", - "replaces": [ - "ls", - "ls -la" - ] - }, - { - "description": "One text file, or a part of it by line: at most 256 KiB in one answer (64 KiB unless more is asked), cut at a line and said so. A binary file is refused; a place a secret is kept, or a known credential file, is refused by name; a line shaped like a secret is withheld and its number said. /proc and /sys are read as far as the holder's account may.", - "input": { - "properties": { - "bytes": { - "description": "the most bytes to answer, up to 262144 (optional, 65536 unless given)", - "type": "string" - }, - "from": { - "description": "the first line to answer, counted from 1 (optional)", - "type": "string" - }, - "lines": { - "description": "how many lines to answer (optional)", - "type": "string" - }, - "path": { - "description": "an absolute path to a file", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "name": "read", - "replaces": [ - "cat", - "head", - "tail", - "less" - ] - }, - { - "description": "Find a fixed string (never a pattern) in one file or under one directory: every matching line with its file and number. Bounded: at most 500 matches (100 unless more is asked), 8 directories deep (4 unless more is asked), 5000 files and 4 MiB per file; links are not followed and binary files are skipped. A place a secret is kept is skipped and named; a matching line shaped like a secret is withheld. The answer says which bound it met.", - "input": { - "properties": { - "case": { - "description": "\"false\": match whatever the case (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "depth": { - "description": "how many directories deep, up to 8 (optional)", - "type": "string" - }, - "matches": { - "description": "the most matches to answer, up to 500 (optional)", - "type": "string" - }, - "path": { - "description": "an absolute path to a file or a directory", - "type": "string" - }, - "text": { - "description": "the fixed string to find", - "type": "string" - } - }, - "required": [ - "text", - "path" - ], - "type": "object" - }, - "name": "search", - "replaces": [ - "grep", - "grep -r", - "grep -F" - ] - }, - { - "description": "One account or group by name or number, as this node's account files say: an account's number, group, home, shell and every group it is in; a group's number and members. With neither, every account and group. Never a password field.", - "input": { - "properties": { - "group": { - "description": "a group's name or number (optional)", - "type": "string" - }, - "name": { - "description": "an account's name or number (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "account", - "replaces": [ - "getent passwd", - "getent group", - "id", - "groups" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0394", - "grain": "seat-list", - "name": "node-files", - "scope": "node", - "serves": [ - { - "description": "Each component of one path or several, from / down: its type, owner and group (name and number), mode, whether it is a link and to what, and whether it is a mount point and of which file system. A link is not followed unless follow is \"true\". At most 200 paths in one call. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "follow": { - "description": "\"true\": also say where the path leads once every link is followed", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "paths": { - "description": "one absolute path, or several separated by line breaks", - "type": "string" - } - }, - "required": [ - "paths" - ], - "type": "object" - }, - "name": "stat", - "replaces": [ - "stat", - "ls -ld", - "namei -l", - "findmnt -T" - ] - }, - { - "description": "What one link points to, as written, and where the path leads once every link is followed, and whether that exists. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "path": { - "description": "an absolute path", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "name": "readlink", - "replaces": [ - "readlink", - "readlink -f", - "realpath" - ] - }, - { - "description": "Every entry of one directory with its type, owner, group, mode, size and, for a link, its target. At most 2000 entries; a longer directory is cut and says so. A place a secret is kept is refused by name. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "path": { - "description": "an absolute path to a directory", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "name": "list", - "replaces": [ - "ls", - "ls -la" - ] - }, - { - "description": "One text file, or a part of it by line: at most 256 KiB in one answer (64 KiB unless more is asked), cut at a line and said so. A binary file is refused; a place a secret is kept, or a known credential file, is refused by name; a line shaped like a secret is withheld and its number said. /proc and /sys are read as far as the holder's account may. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "bytes": { - "description": "the most bytes to answer, up to 262144 (optional, 65536 unless given)", - "type": "string" - }, - "from": { - "description": "the first line to answer, counted from 1 (optional)", - "type": "string" - }, - "lines": { - "description": "how many lines to answer (optional)", - "type": "string" - }, - "path": { - "description": "an absolute path to a file", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "name": "read", - "replaces": [ - "cat", - "head", - "tail", - "less" - ] - }, - { - "description": "Find a fixed string (never a pattern) in one file or under one directory: every matching line with its file and number. Bounded: at most 500 matches (100 unless more is asked), 8 directories deep (4 unless more is asked), 5000 files and 4 MiB per file; links are not followed and binary files are skipped. A place a secret is kept is skipped and named; a matching line shaped like a secret is withheld. The answer says which bound it met. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "case": { - "description": "\"false\": match whatever the case (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "depth": { - "description": "how many directories deep, up to 8 (optional)", - "type": "string" - }, - "matches": { - "description": "the most matches to answer, up to 500 (optional)", - "type": "string" - }, - "path": { - "description": "an absolute path to a file or a directory", - "type": "string" - }, - "text": { - "description": "the fixed string to find", - "type": "string" - } - }, - "required": [ - "text", - "path" - ], - "type": "object" - }, - "name": "search", - "replaces": [ - "grep", - "grep -r", - "grep -F" - ] - }, - { - "description": "One account or group by name or number, as this node's account files say: an account's number, group, home, shell and every group it is in; a group's number and members. With neither, every account and group. Never a password field. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "group": { - "description": "a group's name or number (optional)", - "type": "string" - }, - "name": { - "description": "an account's name or number (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "account", - "replaces": [ - "getent passwd", - "getent group", - "id", - "groups" - ] - }, - { - "description": "Write one text file whole, replacing what it holds; the old content goes to the node's trash. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "content": { - "description": "the text to write", - "type": "string" - }, - "mode": { - "description": "the file's mode, such as 0644 (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - } - }, - "required": [ - "path", - "content", - "reason" - ], - "type": "object" - }, - "name": "write", - "replaces": [ - "tee", - ">" - ] - }, - { - "description": "Copy a file or directory to a path on this node. A target on another node (:/path) is refused: copying between nodes comes in step 2 (issue 903). An existing target is overwritten, the old one kept in the node's trash for 7 days. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "from": { - "description": "an absolute path", - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "to": { - "description": "an absolute path on this node", - "type": "string" - } - }, - "required": [ - "from", - "to", - "reason" - ], - "type": "object" - }, - "name": "copy", - "replaces": [ - "cp", - "cp -a", - "ssh cp" - ] - }, - { - "description": "Move or rename a file or directory. A target on another node is refused: copying between nodes comes in step 2 (issue 903). An existing target is overwritten, the old one kept in the node's trash for 7 days. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "from": { - "description": "an absolute path", - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "to": { - "description": "an absolute path", - "type": "string" - } - }, - "required": [ - "from", - "to", - "reason" - ], - "type": "object" - }, - "name": "move", - "replaces": [ - "mv" - ] - }, - { - "description": "Delete a file or directory into the node's trash, where restore can bring it back. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "recursive": { - "description": "\"true\": delete a directory with what it holds", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "required": [ - "path", - "reason" - ], - "type": "object" - }, - "name": "delete", - "replaces": [ - "rm", - "rm -r" - ] - }, - { - "description": "Make a directory. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "parents": { - "description": "\"true\": make the missing parents too", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - } - }, - "required": [ - "path", - "reason" - ], - "type": "object" - }, - "name": "mkdir", - "replaces": [ - "mkdir", - "mkdir -p" - ] - }, - { - "description": "Change the mode of a file or directory. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "mode": { - "description": "the mode, such as 0644 or u+x", - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "recursive": { - "description": "\"true\": also everything under a directory", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "required": [ - "path", - "mode", - "reason" - ], - "type": "object" - }, - "name": "chmod", - "replaces": [ - "chmod" - ] - }, - { - "description": "Change the owner and group of a file or directory. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "group": { - "description": "a group's name or number (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "owner": { - "description": "an account's name or number", - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "recursive": { - "description": "\"true\": also everything under a directory", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "required": [ - "path", - "owner", - "reason" - ], - "type": "object" - }, - "name": "chown", - "replaces": [ - "chown", - "chgrp" - ] - }, - { - "description": "Send one file of this node into a bucket of the object store, over a short signed link and never over the bus; answers the key (the file's SHA-256 when none is given), the size and the SHA-256. At most 100 MiB. A place a secret is kept, a known credential file, or a link to one is refused by name, before anything is sent. Read as the node's operator account, through sudo -n where its rights do not reach; a place a secret is kept, or a known credential file, is refused by name.", - "input": { - "properties": { - "bucket": { - "description": "the bucket the file goes into", - "type": "string" - }, - "key": { - "description": "the object's key (optional; the file's SHA-256 when not given)", - "type": "string" - }, - "path": { - "description": "an absolute path to a file", - "type": "string" - } - }, - "required": [ - "path", - "bucket" - ], - "type": "object" - }, - "name": "upload" - }, - { - "description": "Write one object of a bucket to a file of this node, over a short signed link and never over the bus; the old file goes to the node's trash. At most 100 MiB. The bytes must hash to sha256, or to the key when the key is a SHA-256; otherwise no file is written. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "bucket": { - "description": "the bucket", - "type": "string" - }, - "key": { - "description": "the object's key", - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "type": "string" - }, - "path": { - "description": "an absolute path", - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - }, - "sha256": { - "description": "the SHA-256 the bytes must have (optional; the key when that is a SHA-256)", - "type": "string" - } - }, - "required": [ - "bucket", - "key", - "path", - "reason" - ], - "type": "object" - }, - "name": "download" - }, - { - "description": "What the node's trash holds: each deleted or replaced item with its id, original path and when it went.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "trash", - "replaces": [ - "ls ~/.local/share/Trash" - ] - }, - { - "description": "Put an item of the node's trash back where it was; refuses when something is there now. Refuses a path a module owns unless override names a reason; done as the node's operator account, through sudo -n where its rights do not reach.", - "input": { - "properties": { - "id": { - "description": "an id from trash", - "type": "string" - }, - "override": { - "description": "a reason, to change a path a module owns anyway (optional)", - "enum": [ - "true", - "false" - ], - "type": "string" - }, - "reason": { - "description": "why the file is changed, for the audit trail", - "type": "string" - } - }, - "required": [ - "id", - "reason" - ], - "type": "object" - }, - "name": "restore", - "replaces": [ - "trash-restore" - ] - } - ] - }, - { - "decision": "novox/hq ADR 0563", - "emits": [ - "asked", - "done", - "refused", - "manifest-reads-changed" - ], - "grain": "whole", - "heldByTheEngine": true, - "name": "node-engine", - "scope": "node", - "serves": [ - { - "description": "Hand a directory this machine uses as found to the mesh (novox/hq ADR 0563 §3, §5): the engine judges the path as it does every hand-over (absolute, as it states it, used as found, reached through no link) and refuses in words, or asks the operator on their channel at the level approve, binding the owner, group and mode it will set. Nothing changes on the call: only the operator's yes hands it over. One ask is open per path; acts shows where it stands.", - "input": { - "properties": { - "path": { - "description": "the directory's absolute path, as the engine states it", - "type": "string" - }, - "why": { - "description": "why it is asked, in words the operator reads", - "type": "string" - } - }, - "required": [ - "path" - ], - "type": "object" - }, - "name": "hand-over", - "output": { - "description": "the answer of an act the operator is asked", - "properties": { - "asked": { - "description": "the ask's id: acts shows where it stands", - "type": "string" - }, - "node": { - "description": "the node", - "type": "string" - }, - "says": { - "description": "what happens next, in words", - "type": "string" - } - }, - "required": [ - "asked", - "node", - "says" - ], - "type": "object" - } - }, - { - "description": "Ask the operator for the take act on a library whose root holds data outside its group (novox/hq ADR 0328 §7, ADR 0563 §3, §4), on the library's home engine: the access as declared and its root as last declared, judged and refused in words or asked at the level approve. Only the operator's yes sets the library's group, group write and setgid under its root. One ask is open per access.", - "input": { - "properties": { - "access": { - "description": "the access, library:", - "type": "string" - }, - "act": { - "description": "the act: take", - "type": "string" - }, - "why": { - "description": "why it is asked, in words the operator reads", - "type": "string" - } - }, - "required": [ - "act", - "access" - ], - "type": "object" - }, - "name": "access-act", - "output": { - "description": "the answer of an act the operator is asked", - "properties": { - "asked": { - "description": "the ask's id: acts shows where it stands", - "type": "string" - }, - "node": { - "description": "the node", - "type": "string" - }, - "says": { - "description": "what happens next, in words", - "type": "string" - } - }, - "required": [ - "asked", - "node", - "says" - ], - "type": "object" - } - }, - { - "description": "Which of this machine's declared directories the engine would refuse to make or keep, one line each (novox/hq ADR 0563 §2). Read-only: changes no owner or mode.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "judge-directories", - "output": { - "description": "the answer of a read-only judgement", - "properties": { - "node": { - "description": "the node", - "type": "string" - }, - "said": { - "description": "the engine's judgement or search, one line per directory or program", - "type": "string" - } - }, - "required": [ - "node", - "said" - ], - "type": "object" - } - }, - { - "description": "Throw away the engine's last search for setuid programs and start a full one (novox/hq ADR 0563 §2); until it completes root-free answers that the machine is not judged yet. Refused while one runs, and when one was started within the hour, naming when the next may start.", - "input": { - "properties": { - "why": { - "description": "why it is asked, in words", - "type": "string" - } - }, - "type": "object" - }, - "name": "setuid-search", - "output": { - "description": "the answer of a read-only judgement", - "properties": { - "node": { - "description": "the node", - "type": "string" - }, - "said": { - "description": "the engine's judgement or search, one line per directory or program", - "type": "string" - } - }, - "required": [ - "node", - "said" - ], - "type": "object" - } - }, - { - "description": "Whether this machine is root-free (novox/hq ADR 0259 §8, ADR 0563 §6): free only on the engine's own verdict, at most fifteen minutes old, that the account agents run as cannot become root without a person, and the login shell's execute not served here. Anything else is no pass, and says why. Answered by this machine's engine alone.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "root-free", - "output": { - "properties": { - "machines": { - "description": "this machine's verdict", - "items": { - "description": "the engine's root-free verdict (ADR 0563 §6)", - "properties": { - "free": { - "description": "the machine is root-free", - "type": "boolean" - }, - "judged": { - "description": "when the engine judged it, RFC 3339", - "type": "string" - }, - "machine": { - "description": "the machine", - "type": "string" - }, - "why": { - "description": "why, in words", - "type": "string" - } - }, - "required": [ - "machine", - "free", - "why", - "judged" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "machines" - ], - "type": "object" - } - }, - { - "description": "Every act this machine's engine was asked through this seat (novox/hq ADR 0563 §9): each hand-over, access act and setuid search, open, done or refused, with who asked, the ask and the operator's warrant. Read-only.", - "input": { - "properties": { - "id": { - "description": "one act, by its id", - "type": "string" - }, - "limit": { - "description": "at most this many, newest", - "type": "string" - } - }, - "type": "object" - }, - "name": "acts", - "output": { - "description": "the acts kept", - "properties": { - "acts": { - "description": "the acts, newest first", - "items": { - "description": "one act asked through the seat, as acts reads it (ADR 0563 §9)", - "properties": { - "act": { - "description": "the act: hand-over, access-act, judge-directories or setuid-search", - "type": "string" - }, - "ask": { - "description": "the operator's ask of a hand-over or an access act", - "type": "string" - }, - "at": { - "description": "when it was asked, RFC 3339", - "type": "string" - }, - "auto": { - "description": "the engine asked it itself, on finding a library's root holding data outside its group (ADR 0563 §4)", - "type": "boolean" - }, - "by": { - "description": "who answered, as the operator's warrant names them", - "type": "string" - }, - "caller": { - "description": "the caller as the runtime proved it", - "type": "string" - }, - "disk": { - "description": "what the engine read on disk when it judged", - "type": "string" - }, - "ended": { - "description": "when it ended, RFC 3339", - "type": "string" - }, - "expires": { - "description": "when the ask ends unanswered, RFC 3339", - "type": "string" - }, - "id": { - "description": "the act's id", - "type": "string" - }, - "path": { - "description": "the directory the act changes", - "type": "string" - }, - "proofs": { - "description": "the warrant's proofs", - "items": { - "type": "string" - }, - "type": "array" - }, - "said": { - "description": "what the engine said of it: its outcome or why it was refused", - "type": "string" - }, - "sets": { - "description": "the owner, group and mode it sets, in words", - "type": "string" - }, - "state": { - "description": "where it stands", - "enum": [ - "asked", - "done", - "refused", - "answered" - ], - "type": "string" - }, - "subject": { - "description": "the path of a hand-over, or the access of an access act", - "type": "string" - }, - "via": { - "description": "the channel the warrant came on", - "type": "string" - }, - "why": { - "description": "why it was asked, in words", - "type": "string" - } - }, - "required": [ - "id", - "act", - "caller", - "at", - "state" - ], - "type": "object" - }, - "type": "array" - }, - "node": { - "description": "the node", - "type": "string" - } - }, - "required": [ - "node", - "acts" - ], - "type": "object" - } - }, - { - "description": "The packages this machine's compositions declare, present or absent, each with the modules that declare it, as the engine keeps them in its key of `packages`; until the controller hands its lease away, beside the controller's mirror for this machine and their difference. With write, the key written again from what the engine composed.", - "input": { - "properties": { - "write": { - "description": "\"true\": write this machine's key again from what the engine composed", - "enum": [ - "true", - "false" - ], - "type": "string" - } - }, - "type": "object" - }, - "name": "packages", - "optional": true, - "output": { - "properties": { - "key": { - "description": "the node's key as the bus holds it; absent when the engine wrote none", - "properties": { - "node": { - "description": "the node", - "type": "string" - }, - "packages": { - "description": "its packages sorted by name, absent after present", - "items": { - "properties": { - "absent": { - "description": "declared absent", - "type": "boolean" - }, - "modules": { - "description": "the modules that declare it", - "items": { - "type": "string" - }, - "type": "array" - }, - "package": { - "description": "the package", - "type": "string" - } - }, - "required": [ - "package", - "modules", - "absent" - ], - "type": "object" - }, - "type": "array" - } - }, - "required": [ - "node", - "packages" - ], - "type": "object" - }, - "key_error": { - "description": "why the key could not be read", - "type": "string" - }, - "node": { - "description": "the node", - "type": "string" - }, - "note": { - "description": "a note, such as that no key is written yet", - "type": "string" - }, - "source": { - "description": "where the key is read: the engine's state packages", - "type": "string" - }, - "wrote": { - "description": "with write: the key was written again (false: it already held what the engine composed)", - "type": "boolean" - } - }, - "required": [ - "node", - "source" - ], - "type": "object" - } - }, - { - "description": "One module's composition on this machine beside what runs of it, read-only: the composition its want's build has in the bucket composed- (the latest revision, or the one whose digest is given, from the revisions the bucket keeps), the digest of each kept revision, what runs (applied, its have, or the kept declaration), its want and have (phase running, failed, held or waiting-yes, and the digest that waits for a yes), and the move's judgement with its words: why a record module waits for a yes. With diff, every resource added, removed or changed, a provider's received file naming the consumers that join or leave.", - "input": { - "properties": { - "diff": { - "description": "\"true\": list the resources that differ from what runs", - "type": "string" - }, - "digest": { - "description": "a composition's digest (sha256:…), to read that revision rather than the latest", - "type": "string" - }, - "module": { - "description": "the module, as placed on this machine; node for the node's own", - "type": "string" - } - }, - "required": [ - "module" - ], - "type": "object" - }, - "name": "composition", - "optional": true, - "output": { - "properties": { - "attempt": { - "description": "the module's last apply that did not end running; absent when none", - "properties": { - "answered_revision": { - "description": "the want's revision the hold took", - "type": "integer" - }, - "composed": { - "description": "the digest tried", - "type": "string" - }, - "composition_revision": { - "description": "the composition's revision it was tried at", - "type": "integer" - }, - "fp": { - "description": "the fingerprint tried", - "type": "string" - }, - "held": { - "description": "held for a failed backup", - "type": "boolean" - }, - "transient": { - "description": "it failed because the bus did not answer for a moment", - "type": "boolean" - }, - "want_revision": { - "description": "the want's revision it was tried at", - "type": "integer" - } - }, - "required": [ - "fp", - "want_revision", - "composition_revision" - ], - "type": "object" - }, - "composition": { - "description": "one revision of a composition key", - "properties": { - "digest": { - "description": "its digest", - "type": "string" - }, - "key": { - "description": "the composition's key", - "type": "string" - }, - "latest": { - "description": "the latest revision", - "type": "boolean" - }, - "refused": { - "description": "why it cannot be read or trusted", - "type": "string" - }, - "resources": { - "description": "how many resources it names", - "type": "integer" - }, - "revision": { - "description": "its revision", - "type": "integer" - } - }, - "required": [ - "key" - ], - "type": "object" - }, - "diff": { - "description": "every resource that differs from what runs, when asked", - "items": { - "description": "one resource added, removed or changed, as the engine's judge says it", - "type": "object" - }, - "type": "array" - }, - "have": { - "description": "what the module's have key says now; absent when it says nothing", - "properties": { - "composed": { - "description": "digest", - "type": "string" - }, - "detail": { - "description": "detail", - "type": "string" - }, - "fp": { - "description": "fingerprint", - "type": "string" - }, - "judgement": { - "description": "judgement", - "type": "string" - }, - "phase": { - "description": "running, failed, held or waiting-yes", - "type": "string" - }, - "waits": { - "description": "the digest that waits for a yes", - "type": "string" - } - }, - "required": [ - "phase" - ], - "type": "object" - }, - "judged": { - "description": "the move from what runs to the composition read", - "properties": { - "judgement": { - "description": "the judgement", - "type": "string" - }, - "waits_for_yes": { - "description": "the agent would wait for a person's yes before applying it", - "type": "boolean" - }, - "words": { - "description": "its words", - "type": "string" - } - }, - "required": [ - "judgement", - "words", - "waits_for_yes" - ], - "type": "object" - }, - "module": { - "description": "the module", - "type": "string" - }, - "node": { - "description": "the node", - "type": "string" - }, - "own": { - "description": "of the node's own composition, its own fields beside what runs names", - "properties": { - "adoption": { - "description": "the revision's adoption; absent for a converged node", - "type": "object" - }, - "groups": { - "description": "groups", - "type": "integer" - }, - "held": { - "additionalProperties": { - "type": "string" - }, - "description": "each module the node's composer held in its last pass, with why", - "type": "object" - }, - "identities": { - "description": "identities the revision names", - "type": "integer" - }, - "libraries": { - "description": "libraries", - "type": "integer" - }, - "provisioner_account": { - "description": "the account the provisioner's client runs as, as the revision names it", - "type": "string" - }, - "runs_provisioner_account": { - "description": "as what runs names it", - "type": "string" - } - }, - "required": [ - "provisioner_account" - ], - "type": "object" - }, - "revisions": { - "description": "the revisions of its key the bucket keeps, oldest first", - "items": { - "description": "one revision of a composition key", - "properties": { - "digest": { - "description": "its digest", - "type": "string" - }, - "key": { - "description": "the composition's key", - "type": "string" - }, - "latest": { - "description": "the latest revision", - "type": "boolean" - }, - "refused": { - "description": "why it cannot be read or trusted", - "type": "string" - }, - "resources": { - "description": "how many resources it names", - "type": "integer" - }, - "revision": { - "description": "its revision", - "type": "integer" - } - }, - "required": [ - "key" - ], - "type": "object" - }, - "type": "array" - }, - "runs": { - "description": "what runs of the module; absent when nothing runs", - "properties": { - "composed": { - "description": "digest", - "type": "string" - }, - "fp": { - "description": "fingerprint", - "type": "string" - }, - "from": { - "description": "applied, have or kept", - "type": "string" - }, - "phase": { - "description": "phase", - "type": "string" - }, - "resources": { - "description": "how many resources run", - "type": "integer" - } - }, - "required": [ - "from", - "resources" - ], - "type": "object" - }, - "started": { - "description": "the agent has looked at the module once since it started", - "type": "boolean" - }, - "want": { - "description": "the module's want as the agent holds it; absent when it holds none", - "properties": { - "backup": { - "description": "the operator's answer to a failed backup, again or skip", - "type": "string" - }, - "composed": { - "description": "the composition's digest it names", - "type": "string" - }, - "fp": { - "description": "its build's fingerprint", - "type": "string" - }, - "removed": { - "description": "the module is removed", - "type": "boolean" - }, - "revision": { - "description": "the revision of the want's key read last", - "type": "integer" - }, - "self": { - "description": "the node composes the module itself (ADR 0547 §2)", - "type": "boolean" - }, - "words": { - "description": "its words", - "type": "string" - }, - "yes": { - "description": "the digest a person said yes to", - "type": "string" - } - }, - "required": [ - "fp", - "revision" - ], - "type": "object" - } - }, - "required": [ - "node", - "module", - "started", - "composition", - "judged" - ], - "type": "object" - } - }, - { - "description": "Compose this machine's modules now and say how it went, so a composition that failed, or a module held, recovers without a restart: what the pass refused or held, each with why, and when the engine composes again on its own after a seat that did not answer (a deadline, no responders: retried with a growing wait; a refusal never). With module, that module's apply is also tried again. Changes only what a pass the engine makes on its own would change.", - "input": { - "properties": { - "module": { - "description": "a module placed on this machine whose apply is also tried again (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "compose", - "optional": true, - "output": { - "properties": { - "pass": { - "description": "how the pass went", - "properties": { - "again": { - "description": "when the node composes again on its own, RFC 3339", - "type": "string" - }, - "at": { - "description": "when the pass ended, RFC 3339", - "type": "string" - }, - "earlier": { - "description": "the answer is the last pass, made moments ago", - "type": "boolean" - }, - "handed": { - "description": "how many compositions the pass handed the agent that differ from before", - "type": "integer" - }, - "held": { - "additionalProperties": { - "type": "string" - }, - "description": "each module refused alone, with why", - "type": "object" - }, - "refused": { - "description": "why the node composed none of its modules", - "type": "string" - } - }, - "required": [ - "at", - "handed" - ], - "type": "object" - }, - "retried": { - "description": "the module whose apply was tried again", - "type": "string" - } - }, - "required": [ - "pass" - ], - "type": "object" - } - }, - { - "description": "Compose a check's candidate on this machine in shadow, with its own sealed values and the indexer's manifest verdicts, and say per module placed here whether it composes (with its digest and whether the candidate changes it), is refused by the candidate or is held without it, each with why, and the verdict of the machine's own composition (novox/hq ADR 0593 §7). Read-only: applies nothing and writes nothing. Answered by this machine's engine alone, within the asker's bound.", - "input": { - "properties": { - "candidate": { - "description": "the key of the indexer's candidate, .", - "type": "string" - }, - "input": { - "description": "also answer the composition input the candidate was composed from, for the sandbox path", - "type": "boolean" - } - }, - "required": [ - "candidate" - ], - "type": "object" - }, - "name": "compose-candidate", - "optional": true, - "output": { - "properties": { - "candidate": { - "description": "the candidate's key", - "type": "string" - }, - "input": { - "description": "when asked: the composition input it composed from", - "type": "object" - }, - "modules": { - "description": "every module placed on the node, sorted by name", - "items": { - "description": "one module's composition with the candidate", - "properties": { - "changed": { - "description": "the candidate changes it from the one composed without it", - "type": "boolean" - }, - "digest": { - "description": "the composition's digest when composed", - "type": "string" - }, - "fp": { - "description": "the fingerprint it was composed at", - "type": "string" - }, - "module": { - "description": "the module, node for the node's own composition", - "type": "string" - }, - "state": { - "description": "composed (digest set), refused by the candidate, or held without it too (words set)", - "enum": [ - "composed", - "refused", - "held" - ], - "type": "string" - }, - "words": { - "description": "why, for a refused or held one", - "type": "string" - } - }, - "required": [ - "module", - "state" - ], - "type": "object" - }, - "type": "array" - }, - "node": { - "description": "the node", - "type": "string" - }, - "own": { - "description": "one module's composition with the candidate", - "properties": { - "changed": { - "description": "the candidate changes it from the one composed without it", - "type": "boolean" - }, - "digest": { - "description": "the composition's digest when composed", - "type": "string" - }, - "fp": { - "description": "the fingerprint it was composed at", - "type": "string" - }, - "module": { - "description": "the module, node for the node's own composition", - "type": "string" - }, - "state": { - "description": "composed (digest set), refused by the candidate, or held without it too (words set)", - "enum": [ - "composed", - "refused", - "held" - ], - "type": "string" - }, - "words": { - "description": "why, for a refused or held one", - "type": "string" - } - }, - "required": [ - "module", - "state" - ], - "type": "object" - }, - "refused": { - "description": "why the node could not compose with the candidate at all", - "type": "string" - }, - "took": { - "description": "how long the engine took, in milliseconds", - "type": "integer" - }, - "v": { - "description": "the layout's version", - "type": "integer" - }, - "verdict": { - "description": "the node's verdict (ADR 0593 §7, §9)", - "enum": [ - "composed", - "refused", - "unready", - "not-judged" - ], - "type": "string" - } - }, - "required": [ - "v", - "node", - "candidate", - "verdict", - "modules", - "own", - "took" - ], - "type": "object" - } - }, - { - "description": "The reader capabilities this machine's engine states (manifest words, forms and seat names it reads, novox/hq ADR 0618 §3) and when it last stated them. An engine built before it said them states none, which a reader takes as the baseline. Read-only. Answered by this machine's engine alone.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "manifest-reads", - "optional": true, - "output": { - "description": "what this machine's engine reads of a manifest (ADR 0618 §3)", - "properties": { - "manifest-reads": { - "description": "the reader capabilities its engine states: the manifest words, forms and seat names it reads; empty for an engine built before it said them, read as the baseline", - "items": { - "minLength": 1, - "type": "string" - }, - "type": "array" - }, - "node": { - "description": "the node", - "type": "string" - }, - "stated-at": { - "description": "when the engine last stated them, RFC 3339", - "type": "string" - }, - "v": { - "description": "the layout's version", - "type": "integer" - } - }, - "required": [ - "v", - "node", - "stated-at" - ], - "type": "object" - } - } - ] - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-login-manager", - "scope": "node", - "serves": [ - { - "description": "The sessions the login manager offers on this machine, and which one the operator account starts by default.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "sessions" - } - ] - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-display-server", - "scope": "node", - "serves": [ - { - "description": "The monitors connected now, each with its identity, its modes and where it is placed; and the layout profile in force, if one matches.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "displays" - }, - { - "description": "The monitor layout profiles, keyed by the connected monitors' identities: list them, save the current arrangement under a name, or apply one.", - "input": { - "properties": { - "action": { - "description": "list, save or apply", - "enum": [ - "list", - "save", - "apply" - ], - "type": "string" - }, - "name": { - "description": "the profile to save or apply (save and apply only)", - "type": "string" - } - }, - "required": [ - "action" - ], - "type": "object" - }, - "name": "layout" - } - ] - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-display-session", - "scope": "node", - "serves": [ - { - "description": "Reload the session's configuration in place, keeping its windows.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "reload" - }, - { - "description": "The session's workspaces: each one's name, output, and whether it is visible or focused.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "workspaces" - }, - { - "description": "The session's windows: each one's title, class, workspace and whether it has focus; narrowed to one workspace when named.", - "input": { - "properties": { - "workspace": { - "description": "one workspace (optional)", - "type": "string" - } - }, - "type": "object" - }, - "name": "windows" - } - ] - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-terminal-emulator", - "scope": "node", - "serves": [ - { - "description": "Open a terminal window in the operator's session, running a command or the login shell, in a directory or the account's home.", - "input": { - "properties": { - "command": { - "description": "what to run in it (optional; the login shell when absent)", - "type": "string" - }, - "directory": { - "description": "where it starts (optional; the account's home when absent)", - "type": "string" - } - }, - "type": "object" - }, - "name": "open" - } - ] - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-launcher", - "scope": "node", - "serves": [ - { - "description": "Put a menu of choices in front of the operator and answer with the one chosen, or nothing when the menu was dismissed — the dmenu-compatible contract.", - "input": { - "properties": { - "choices": { - "description": "the lines to choose between, in order", - "items": { - "type": "string" - }, - "type": "array" - }, - "prompt": { - "description": "what the menu asks (optional)", - "type": "string" - } - }, - "required": [ - "choices" - ], - "type": "object" - }, - "name": "menu" - }, - { - "description": "Ask the operator for a value in a prompt that does not show what is typed, and answer it sealed to the key the asker gives — never in the clear — or cancelled when the prompt was dismissed or not answered in time.", - "input": { - "properties": { - "module": { - "description": "the module whose own secret is asked for", - "type": "string" - }, - "node": { - "description": "the machine the module runs on", - "type": "string" - }, - "seal_to": { - "description": "the asker's public sealing key: the answer is sealed to it", - "type": "string" - }, - "secret": { - "description": "the own secret's name", - "type": "string" - }, - "timeout_seconds": { - "description": "give up after this long (optional)", - "type": "string" - } - }, - "required": [ - "module", - "secret", - "node", - "seal_to" - ], - "type": "object" - }, - "name": "secret", - "optional": true - } - ] - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-notifier", - "scope": "node", - "serves": [ - { - "description": "Show the operator a notification.", - "input": { - "properties": { - "body": { - "description": "its text (optional)", - "type": "string" - }, - "title": { - "description": "the notification's summary", - "type": "string" - }, - "urgency": { - "description": "low, normal (the default) or critical", - "enum": [ - "low", - "normal", - "critical" - ], - "type": "string" - } - }, - "required": [ - "title" - ], - "type": "object" - }, - "name": "send" - }, - { - "description": "The notifications shown lately, newest first.", - "input": { - "properties": { - "limit": { - "description": "how many (optional, default 20)", - "type": "string" - } - }, - "type": "object" - }, - "name": "history" - } - ] - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-lock-screen", - "scope": "node", - "serves": [ - { - "description": "Lock the operator's session now.", - "input": { - "properties": {}, - "type": "object" - }, - "name": "lock" - } - ] - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-clipboard", - "scope": "node", - "serves": [ - { - "description": "What the clipboard held lately, newest first.", - "input": { - "properties": { - "limit": { - "description": "how many (optional, default 20)", - "type": "string" - } - }, - "type": "object" - }, - "name": "history" - }, - { - "description": "Put text on the operator's clipboard.", - "input": { - "properties": { - "text": { - "description": "the text", - "type": "string" - } - }, - "required": [ - "text" - ], - "type": "object" - }, - "name": "copy" - } - ] - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-bar", - "scope": "node" - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-compositor", - "scope": "node" - }, - { - "decision": "novox/hq ADR 0208", - "grain": "seat-list", - "name": "node-secret-service", - "scope": "node" - } -];